1 /* 2 * Copyright (C) 2022 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License 15 */ 16 package com.android.keyguard 17 18 import android.content.BroadcastReceiver 19 import android.testing.AndroidTestingRunner 20 import android.view.View 21 import android.view.ViewTreeObserver 22 import android.widget.FrameLayout 23 import androidx.test.filters.SmallTest 24 import com.android.systemui.SysuiTestCase 25 import com.android.systemui.broadcast.BroadcastDispatcher 26 import com.android.systemui.flags.Flags 27 import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository 28 import com.android.systemui.keyguard.domain.interactor.KeyguardInteractorFactory 29 import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor 30 import com.android.systemui.keyguard.shared.model.Edge 31 import com.android.systemui.keyguard.shared.model.KeyguardState.AOD 32 import com.android.systemui.keyguard.shared.model.KeyguardState.DOZING 33 import com.android.systemui.keyguard.shared.model.KeyguardState.GONE 34 import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN 35 import com.android.systemui.keyguard.shared.model.KeyguardState.OCCLUDED 36 import com.android.systemui.keyguard.shared.model.TransitionState 37 import com.android.systemui.keyguard.shared.model.TransitionStep 38 import com.android.systemui.log.core.LogLevel 39 import com.android.systemui.log.core.LogcatOnlyMessageBuffer 40 import com.android.systemui.plugins.clocks.ClockAnimations 41 import com.android.systemui.plugins.clocks.ClockController 42 import com.android.systemui.plugins.clocks.ClockEvents 43 import com.android.systemui.plugins.clocks.ClockFaceConfig 44 import com.android.systemui.plugins.clocks.ClockFaceController 45 import com.android.systemui.plugins.clocks.ClockFaceEvents 46 import com.android.systemui.plugins.clocks.ClockMessageBuffers 47 import com.android.systemui.plugins.clocks.ClockTickRate 48 import com.android.systemui.statusbar.policy.BatteryController 49 import com.android.systemui.statusbar.policy.ConfigurationController 50 import com.android.systemui.statusbar.policy.ZenModeController 51 import com.android.systemui.util.concurrency.DelayableExecutor 52 import com.android.systemui.util.mockito.any 53 import com.android.systemui.util.mockito.argumentCaptor 54 import com.android.systemui.util.mockito.capture 55 import com.android.systemui.util.mockito.eq 56 import com.android.systemui.util.mockito.mock 57 import java.util.TimeZone 58 import java.util.concurrent.Executor 59 import kotlinx.coroutines.Dispatchers 60 import kotlinx.coroutines.flow.MutableStateFlow 61 import kotlinx.coroutines.runBlocking 62 import kotlinx.coroutines.yield 63 import org.junit.Assert.assertEquals 64 import org.junit.Before 65 import org.junit.Rule 66 import org.junit.Test 67 import org.junit.runner.RunWith 68 import org.mockito.ArgumentMatchers.anyBoolean 69 import org.mockito.ArgumentMatchers.anyFloat 70 import org.mockito.ArgumentMatchers.anyInt 71 import org.mockito.Mock 72 import org.mockito.Mockito.never 73 import org.mockito.Mockito.times 74 import org.mockito.Mockito.verify 75 import org.mockito.junit.MockitoJUnit 76 import com.android.systemui.Flags as AConfigFlags 77 import org.mockito.Mockito.`when` as whenever 78 79 @RunWith(AndroidTestingRunner::class) 80 @SmallTest 81 class ClockEventControllerTest : SysuiTestCase() { 82 83 @JvmField @Rule val mockito = MockitoJUnit.rule() 84 @Mock private lateinit var broadcastDispatcher: BroadcastDispatcher 85 @Mock private lateinit var batteryController: BatteryController 86 @Mock private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor 87 @Mock private lateinit var configurationController: ConfigurationController 88 @Mock private lateinit var animations: ClockAnimations 89 @Mock private lateinit var events: ClockEvents 90 @Mock private lateinit var clock: ClockController 91 @Mock private lateinit var mainExecutor: DelayableExecutor 92 @Mock private lateinit var bgExecutor: Executor 93 @Mock private lateinit var smallClockController: ClockFaceController 94 @Mock private lateinit var smallClockView: View 95 @Mock private lateinit var smallClockViewTreeObserver: ViewTreeObserver 96 @Mock private lateinit var smallClockFrame: FrameLayout 97 @Mock private lateinit var smallClockFrameViewTreeObserver: ViewTreeObserver 98 @Mock private lateinit var largeClockController: ClockFaceController 99 @Mock private lateinit var largeClockView: View 100 @Mock private lateinit var largeClockViewTreeObserver: ViewTreeObserver 101 @Mock private lateinit var smallClockEvents: ClockFaceEvents 102 @Mock private lateinit var largeClockEvents: ClockFaceEvents 103 @Mock private lateinit var parentView: View 104 private lateinit var repository: FakeKeyguardRepository 105 @Mock private lateinit var keyguardTransitionInteractor: KeyguardTransitionInteractor 106 private val messageBuffer = LogcatOnlyMessageBuffer(LogLevel.DEBUG) 107 private val clockBuffers = ClockMessageBuffers(messageBuffer, messageBuffer, messageBuffer) 108 private lateinit var underTest: ClockEventController 109 @Mock private lateinit var zenModeController: ZenModeController 110 111 @Before setUpnull112 fun setUp() { 113 whenever(clock.smallClock).thenReturn(smallClockController) 114 whenever(clock.largeClock).thenReturn(largeClockController) 115 whenever(smallClockController.view).thenReturn(smallClockView) 116 whenever(smallClockView.parent).thenReturn(smallClockFrame) 117 whenever(smallClockView.viewTreeObserver).thenReturn(smallClockViewTreeObserver) 118 whenever(smallClockFrame.viewTreeObserver).thenReturn(smallClockFrameViewTreeObserver) 119 whenever(largeClockController.view).thenReturn(largeClockView) 120 whenever(largeClockView.viewTreeObserver).thenReturn(largeClockViewTreeObserver) 121 whenever(smallClockController.events).thenReturn(smallClockEvents) 122 whenever(largeClockController.events).thenReturn(largeClockEvents) 123 whenever(clock.events).thenReturn(events) 124 whenever(smallClockController.animations).thenReturn(animations) 125 whenever(largeClockController.animations).thenReturn(animations) 126 whenever(smallClockController.config) 127 .thenReturn(ClockFaceConfig(tickRate = ClockTickRate.PER_MINUTE)) 128 whenever(largeClockController.config) 129 .thenReturn(ClockFaceConfig(tickRate = ClockTickRate.PER_MINUTE)) 130 131 repository = FakeKeyguardRepository() 132 133 val withDeps = 134 KeyguardInteractorFactory.create( 135 repository = repository, 136 ) 137 138 withDeps.featureFlags.apply { set(Flags.REGION_SAMPLING, false) } 139 underTest = 140 ClockEventController( 141 withDeps.keyguardInteractor, 142 keyguardTransitionInteractor, 143 broadcastDispatcher, 144 batteryController, 145 keyguardUpdateMonitor, 146 configurationController, 147 context.resources, 148 context, 149 mainExecutor, 150 bgExecutor, 151 clockBuffers, 152 withDeps.featureFlags, 153 zenModeController 154 ) 155 underTest.clock = clock 156 157 runBlocking(IMMEDIATE) { 158 underTest.registerListeners(parentView) 159 160 repository.setIsDozing(true) 161 repository.setDozeAmount(1f) 162 } 163 } 164 165 @Test clockSet_validateInitializationnull166 fun clockSet_validateInitialization() { 167 verify(clock).initialize(any(), anyFloat(), anyFloat()) 168 } 169 170 @Test clockUnset_validateStatenull171 fun clockUnset_validateState() { 172 underTest.clock = null 173 174 assertEquals(underTest.clock, null) 175 } 176 177 @Test themeChanged_verifyClockPaletteUpdatednull178 fun themeChanged_verifyClockPaletteUpdated() = 179 runBlocking(IMMEDIATE) { 180 verify(smallClockEvents).onRegionDarknessChanged(anyBoolean()) 181 verify(largeClockEvents).onRegionDarknessChanged(anyBoolean()) 182 183 val captor = argumentCaptor<ConfigurationController.ConfigurationListener>() 184 verify(configurationController).addCallback(capture(captor)) 185 captor.value.onThemeChanged() 186 187 verify(events).onColorPaletteChanged(any()) 188 } 189 190 @Test fontChanged_verifyFontSizeUpdatednull191 fun fontChanged_verifyFontSizeUpdated() = 192 runBlocking(IMMEDIATE) { 193 val captor = argumentCaptor<ConfigurationController.ConfigurationListener>() 194 verify(configurationController).addCallback(capture(captor)) 195 captor.value.onDensityOrFontScaleChanged() 196 197 verify(smallClockEvents, times(2)).onFontSettingChanged(anyFloat()) 198 verify(largeClockEvents, times(2)).onFontSettingChanged(anyFloat()) 199 } 200 201 @Test batteryCallback_keyguardShowingCharging_verifyChargeAnimationnull202 fun batteryCallback_keyguardShowingCharging_verifyChargeAnimation() = 203 runBlocking(IMMEDIATE) { 204 val batteryCaptor = argumentCaptor<BatteryController.BatteryStateChangeCallback>() 205 verify(batteryController).addCallback(capture(batteryCaptor)) 206 val keyguardCaptor = argumentCaptor<KeyguardUpdateMonitorCallback>() 207 verify(keyguardUpdateMonitor).registerCallback(capture(keyguardCaptor)) 208 keyguardCaptor.value.onKeyguardVisibilityChanged(true) 209 batteryCaptor.value.onBatteryLevelChanged(10, false, true) 210 211 verify(animations, times(2)).charge() 212 } 213 214 @Test batteryCallback_keyguardShowingCharging_Duplicate_verifyChargeAnimationnull215 fun batteryCallback_keyguardShowingCharging_Duplicate_verifyChargeAnimation() = 216 runBlocking(IMMEDIATE) { 217 val batteryCaptor = argumentCaptor<BatteryController.BatteryStateChangeCallback>() 218 verify(batteryController).addCallback(capture(batteryCaptor)) 219 val keyguardCaptor = argumentCaptor<KeyguardUpdateMonitorCallback>() 220 verify(keyguardUpdateMonitor).registerCallback(capture(keyguardCaptor)) 221 keyguardCaptor.value.onKeyguardVisibilityChanged(true) 222 batteryCaptor.value.onBatteryLevelChanged(10, false, true) 223 batteryCaptor.value.onBatteryLevelChanged(10, false, true) 224 225 verify(animations, times(2)).charge() 226 } 227 228 @Test batteryCallback_keyguardHiddenCharging_verifyChargeAnimationnull229 fun batteryCallback_keyguardHiddenCharging_verifyChargeAnimation() = 230 runBlocking(IMMEDIATE) { 231 val batteryCaptor = argumentCaptor<BatteryController.BatteryStateChangeCallback>() 232 verify(batteryController).addCallback(capture(batteryCaptor)) 233 val keyguardCaptor = argumentCaptor<KeyguardUpdateMonitorCallback>() 234 verify(keyguardUpdateMonitor).registerCallback(capture(keyguardCaptor)) 235 keyguardCaptor.value.onKeyguardVisibilityChanged(false) 236 batteryCaptor.value.onBatteryLevelChanged(10, false, true) 237 238 verify(animations, never()).charge() 239 } 240 241 @Test batteryCallback_keyguardShowingNotCharging_verifyChargeAnimationnull242 fun batteryCallback_keyguardShowingNotCharging_verifyChargeAnimation() = 243 runBlocking(IMMEDIATE) { 244 val batteryCaptor = argumentCaptor<BatteryController.BatteryStateChangeCallback>() 245 verify(batteryController).addCallback(capture(batteryCaptor)) 246 val keyguardCaptor = argumentCaptor<KeyguardUpdateMonitorCallback>() 247 verify(keyguardUpdateMonitor).registerCallback(capture(keyguardCaptor)) 248 keyguardCaptor.value.onKeyguardVisibilityChanged(true) 249 batteryCaptor.value.onBatteryLevelChanged(10, false, false) 250 251 verify(animations, never()).charge() 252 } 253 254 @Test localeCallback_verifyClockNotifiednull255 fun localeCallback_verifyClockNotified() = 256 runBlocking(IMMEDIATE) { 257 val captor = argumentCaptor<BroadcastReceiver>() 258 verify(broadcastDispatcher) 259 .registerReceiver(capture(captor), any(), eq(null), eq(null), anyInt(), eq(null)) 260 captor.value.onReceive(context, mock()) 261 262 verify(events).onLocaleChanged(any()) 263 } 264 265 @Test keyguardCallback_visibilityChanged_clockDozeCallednull266 fun keyguardCallback_visibilityChanged_clockDozeCalled() = 267 runBlocking(IMMEDIATE) { 268 mSetFlagsRule.disableFlags(AConfigFlags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT) 269 val captor = argumentCaptor<KeyguardUpdateMonitorCallback>() 270 verify(keyguardUpdateMonitor).registerCallback(capture(captor)) 271 272 captor.value.onKeyguardVisibilityChanged(true) 273 verify(animations, never()).doze(0f) 274 275 captor.value.onKeyguardVisibilityChanged(false) 276 verify(animations, times(2)).doze(0f) 277 } 278 279 @Test keyguardCallback_timeFormat_clockNotifiednull280 fun keyguardCallback_timeFormat_clockNotified() = 281 runBlocking(IMMEDIATE) { 282 val captor = argumentCaptor<KeyguardUpdateMonitorCallback>() 283 verify(keyguardUpdateMonitor).registerCallback(capture(captor)) 284 captor.value.onTimeFormatChanged("12h") 285 286 verify(events).onTimeFormatChanged(false) 287 } 288 289 @Test keyguardCallback_timezoneChanged_clockNotifiednull290 fun keyguardCallback_timezoneChanged_clockNotified() = 291 runBlocking(IMMEDIATE) { 292 val mockTimeZone = mock<TimeZone>() 293 val captor = argumentCaptor<KeyguardUpdateMonitorCallback>() 294 verify(keyguardUpdateMonitor).registerCallback(capture(captor)) 295 captor.value.onTimeZoneChanged(mockTimeZone) 296 297 verify(events).onTimeZoneChanged(mockTimeZone) 298 } 299 300 @Test keyguardCallback_userSwitched_clockNotifiednull301 fun keyguardCallback_userSwitched_clockNotified() = 302 runBlocking(IMMEDIATE) { 303 val captor = argumentCaptor<KeyguardUpdateMonitorCallback>() 304 verify(keyguardUpdateMonitor).registerCallback(capture(captor)) 305 captor.value.onUserSwitchComplete(10) 306 307 verify(events).onTimeFormatChanged(false) 308 } 309 310 @Test keyguardCallback_verifyKeyguardChangednull311 fun keyguardCallback_verifyKeyguardChanged() = 312 runBlocking(IMMEDIATE) { 313 val job = underTest.listenForDozeAmount(this) 314 repository.setDozeAmount(0.4f) 315 316 yield() 317 318 verify(animations, times(2)).doze(0.4f) 319 320 job.cancel() 321 } 322 323 @Test listenForDozeAmountTransition_updatesClockDozeAmountnull324 fun listenForDozeAmountTransition_updatesClockDozeAmount() = 325 runBlocking(IMMEDIATE) { 326 val transitionStep = MutableStateFlow(TransitionStep()) 327 whenever(keyguardTransitionInteractor.transition(Edge.create(LOCKSCREEN, AOD))) 328 .thenReturn(transitionStep) 329 whenever(keyguardTransitionInteractor.transition(Edge.create(AOD, LOCKSCREEN))) 330 .thenReturn(transitionStep) 331 332 val job = underTest.listenForDozeAmountTransition(this) 333 transitionStep.value = 334 TransitionStep( 335 from = LOCKSCREEN, 336 to = AOD, 337 value = 0.4f, 338 transitionState = TransitionState.RUNNING, 339 ) 340 yield() 341 342 verify(animations, times(2)).doze(0.4f) 343 344 job.cancel() 345 } 346 347 @Test listenForTransitionToAodFromGone_updatesClockDozeAmountToOnenull348 fun listenForTransitionToAodFromGone_updatesClockDozeAmountToOne() = 349 runBlocking(IMMEDIATE) { 350 val transitionStep = MutableStateFlow(TransitionStep()) 351 whenever(keyguardTransitionInteractor 352 .transition(Edge.create(to = AOD))) 353 .thenReturn(transitionStep) 354 355 val job = underTest.listenForAnyStateToAodTransition(this) 356 transitionStep.value = 357 TransitionStep( 358 from = GONE, 359 to = AOD, 360 transitionState = TransitionState.STARTED, 361 ) 362 yield() 363 364 verify(animations, times(2)).doze(1f) 365 366 job.cancel() 367 } 368 369 @Test listenForTransitionToLSFromOccluded_updatesClockDozeAmountToZeronull370 fun listenForTransitionToLSFromOccluded_updatesClockDozeAmountToZero() = 371 runBlocking(IMMEDIATE) { 372 val transitionStep = MutableStateFlow(TransitionStep()) 373 whenever(keyguardTransitionInteractor 374 .transition(Edge.create(to = LOCKSCREEN))) 375 .thenReturn(transitionStep) 376 377 val job = underTest.listenForAnyStateToLockscreenTransition(this) 378 transitionStep.value = 379 TransitionStep( 380 from = OCCLUDED, 381 to = LOCKSCREEN, 382 transitionState = TransitionState.STARTED, 383 ) 384 yield() 385 386 verify(animations, times(2)).doze(0f) 387 388 job.cancel() 389 } 390 391 @Test listenForTransitionToAodFromLockscreen_neverUpdatesClockDozeAmountnull392 fun listenForTransitionToAodFromLockscreen_neverUpdatesClockDozeAmount() = 393 runBlocking(IMMEDIATE) { 394 val transitionStep = MutableStateFlow(TransitionStep()) 395 whenever(keyguardTransitionInteractor 396 .transition(Edge.create(to = AOD))) 397 .thenReturn(transitionStep) 398 399 val job = underTest.listenForAnyStateToAodTransition(this) 400 transitionStep.value = 401 TransitionStep( 402 from = LOCKSCREEN, 403 to = AOD, 404 transitionState = TransitionState.STARTED, 405 ) 406 yield() 407 408 verify(animations, never()).doze(1f) 409 410 job.cancel() 411 } 412 413 @Test listenForAnyStateToLockscreenTransition_neverUpdatesClockDozeAmountnull414 fun listenForAnyStateToLockscreenTransition_neverUpdatesClockDozeAmount() = 415 runBlocking(IMMEDIATE) { 416 val transitionStep = MutableStateFlow(TransitionStep()) 417 whenever(keyguardTransitionInteractor 418 .transition(Edge.create(to = LOCKSCREEN))) 419 .thenReturn(transitionStep) 420 421 val job = underTest.listenForAnyStateToLockscreenTransition(this) 422 transitionStep.value = 423 TransitionStep( 424 from = AOD, 425 to = LOCKSCREEN, 426 transitionState = TransitionState.STARTED, 427 ) 428 yield() 429 430 verify(animations, never()).doze(0f) 431 432 job.cancel() 433 } 434 435 @Test listenForAnyStateToDozingTransition_UpdatesClockDozeAmountToOnenull436 fun listenForAnyStateToDozingTransition_UpdatesClockDozeAmountToOne() = 437 runBlocking(IMMEDIATE) { 438 val transitionStep = MutableStateFlow(TransitionStep()) 439 whenever(keyguardTransitionInteractor 440 .transition(Edge.create(to = DOZING))) 441 .thenReturn(transitionStep) 442 443 val job = underTest.listenForAnyStateToDozingTransition(this) 444 transitionStep.value = 445 TransitionStep( 446 from = LOCKSCREEN, 447 to = DOZING, 448 transitionState = TransitionState.STARTED, 449 ) 450 yield() 451 452 verify(animations, times(2)).doze(1f) 453 454 job.cancel() 455 } 456 457 @Test unregisterListeners_validatenull458 fun unregisterListeners_validate() = 459 runBlocking(IMMEDIATE) { 460 underTest.unregisterListeners() 461 verify(broadcastDispatcher).unregisterReceiver(any()) 462 verify(configurationController).removeCallback(any()) 463 verify(batteryController).removeCallback(any()) 464 verify(keyguardUpdateMonitor).removeCallback(any()) 465 verify(smallClockController.view) 466 .removeOnAttachStateChangeListener(underTest.smallClockOnAttachStateChangeListener) 467 verify(largeClockController.view) 468 .removeOnAttachStateChangeListener(underTest.largeClockOnAttachStateChangeListener) 469 } 470 471 @Test registerOnAttachStateChangeListener_validatenull472 fun registerOnAttachStateChangeListener_validate() = 473 runBlocking(IMMEDIATE) { 474 verify(smallClockController.view) 475 .addOnAttachStateChangeListener(underTest.smallClockOnAttachStateChangeListener) 476 verify(largeClockController.view) 477 .addOnAttachStateChangeListener(underTest.largeClockOnAttachStateChangeListener) 478 } 479 480 @Test registerAndRemoveOnGlobalLayoutListener_correctlynull481 fun registerAndRemoveOnGlobalLayoutListener_correctly() = 482 runBlocking(IMMEDIATE) { 483 underTest.smallClockOnAttachStateChangeListener!!.onViewAttachedToWindow(smallClockView) 484 verify(smallClockFrame.viewTreeObserver).addOnGlobalLayoutListener(any()) 485 underTest.smallClockOnAttachStateChangeListener!!.onViewDetachedFromWindow( 486 smallClockView 487 ) 488 verify(smallClockFrame.viewTreeObserver).removeOnGlobalLayoutListener(any()) 489 } 490 491 @Test registerOnGlobalLayoutListener_RemoveOnAttachStateChangeListener_correctlynull492 fun registerOnGlobalLayoutListener_RemoveOnAttachStateChangeListener_correctly() = 493 runBlocking(IMMEDIATE) { 494 underTest.smallClockOnAttachStateChangeListener!!.onViewAttachedToWindow(smallClockView) 495 verify(smallClockFrame.viewTreeObserver).addOnGlobalLayoutListener(any()) 496 underTest.unregisterListeners() 497 verify(smallClockFrame.viewTreeObserver).removeOnGlobalLayoutListener(any()) 498 } 499 500 companion object { 501 private val IMMEDIATE = Dispatchers.Main.immediate 502 } 503 } 504