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.bouncer.data.repository.FakeKeyguardBouncerRepository 26 import com.android.systemui.broadcast.BroadcastDispatcher 27 import com.android.systemui.common.ui.data.repository.FakeConfigurationRepository 28 import com.android.systemui.flags.FeatureFlags 29 import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository 30 import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository 31 import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor 32 import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractorFactory 33 import com.android.systemui.log.LogBuffer 34 import com.android.systemui.plugins.ClockAnimations 35 import com.android.systemui.plugins.ClockController 36 import com.android.systemui.plugins.ClockEvents 37 import com.android.systemui.plugins.ClockFaceConfig 38 import com.android.systemui.plugins.ClockFaceController 39 import com.android.systemui.plugins.ClockFaceEvents 40 import com.android.systemui.plugins.ClockTickRate 41 import com.android.systemui.statusbar.CommandQueue 42 import com.android.systemui.statusbar.policy.BatteryController 43 import com.android.systemui.statusbar.policy.ConfigurationController 44 import com.android.systemui.util.concurrency.DelayableExecutor 45 import com.android.systemui.util.mockito.any 46 import com.android.systemui.util.mockito.argumentCaptor 47 import com.android.systemui.util.mockito.capture 48 import com.android.systemui.util.mockito.eq 49 import com.android.systemui.util.mockito.mock 50 import kotlinx.coroutines.Dispatchers 51 import kotlinx.coroutines.runBlocking 52 import kotlinx.coroutines.test.TestScope 53 import kotlinx.coroutines.yield 54 import org.junit.Assert.assertEquals 55 import org.junit.Before 56 import org.junit.Rule 57 import org.junit.Test 58 import org.junit.runner.RunWith 59 import org.mockito.ArgumentMatchers.anyBoolean 60 import org.mockito.ArgumentMatchers.anyFloat 61 import org.mockito.ArgumentMatchers.anyInt 62 import org.mockito.Mock 63 import org.mockito.Mockito.never 64 import org.mockito.Mockito.times 65 import org.mockito.Mockito.verify 66 import org.mockito.junit.MockitoJUnit 67 import java.util.TimeZone 68 import java.util.concurrent.Executor 69 import org.mockito.Mockito.`when` as whenever 70 71 @RunWith(AndroidTestingRunner::class) 72 @SmallTest 73 class ClockEventControllerTest : SysuiTestCase() { 74 75 @JvmField @Rule val mockito = MockitoJUnit.rule() 76 @Mock private lateinit var broadcastDispatcher: BroadcastDispatcher 77 @Mock private lateinit var batteryController: BatteryController 78 @Mock private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor 79 @Mock private lateinit var configurationController: ConfigurationController 80 @Mock private lateinit var animations: ClockAnimations 81 @Mock private lateinit var events: ClockEvents 82 @Mock private lateinit var clock: ClockController 83 @Mock private lateinit var mainExecutor: DelayableExecutor 84 @Mock private lateinit var bgExecutor: Executor 85 @Mock private lateinit var featureFlags: FeatureFlags 86 @Mock private lateinit var smallClockController: ClockFaceController 87 @Mock private lateinit var smallClockView: View 88 @Mock private lateinit var smallClockViewTreeObserver: ViewTreeObserver 89 @Mock private lateinit var smallClockFrame: FrameLayout 90 @Mock private lateinit var smallClockFrameViewTreeObserver: ViewTreeObserver 91 @Mock private lateinit var largeClockController: ClockFaceController 92 @Mock private lateinit var largeClockView: View 93 @Mock private lateinit var largeClockViewTreeObserver: ViewTreeObserver 94 @Mock private lateinit var smallClockEvents: ClockFaceEvents 95 @Mock private lateinit var largeClockEvents: ClockFaceEvents 96 @Mock private lateinit var parentView: View 97 @Mock private lateinit var transitionRepository: KeyguardTransitionRepository 98 @Mock private lateinit var commandQueue: CommandQueue 99 private lateinit var repository: FakeKeyguardRepository 100 private lateinit var bouncerRepository: FakeKeyguardBouncerRepository 101 @Mock private lateinit var smallLogBuffer: LogBuffer 102 @Mock private lateinit var largeLogBuffer: LogBuffer 103 private lateinit var underTest: ClockEventController 104 105 @Before 106 fun setUp() { 107 whenever(clock.smallClock).thenReturn(smallClockController) 108 whenever(clock.largeClock).thenReturn(largeClockController) 109 whenever(smallClockController.view).thenReturn(smallClockView) 110 whenever(smallClockView.parent).thenReturn(smallClockFrame) 111 whenever(smallClockView.viewTreeObserver).thenReturn(smallClockViewTreeObserver) 112 whenever(smallClockFrame.viewTreeObserver).thenReturn(smallClockFrameViewTreeObserver) 113 whenever(largeClockController.view).thenReturn(largeClockView) 114 whenever(largeClockView.viewTreeObserver).thenReturn(largeClockViewTreeObserver) 115 whenever(smallClockController.events).thenReturn(smallClockEvents) 116 whenever(largeClockController.events).thenReturn(largeClockEvents) 117 whenever(clock.events).thenReturn(events) 118 whenever(smallClockController.animations).thenReturn(animations) 119 whenever(largeClockController.animations).thenReturn(animations) 120 whenever(smallClockController.config) 121 .thenReturn(ClockFaceConfig(tickRate = ClockTickRate.PER_MINUTE)) 122 whenever(largeClockController.config) 123 .thenReturn(ClockFaceConfig(tickRate = ClockTickRate.PER_MINUTE)) 124 125 repository = FakeKeyguardRepository() 126 bouncerRepository = FakeKeyguardBouncerRepository() 127 128 underTest = ClockEventController( 129 KeyguardInteractor( 130 repository = repository, 131 commandQueue = commandQueue, 132 featureFlags = featureFlags, 133 bouncerRepository = bouncerRepository, 134 configurationRepository = FakeConfigurationRepository(), 135 ), 136 KeyguardTransitionInteractorFactory.create( 137 scope = TestScope().backgroundScope, 138 ).keyguardTransitionInteractor, 139 broadcastDispatcher, 140 batteryController, 141 keyguardUpdateMonitor, 142 configurationController, 143 context.resources, 144 context, 145 mainExecutor, 146 bgExecutor, 147 smallLogBuffer, 148 largeLogBuffer, 149 featureFlags 150 ) 151 underTest.clock = clock 152 153 runBlocking(IMMEDIATE) { 154 underTest.registerListeners(parentView) 155 156 repository.setIsDozing(true) 157 repository.setDozeAmount(1f) 158 } 159 } 160 161 @Test 162 fun clockSet_validateInitialization() { 163 verify(clock).initialize(any(), anyFloat(), anyFloat()) 164 } 165 166 @Test 167 fun clockUnset_validateState() { 168 underTest.clock = null 169 170 assertEquals(underTest.clock, null) 171 } 172 173 @Test 174 fun themeChanged_verifyClockPaletteUpdated() = runBlocking(IMMEDIATE) { 175 verify(smallClockEvents).onRegionDarknessChanged(anyBoolean()) 176 verify(largeClockEvents).onRegionDarknessChanged(anyBoolean()) 177 178 val captor = argumentCaptor<ConfigurationController.ConfigurationListener>() 179 verify(configurationController).addCallback(capture(captor)) 180 captor.value.onThemeChanged() 181 182 verify(events).onColorPaletteChanged(any()) 183 } 184 185 @Test 186 fun fontChanged_verifyFontSizeUpdated() = runBlocking(IMMEDIATE) { 187 val captor = argumentCaptor<ConfigurationController.ConfigurationListener>() 188 verify(configurationController).addCallback(capture(captor)) 189 captor.value.onDensityOrFontScaleChanged() 190 191 verify(smallClockEvents, times(2)).onFontSettingChanged(anyFloat()) 192 verify(largeClockEvents, times(2)).onFontSettingChanged(anyFloat()) 193 } 194 195 @Test 196 fun batteryCallback_keyguardShowingCharging_verifyChargeAnimation() = runBlocking(IMMEDIATE) { 197 val batteryCaptor = argumentCaptor<BatteryController.BatteryStateChangeCallback>() 198 verify(batteryController).addCallback(capture(batteryCaptor)) 199 val keyguardCaptor = argumentCaptor<KeyguardUpdateMonitorCallback>() 200 verify(keyguardUpdateMonitor).registerCallback(capture(keyguardCaptor)) 201 keyguardCaptor.value.onKeyguardVisibilityChanged(true) 202 batteryCaptor.value.onBatteryLevelChanged(10, false, true) 203 204 verify(animations, times(2)).charge() 205 } 206 207 @Test 208 fun batteryCallback_keyguardShowingCharging_Duplicate_verifyChargeAnimation() = 209 runBlocking(IMMEDIATE) { 210 val batteryCaptor = argumentCaptor<BatteryController.BatteryStateChangeCallback>() 211 verify(batteryController).addCallback(capture(batteryCaptor)) 212 val keyguardCaptor = argumentCaptor<KeyguardUpdateMonitorCallback>() 213 verify(keyguardUpdateMonitor).registerCallback(capture(keyguardCaptor)) 214 keyguardCaptor.value.onKeyguardVisibilityChanged(true) 215 batteryCaptor.value.onBatteryLevelChanged(10, false, true) 216 batteryCaptor.value.onBatteryLevelChanged(10, false, true) 217 218 verify(animations, times(2)).charge() 219 } 220 221 @Test 222 fun batteryCallback_keyguardHiddenCharging_verifyChargeAnimation() = runBlocking(IMMEDIATE) { 223 val batteryCaptor = argumentCaptor<BatteryController.BatteryStateChangeCallback>() 224 verify(batteryController).addCallback(capture(batteryCaptor)) 225 val keyguardCaptor = argumentCaptor<KeyguardUpdateMonitorCallback>() 226 verify(keyguardUpdateMonitor).registerCallback(capture(keyguardCaptor)) 227 keyguardCaptor.value.onKeyguardVisibilityChanged(false) 228 batteryCaptor.value.onBatteryLevelChanged(10, false, true) 229 230 verify(animations, never()).charge() 231 } 232 233 @Test 234 fun batteryCallback_keyguardShowingNotCharging_verifyChargeAnimation() = 235 runBlocking(IMMEDIATE) { 236 val batteryCaptor = argumentCaptor<BatteryController.BatteryStateChangeCallback>() 237 verify(batteryController).addCallback(capture(batteryCaptor)) 238 val keyguardCaptor = argumentCaptor<KeyguardUpdateMonitorCallback>() 239 verify(keyguardUpdateMonitor).registerCallback(capture(keyguardCaptor)) 240 keyguardCaptor.value.onKeyguardVisibilityChanged(true) 241 batteryCaptor.value.onBatteryLevelChanged(10, false, false) 242 243 verify(animations, never()).charge() 244 } 245 246 @Test 247 fun localeCallback_verifyClockNotified() = runBlocking(IMMEDIATE) { 248 val captor = argumentCaptor<BroadcastReceiver>() 249 verify(broadcastDispatcher).registerReceiver( 250 capture(captor), any(), eq(null), eq(null), anyInt(), eq(null) 251 ) 252 captor.value.onReceive(context, mock()) 253 254 verify(events).onLocaleChanged(any()) 255 } 256 257 @Test 258 fun keyguardCallback_visibilityChanged_clockDozeCalled() = runBlocking(IMMEDIATE) { 259 val captor = argumentCaptor<KeyguardUpdateMonitorCallback>() 260 verify(keyguardUpdateMonitor).registerCallback(capture(captor)) 261 262 captor.value.onKeyguardVisibilityChanged(true) 263 verify(animations, never()).doze(0f) 264 265 captor.value.onKeyguardVisibilityChanged(false) 266 verify(animations, times(2)).doze(0f) 267 } 268 269 @Test 270 fun keyguardCallback_timeFormat_clockNotified() = runBlocking(IMMEDIATE) { 271 val captor = argumentCaptor<KeyguardUpdateMonitorCallback>() 272 verify(keyguardUpdateMonitor).registerCallback(capture(captor)) 273 captor.value.onTimeFormatChanged("12h") 274 275 verify(events).onTimeFormatChanged(false) 276 } 277 278 @Test 279 fun keyguardCallback_timezoneChanged_clockNotified() = runBlocking(IMMEDIATE) { 280 val mockTimeZone = mock<TimeZone>() 281 val captor = argumentCaptor<KeyguardUpdateMonitorCallback>() 282 verify(keyguardUpdateMonitor).registerCallback(capture(captor)) 283 captor.value.onTimeZoneChanged(mockTimeZone) 284 285 verify(events).onTimeZoneChanged(mockTimeZone) 286 } 287 288 @Test 289 fun keyguardCallback_userSwitched_clockNotified() = runBlocking(IMMEDIATE) { 290 val captor = argumentCaptor<KeyguardUpdateMonitorCallback>() 291 verify(keyguardUpdateMonitor).registerCallback(capture(captor)) 292 captor.value.onUserSwitchComplete(10) 293 294 verify(events).onTimeFormatChanged(false) 295 } 296 297 @Test 298 fun keyguardCallback_verifyKeyguardChanged() = runBlocking(IMMEDIATE) { 299 val job = underTest.listenForDozeAmount(this) 300 repository.setDozeAmount(0.4f) 301 302 yield() 303 304 verify(animations, times(2)).doze(0.4f) 305 306 job.cancel() 307 } 308 309 @Test 310 fun unregisterListeners_validate() = runBlocking(IMMEDIATE) { 311 underTest.unregisterListeners() 312 verify(broadcastDispatcher).unregisterReceiver(any()) 313 verify(configurationController).removeCallback(any()) 314 verify(batteryController).removeCallback(any()) 315 verify(keyguardUpdateMonitor).removeCallback(any()) 316 verify(smallClockController.view) 317 .removeOnAttachStateChangeListener(underTest.smallClockOnAttachStateChangeListener) 318 verify(largeClockController.view) 319 .removeOnAttachStateChangeListener(underTest.largeClockOnAttachStateChangeListener) 320 } 321 322 @Test 323 fun registerOnAttachStateChangeListener_validate() = runBlocking(IMMEDIATE) { 324 verify(smallClockController.view) 325 .addOnAttachStateChangeListener(underTest.smallClockOnAttachStateChangeListener) 326 verify(largeClockController.view) 327 .addOnAttachStateChangeListener(underTest.largeClockOnAttachStateChangeListener) 328 } 329 330 @Test 331 fun registerAndRemoveOnGlobalLayoutListener_correctly() = runBlocking(IMMEDIATE) { 332 underTest.smallClockOnAttachStateChangeListener!!.onViewAttachedToWindow(smallClockView) 333 verify(smallClockFrame.viewTreeObserver).addOnGlobalLayoutListener(any()) 334 underTest.smallClockOnAttachStateChangeListener!!.onViewDetachedFromWindow(smallClockView) 335 verify(smallClockFrame.viewTreeObserver).removeOnGlobalLayoutListener(any()) 336 } 337 338 @Test 339 fun registerOnGlobalLayoutListener_RemoveOnAttachStateChangeListener_correctly() = 340 runBlocking(IMMEDIATE) { 341 underTest.smallClockOnAttachStateChangeListener!! 342 .onViewAttachedToWindow(smallClockView) 343 verify(smallClockFrame.viewTreeObserver).addOnGlobalLayoutListener(any()) 344 underTest.unregisterListeners() 345 verify(smallClockFrame.viewTreeObserver).removeOnGlobalLayoutListener(any()) 346 } 347 348 companion object { 349 private val IMMEDIATE = Dispatchers.Main.immediate 350 } 351 } 352