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 android.content.Intent 21 import androidx.test.ext.junit.runners.AndroidJUnit4 22 import androidx.test.filters.SmallTest 23 import com.android.internal.logging.UiEventLogger 24 import com.android.systemui.R 25 import com.android.systemui.RoboPilotTest 26 import com.android.systemui.SysuiTestCase 27 import com.android.systemui.coroutines.collectLastValue 28 import com.android.systemui.flags.FakeFeatureFlags 29 import com.android.systemui.flags.Flags 30 import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository 31 import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository 32 import com.android.systemui.keyguard.shared.model.KeyguardState 33 import com.android.systemui.keyguard.shared.model.TransitionStep 34 import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper 35 import com.android.systemui.util.mockito.whenever 36 import com.google.common.truth.Truth.assertThat 37 import kotlinx.coroutines.ExperimentalCoroutinesApi 38 import kotlinx.coroutines.runBlocking 39 import kotlinx.coroutines.test.TestScope 40 import kotlinx.coroutines.test.advanceTimeBy 41 import kotlinx.coroutines.test.runCurrent 42 import kotlinx.coroutines.test.runTest 43 import org.junit.After 44 import org.junit.Before 45 import org.junit.Test 46 import org.junit.runner.RunWith 47 import org.mockito.ArgumentMatchers.anyInt 48 import org.mockito.Mock 49 import org.mockito.Mockito.verify 50 import org.mockito.MockitoAnnotations 51 52 @OptIn(ExperimentalCoroutinesApi::class) 53 @SmallTest 54 @RoboPilotTest 55 @RunWith(AndroidJUnit4::class) 56 class KeyguardLongPressInteractorTest : SysuiTestCase() { 57 58 @Mock private lateinit var logger: UiEventLogger 59 @Mock private lateinit var accessibilityManager: AccessibilityManagerWrapper 60 61 private lateinit var underTest: KeyguardLongPressInteractor 62 63 private lateinit var testScope: TestScope 64 private lateinit var keyguardRepository: FakeKeyguardRepository 65 private lateinit var keyguardTransitionRepository: FakeKeyguardTransitionRepository 66 67 @Before 68 fun setUp() { 69 MockitoAnnotations.initMocks(this) 70 overrideResource(R.bool.long_press_keyguard_customize_lockscreen_enabled, true) 71 whenever(accessibilityManager.getRecommendedTimeoutMillis(anyInt(), anyInt())).thenAnswer { 72 it.arguments[0] 73 } 74 75 testScope = TestScope() 76 keyguardRepository = FakeKeyguardRepository() 77 keyguardTransitionRepository = FakeKeyguardTransitionRepository() 78 79 runBlocking { createUnderTest() } 80 } 81 82 @After 83 fun tearDown() { 84 mContext 85 .getOrCreateTestableResources() 86 .removeOverride(R.bool.long_press_keyguard_customize_lockscreen_enabled) 87 } 88 89 @Test 90 fun isEnabled() = 91 testScope.runTest { 92 val isEnabled = collectLastValue(underTest.isLongPressHandlingEnabled) 93 KeyguardState.values().forEach { keyguardState -> 94 setUpState( 95 keyguardState = keyguardState, 96 ) 97 98 if (keyguardState == KeyguardState.LOCKSCREEN) { 99 assertThat(isEnabled()).isTrue() 100 } else { 101 assertThat(isEnabled()).isFalse() 102 } 103 } 104 } 105 106 @Test 107 fun isEnabled_alwaysFalseWhenQuickSettingsAreVisible() = 108 testScope.runTest { 109 val isEnabled = collectLastValue(underTest.isLongPressHandlingEnabled) 110 KeyguardState.values().forEach { keyguardState -> 111 setUpState( 112 keyguardState = keyguardState, 113 isQuickSettingsVisible = true, 114 ) 115 116 assertThat(isEnabled()).isFalse() 117 } 118 } 119 120 @Test 121 fun isEnabled_alwaysFalseWhenConfigEnabledBooleanIsFalse() = 122 testScope.runTest { 123 overrideResource(R.bool.long_press_keyguard_customize_lockscreen_enabled, false) 124 createUnderTest() 125 val isEnabled by collectLastValue(underTest.isLongPressHandlingEnabled) 126 runCurrent() 127 128 assertThat(isEnabled).isFalse() 129 } 130 131 @Test 132 fun longPressed_menuClicked_showsSettings() = 133 testScope.runTest { 134 val isMenuVisible by collectLastValue(underTest.isMenuVisible) 135 val shouldOpenSettings by collectLastValue(underTest.shouldOpenSettings) 136 runCurrent() 137 138 underTest.onLongPress() 139 assertThat(isMenuVisible).isTrue() 140 141 underTest.onMenuTouchGestureEnded(/* isClick= */ true) 142 143 assertThat(isMenuVisible).isFalse() 144 assertThat(shouldOpenSettings).isTrue() 145 } 146 147 @Test 148 fun onSettingsShown_consumesSettingsShowEvent() = 149 testScope.runTest { 150 val shouldOpenSettings by collectLastValue(underTest.shouldOpenSettings) 151 runCurrent() 152 153 underTest.onLongPress() 154 underTest.onMenuTouchGestureEnded(/* isClick= */ true) 155 assertThat(shouldOpenSettings).isTrue() 156 157 underTest.onSettingsShown() 158 assertThat(shouldOpenSettings).isFalse() 159 } 160 161 @Test 162 fun onTouchedOutside_neverShowsSettings() = 163 testScope.runTest { 164 val isMenuVisible by collectLastValue(underTest.isMenuVisible) 165 val shouldOpenSettings by collectLastValue(underTest.shouldOpenSettings) 166 runCurrent() 167 168 underTest.onTouchedOutside() 169 170 assertThat(isMenuVisible).isFalse() 171 assertThat(shouldOpenSettings).isFalse() 172 } 173 174 @Test 175 fun longPressed_openWppDirectlyEnabled_doesNotShowMenu_opensSettings() = 176 testScope.runTest { 177 createUnderTest(isOpenWppDirectlyEnabled = true) 178 val isMenuVisible by collectLastValue(underTest.isMenuVisible) 179 val shouldOpenSettings by collectLastValue(underTest.shouldOpenSettings) 180 runCurrent() 181 182 underTest.onLongPress() 183 184 assertThat(isMenuVisible).isFalse() 185 assertThat(shouldOpenSettings).isTrue() 186 } 187 188 @Test 189 fun longPressed_closeDialogsBroadcastReceived_popupDismissed() = 190 testScope.runTest { 191 val isMenuVisible by collectLastValue(underTest.isMenuVisible) 192 runCurrent() 193 194 underTest.onLongPress() 195 assertThat(isMenuVisible).isTrue() 196 197 fakeBroadcastDispatcher.sendIntentToMatchingReceiversOnly( 198 context, 199 Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS), 200 ) 201 202 assertThat(isMenuVisible).isFalse() 203 } 204 205 @Test 206 fun closesDialogAfterTimeout() = 207 testScope.runTest { 208 val isMenuVisible by collectLastValue(underTest.isMenuVisible) 209 runCurrent() 210 211 underTest.onLongPress() 212 assertThat(isMenuVisible).isTrue() 213 214 advanceTimeBy(KeyguardLongPressInteractor.DEFAULT_POPUP_AUTO_HIDE_TIMEOUT_MS) 215 216 assertThat(isMenuVisible).isFalse() 217 } 218 219 @Test 220 fun closesDialogAfterTimeout_onlyAfterTouchGestureEnded() = 221 testScope.runTest { 222 val isMenuVisible by collectLastValue(underTest.isMenuVisible) 223 runCurrent() 224 225 underTest.onLongPress() 226 assertThat(isMenuVisible).isTrue() 227 underTest.onMenuTouchGestureStarted() 228 229 advanceTimeBy(KeyguardLongPressInteractor.DEFAULT_POPUP_AUTO_HIDE_TIMEOUT_MS) 230 assertThat(isMenuVisible).isTrue() 231 232 underTest.onMenuTouchGestureEnded(/* isClick= */ false) 233 advanceTimeBy(KeyguardLongPressInteractor.DEFAULT_POPUP_AUTO_HIDE_TIMEOUT_MS) 234 assertThat(isMenuVisible).isFalse() 235 } 236 237 @Test 238 fun logsWhenMenuIsShown() = 239 testScope.runTest { 240 collectLastValue(underTest.isMenuVisible) 241 runCurrent() 242 243 underTest.onLongPress() 244 245 verify(logger) 246 .log(KeyguardLongPressInteractor.LogEvents.LOCK_SCREEN_LONG_PRESS_POPUP_SHOWN) 247 } 248 249 @Test 250 fun logsWhenMenuIsClicked() = 251 testScope.runTest { 252 collectLastValue(underTest.isMenuVisible) 253 runCurrent() 254 255 underTest.onLongPress() 256 underTest.onMenuTouchGestureEnded(/* isClick= */ true) 257 258 verify(logger) 259 .log(KeyguardLongPressInteractor.LogEvents.LOCK_SCREEN_LONG_PRESS_POPUP_CLICKED) 260 } 261 262 @Test 263 fun showMenu_leaveLockscreen_returnToLockscreen_menuNotVisible() = 264 testScope.runTest { 265 val isMenuVisible by collectLastValue(underTest.isMenuVisible) 266 runCurrent() 267 underTest.onLongPress() 268 assertThat(isMenuVisible).isTrue() 269 270 keyguardTransitionRepository.sendTransitionStep( 271 TransitionStep( 272 to = KeyguardState.GONE, 273 ), 274 ) 275 assertThat(isMenuVisible).isFalse() 276 277 keyguardTransitionRepository.sendTransitionStep( 278 TransitionStep( 279 to = KeyguardState.LOCKSCREEN, 280 ), 281 ) 282 assertThat(isMenuVisible).isFalse() 283 } 284 285 private suspend fun createUnderTest( 286 isLongPressFeatureEnabled: Boolean = true, 287 isRevampedWppFeatureEnabled: Boolean = true, 288 isOpenWppDirectlyEnabled: Boolean = false, 289 ) { 290 underTest = 291 KeyguardLongPressInteractor( 292 appContext = mContext, 293 scope = testScope.backgroundScope, 294 transitionInteractor = 295 KeyguardTransitionInteractorFactory.create( 296 scope = testScope.backgroundScope, 297 repository = keyguardTransitionRepository, 298 ) 299 .keyguardTransitionInteractor, 300 repository = keyguardRepository, 301 logger = logger, 302 featureFlags = 303 FakeFeatureFlags().apply { 304 set(Flags.LOCK_SCREEN_LONG_PRESS_ENABLED, isLongPressFeatureEnabled) 305 set(Flags.REVAMPED_WALLPAPER_UI, isRevampedWppFeatureEnabled) 306 set(Flags.LOCK_SCREEN_LONG_PRESS_DIRECT_TO_WPP, isOpenWppDirectlyEnabled) 307 }, 308 broadcastDispatcher = fakeBroadcastDispatcher, 309 accessibilityManager = accessibilityManager 310 ) 311 setUpState() 312 } 313 314 private suspend fun setUpState( 315 keyguardState: KeyguardState = KeyguardState.LOCKSCREEN, 316 isQuickSettingsVisible: Boolean = false, 317 ) { 318 keyguardTransitionRepository.sendTransitionStep( 319 TransitionStep( 320 to = keyguardState, 321 ), 322 ) 323 keyguardRepository.setQuickSettingsVisible(isVisible = isQuickSettingsVisible) 324 } 325 } 326