1 /* 2 * Copyright (C) 2023 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 */ 17 18 package com.android.systemui.keyguard.domain.interactor 19 20 import androidx.test.ext.junit.runners.AndroidJUnit4 21 import androidx.test.filters.SmallTest 22 import com.android.systemui.SysuiTestCase 23 import com.android.systemui.bouncer.data.repository.FakeKeyguardBouncerRepository 24 import com.android.systemui.bouncer.data.repository.KeyguardBouncerRepository 25 import com.android.systemui.common.ui.data.repository.FakeConfigurationRepository 26 import com.android.systemui.coroutines.collectLastValue 27 import com.android.systemui.doze.util.BurnInHelperWrapper 28 import com.android.systemui.flags.FakeFeatureFlags 29 import com.android.systemui.flags.Flags 30 import com.android.systemui.keyguard.data.repository.FakeCommandQueue 31 import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository 32 import com.android.systemui.keyguard.shared.model.StatusBarState 33 import com.android.systemui.keyguard.shared.model.WakeSleepReason 34 import com.android.systemui.keyguard.shared.model.WakefulnessModel 35 import com.android.systemui.keyguard.shared.model.WakefulnessState 36 import com.android.systemui.shade.data.repository.FakeShadeRepository 37 import com.android.systemui.statusbar.phone.SystemUIDialogManager 38 import com.android.systemui.util.mockito.argumentCaptor 39 import com.android.systemui.util.mockito.eq 40 import com.android.systemui.util.mockito.whenever 41 import com.google.common.truth.Truth.assertThat 42 import kotlinx.coroutines.ExperimentalCoroutinesApi 43 import kotlinx.coroutines.test.TestScope 44 import kotlinx.coroutines.test.runCurrent 45 import kotlinx.coroutines.test.runTest 46 import org.junit.Before 47 import org.junit.Test 48 import org.junit.runner.RunWith 49 import org.mockito.ArgumentMatchers.anyInt 50 import org.mockito.Mock 51 import org.mockito.Mockito.verify 52 import org.mockito.MockitoAnnotations 53 54 @ExperimentalCoroutinesApi 55 @SmallTest 56 @RunWith(AndroidJUnit4::class) 57 class UdfpsKeyguardInteractorTest : SysuiTestCase() { 58 private val burnInProgress = 1f 59 private val burnInYOffset = 20 60 private val burnInXOffset = 10 61 62 private lateinit var testScope: TestScope 63 private lateinit var configRepository: FakeConfigurationRepository 64 private lateinit var bouncerRepository: KeyguardBouncerRepository 65 private lateinit var keyguardRepository: FakeKeyguardRepository 66 private lateinit var fakeCommandQueue: FakeCommandQueue 67 private lateinit var featureFlags: FakeFeatureFlags 68 private lateinit var burnInInteractor: BurnInInteractor 69 private lateinit var shadeRepository: FakeShadeRepository 70 private lateinit var keyguardInteractor: KeyguardInteractor 71 72 @Mock private lateinit var burnInHelper: BurnInHelperWrapper 73 @Mock private lateinit var dialogManager: SystemUIDialogManager 74 75 private lateinit var underTest: UdfpsKeyguardInteractor 76 77 @Before 78 fun setUp() { 79 MockitoAnnotations.initMocks(this) 80 testScope = TestScope() 81 configRepository = FakeConfigurationRepository() 82 featureFlags = 83 FakeFeatureFlags().apply { 84 set(Flags.REFACTOR_UDFPS_KEYGUARD_VIEWS, true) 85 set(Flags.FACE_AUTH_REFACTOR, false) 86 } 87 KeyguardInteractorFactory.create(featureFlags = featureFlags).let { 88 keyguardInteractor = it.keyguardInteractor 89 keyguardRepository = it.repository 90 } 91 bouncerRepository = FakeKeyguardBouncerRepository() 92 shadeRepository = FakeShadeRepository() 93 fakeCommandQueue = FakeCommandQueue() 94 burnInInteractor = 95 BurnInInteractor( 96 context, 97 burnInHelper, 98 testScope.backgroundScope, 99 configRepository, 100 keyguardInteractor 101 ) 102 103 underTest = 104 UdfpsKeyguardInteractor( 105 configRepository, 106 burnInInteractor, 107 keyguardInteractor, 108 shadeRepository, 109 dialogManager, 110 ) 111 } 112 113 @Test 114 fun dozeChanges_updatesUdfpsAodModel() = 115 testScope.runTest { 116 val burnInOffsets by collectLastValue(underTest.burnInOffsets) 117 initializeBurnInOffsets() 118 119 // WHEN we're not dozing 120 setAwake() 121 runCurrent() 122 123 // THEN burn in offsets are 0 124 assertThat(burnInOffsets?.burnInProgress).isEqualTo(0f) 125 assertThat(burnInOffsets?.burnInYOffset).isEqualTo(0) 126 assertThat(burnInOffsets?.burnInXOffset).isEqualTo(0) 127 128 // WHEN we're in the middle of the doze amount change 129 keyguardRepository.setDozeAmount(.50f) 130 runCurrent() 131 132 // THEN burn in is updated (between 0 and the full offset) 133 assertThat(burnInOffsets?.burnInProgress).isGreaterThan(0f) 134 assertThat(burnInOffsets?.burnInYOffset).isGreaterThan(0) 135 assertThat(burnInOffsets?.burnInXOffset).isGreaterThan(0) 136 assertThat(burnInOffsets?.burnInProgress).isLessThan(burnInProgress) 137 assertThat(burnInOffsets?.burnInYOffset).isLessThan(burnInYOffset) 138 assertThat(burnInOffsets?.burnInXOffset).isLessThan(burnInXOffset) 139 140 // WHEN we're fully dozing 141 keyguardRepository.setDozeAmount(1f) 142 runCurrent() 143 144 // THEN burn in offsets are updated to final current values (for the given time) 145 assertThat(burnInOffsets?.burnInProgress).isEqualTo(burnInProgress) 146 assertThat(burnInOffsets?.burnInYOffset).isEqualTo(burnInYOffset) 147 assertThat(burnInOffsets?.burnInXOffset).isEqualTo(burnInXOffset) 148 } 149 150 @Test 151 fun dialogHideAffordances() = 152 testScope.runTest { 153 val dialogHideAffordancesRequest by 154 collectLastValue(underTest.dialogHideAffordancesRequest) 155 runCurrent() 156 val captor = argumentCaptor<SystemUIDialogManager.Listener>() 157 verify(dialogManager).registerListener(captor.capture()) 158 159 captor.value.shouldHideAffordances(false) 160 assertThat(dialogHideAffordancesRequest).isEqualTo(false) 161 162 captor.value.shouldHideAffordances(true) 163 assertThat(dialogHideAffordancesRequest).isEqualTo(true) 164 165 captor.value.shouldHideAffordances(false) 166 assertThat(dialogHideAffordancesRequest).isEqualTo(false) 167 } 168 169 @Test 170 fun shadeExpansion_updates() = 171 testScope.runTest { 172 keyguardRepository.setStatusBarState(StatusBarState.KEYGUARD) 173 val shadeExpansion by collectLastValue(underTest.shadeExpansion) 174 assertThat(shadeExpansion).isEqualTo(0f) 175 176 shadeRepository.setUdfpsTransitionToFullShadeProgress(.5f) 177 assertThat(shadeExpansion).isEqualTo(.5f) 178 179 shadeRepository.setUdfpsTransitionToFullShadeProgress(.7f) 180 assertThat(shadeExpansion).isEqualTo(.7f) 181 182 shadeRepository.setUdfpsTransitionToFullShadeProgress(.22f) 183 assertThat(shadeExpansion).isEqualTo(.22f) 184 185 keyguardRepository.setStatusBarState(StatusBarState.SHADE_LOCKED) 186 assertThat(shadeExpansion).isEqualTo(1f) 187 } 188 189 @Test 190 fun qsProgress_updates() = 191 testScope.runTest { 192 val qsProgress by collectLastValue(underTest.qsProgress) 193 assertThat(qsProgress).isEqualTo(0f) 194 195 shadeRepository.setQsExpansion(.22f) 196 assertThat(qsProgress).isEqualTo(.44f) 197 198 shadeRepository.setQsExpansion(.5f) 199 assertThat(qsProgress).isEqualTo(1f) 200 201 shadeRepository.setQsExpansion(.7f) 202 assertThat(qsProgress).isEqualTo(1f) 203 } 204 205 private fun initializeBurnInOffsets() { 206 whenever(burnInHelper.burnInProgressOffset()).thenReturn(burnInProgress) 207 whenever(burnInHelper.burnInOffset(anyInt(), /* xAxis */ eq(true))) 208 .thenReturn(burnInXOffset) 209 whenever(burnInHelper.burnInOffset(anyInt(), /* xAxis */ eq(false))) 210 .thenReturn(burnInYOffset) 211 } 212 213 private fun setAwake() { 214 keyguardRepository.setDozeAmount(0f) 215 keyguardRepository.dozeTimeTick() 216 217 bouncerRepository.setAlternateVisible(false) 218 keyguardRepository.setStatusBarState(StatusBarState.KEYGUARD) 219 bouncerRepository.setPrimaryShow(false) 220 keyguardRepository.setWakefulnessModel( 221 WakefulnessModel( 222 WakefulnessState.AWAKE, 223 WakeSleepReason.POWER_BUTTON, 224 WakeSleepReason.POWER_BUTTON, 225 ) 226 ) 227 } 228 } 229