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 @file:OptIn(ExperimentalCoroutinesApi::class) 18 19 package com.android.systemui.notetask.quickaffordance 20 21 import android.app.role.RoleManager 22 import android.content.pm.ApplicationInfo 23 import android.content.pm.PackageManager 24 import android.content.pm.PackageManager.ApplicationInfoFlags 25 import android.hardware.input.InputSettings 26 import android.os.UserHandle 27 import android.os.UserManager 28 import androidx.test.ext.junit.runners.AndroidJUnit4 29 import androidx.test.filters.SmallTest 30 import com.android.dx.mockito.inline.extended.ExtendedMockito 31 import com.android.systemui.SysuiTestCase 32 import com.android.systemui.common.shared.model.ContentDescription 33 import com.android.systemui.common.shared.model.Icon 34 import com.android.systemui.coroutines.collectLastValue 35 import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceConfig 36 import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceConfig.LockScreenState 37 import com.android.systemui.keyguard.data.repository.KeyguardQuickAffordanceRepository 38 import com.android.systemui.notetask.LaunchNotesRoleSettingsTrampolineActivity.Companion.ACTION_MANAGE_NOTES_ROLE_FROM_QUICK_AFFORDANCE 39 import com.android.systemui.notetask.NoteTaskController 40 import com.android.systemui.notetask.NoteTaskEntryPoint 41 import com.android.systemui.notetask.NoteTaskInfoResolver 42 import com.android.systemui.res.R 43 import com.android.systemui.stylus.StylusManager 44 import com.android.systemui.util.concurrency.FakeExecutor 45 import com.android.systemui.util.mockito.any 46 import com.android.systemui.util.mockito.eq 47 import com.android.systemui.util.mockito.mock 48 import com.android.systemui.util.mockito.whenever 49 import com.android.systemui.util.time.FakeSystemClock 50 import com.google.common.truth.Truth.assertThat 51 import kotlinx.coroutines.ExperimentalCoroutinesApi 52 import kotlinx.coroutines.flow.MutableStateFlow 53 import kotlinx.coroutines.test.runTest 54 import org.junit.After 55 import org.junit.Before 56 import org.junit.Test 57 import org.junit.runner.RunWith 58 import org.mockito.Mock 59 import org.mockito.Mockito.anyString 60 import org.mockito.Mockito.spy 61 import org.mockito.Mockito.verify 62 import org.mockito.Mockito.`when` 63 import org.mockito.MockitoSession 64 import org.mockito.quality.Strictness 65 66 /** atest SystemUITests:NoteTaskQuickAffordanceConfigTest */ 67 @SmallTest 68 @RunWith(AndroidJUnit4::class) 69 internal class NoteTaskQuickAffordanceConfigTest : SysuiTestCase() { 70 71 @Mock lateinit var controller: NoteTaskController 72 @Mock lateinit var stylusManager: StylusManager 73 @Mock lateinit var repository: KeyguardQuickAffordanceRepository 74 @Mock lateinit var userManager: UserManager 75 @Mock lateinit var roleManager: RoleManager 76 @Mock lateinit var packageManager: PackageManager 77 78 private lateinit var mockitoSession: MockitoSession 79 80 private val spiedContext = spy(context) 81 private val spiedResources = spy(spiedContext.resources) 82 83 @Before setUpnull84 fun setUp() { 85 mockitoSession = 86 ExtendedMockito.mockitoSession() 87 .initMocks(this) 88 .mockStatic(InputSettings::class.java) 89 .strictness(Strictness.LENIENT) 90 .startMocking() 91 92 whenever( 93 packageManager.getApplicationInfoAsUser( 94 anyString(), 95 any(ApplicationInfoFlags::class.java), 96 any(UserHandle::class.java) 97 ) 98 ) 99 .thenReturn(ApplicationInfo()) 100 whenever(controller.getUserForHandlingNotesTaking(any())).thenReturn(UserHandle.SYSTEM) 101 whenever( 102 roleManager.getRoleHoldersAsUser( 103 eq(RoleManager.ROLE_NOTES), 104 any(UserHandle::class.java) 105 ) 106 ) 107 .thenReturn(listOf("com.google.test.notes")) 108 109 `when`(spiedContext.resources).thenReturn(spiedResources) 110 } 111 112 @After tearDownnull113 fun tearDown() { 114 mockitoSession.finishMocking() 115 } 116 createUnderTestnull117 private fun createUnderTest(isEnabled: Boolean = true): KeyguardQuickAffordanceConfig = 118 NoteTaskQuickAffordanceConfig( 119 context = spiedContext, 120 controller = controller, 121 stylusManager = stylusManager, 122 userManager = userManager, 123 keyguardMonitor = mock(), 124 lazyRepository = { repository }, 125 isEnabled = isEnabled, 126 backgroundExecutor = FakeExecutor(FakeSystemClock()), 127 roleManager = roleManager, 128 noteTaskInfoResolver = NoteTaskInfoResolver(roleManager, packageManager) 129 ) 130 createLockScreenStateVisiblenull131 private fun createLockScreenStateVisible(): LockScreenState = 132 LockScreenState.Visible( 133 icon = 134 Icon.Resource( 135 res = R.drawable.ic_note_task_shortcut_keyguard, 136 contentDescription = 137 ContentDescription.Resource(R.string.note_task_button_label), 138 ) 139 ) 140 141 // region lockScreenState 142 @Suppress("ktlint:standard:max-line-length") 143 @Test 144 fun lockScreenState_stylusUnused_userLocked_customizationDisabled_notesLockScreenShortcutNotSelected_shouldEmitHidden() = 145 runTest { 146 val underTest = createUnderTest() 147 TestConfig() 148 .setStylusEverUsed(false) 149 .setUserUnlocked(false) 150 .setLockScreenCustomizationEnabled(false) 151 .setConfigSelections() 152 153 val actual by collectLastValue(underTest.lockScreenState) 154 155 assertThat(actual).isEqualTo(LockScreenState.Hidden) 156 } 157 158 @Suppress("ktlint:standard:max-line-length") 159 @Test lockScreenState_stylusUnused_userLocked_customizationDisabled_notesLockScreenShortcutSelected_shouldEmitHiddennull160 fun lockScreenState_stylusUnused_userLocked_customizationDisabled_notesLockScreenShortcutSelected_shouldEmitHidden() = 161 runTest { 162 val underTest = createUnderTest() 163 TestConfig() 164 .setStylusEverUsed(false) 165 .setUserUnlocked(false) 166 .setLockScreenCustomizationEnabled(false) 167 .setConfigSelections(underTest) 168 169 val actual by collectLastValue(underTest.lockScreenState) 170 171 assertThat(actual).isEqualTo(LockScreenState.Hidden) 172 } 173 174 @Suppress("ktlint:standard:max-line-length") 175 @Test lockScreenState_stylusUnused_userLocked_customizationEnabled_notesLockScreenShortcutNotSelected_shouldEmitHiddennull176 fun lockScreenState_stylusUnused_userLocked_customizationEnabled_notesLockScreenShortcutNotSelected_shouldEmitHidden() = 177 runTest { 178 val underTest = createUnderTest() 179 TestConfig() 180 .setStylusEverUsed(false) 181 .setUserUnlocked(false) 182 .setLockScreenCustomizationEnabled(true) 183 .setConfigSelections() 184 185 val actual by collectLastValue(underTest.lockScreenState) 186 187 assertThat(actual).isEqualTo(LockScreenState.Hidden) 188 } 189 190 @Suppress("ktlint:standard:max-line-length") 191 @Test lockScreenState_stylusUnused_userLocked_customizationEnabled_notesLockScreenShortcutSelected_shouldEmitHiddennull192 fun lockScreenState_stylusUnused_userLocked_customizationEnabled_notesLockScreenShortcutSelected_shouldEmitHidden() = 193 runTest { 194 val underTest = createUnderTest() 195 TestConfig() 196 .setStylusEverUsed(false) 197 .setUserUnlocked(false) 198 .setLockScreenCustomizationEnabled(true) 199 .setConfigSelections(underTest) 200 201 val actual by collectLastValue(underTest.lockScreenState) 202 203 assertThat(actual).isEqualTo(LockScreenState.Hidden) 204 } 205 206 @Suppress("ktlint:standard:max-line-length") 207 @Test lockScreenState_stylusUnused_userUnlocked_customizationDisabled_notesLockScreenShortcutNotSelected_shouldEmitHiddennull208 fun lockScreenState_stylusUnused_userUnlocked_customizationDisabled_notesLockScreenShortcutNotSelected_shouldEmitHidden() = 209 runTest { 210 val underTest = createUnderTest() 211 TestConfig() 212 .setStylusEverUsed(false) 213 .setUserUnlocked(true) 214 .setLockScreenCustomizationEnabled(false) 215 .setConfigSelections() 216 217 val actual by collectLastValue(underTest.lockScreenState) 218 219 assertThat(actual).isEqualTo(LockScreenState.Hidden) 220 } 221 222 @Suppress("ktlint:standard:max-line-length") 223 @Test lockScreenState_stylusUnused_userUnlocked_customizationDisabled_notesLockScreenShortcutSelected_shouldEmitHiddennull224 fun lockScreenState_stylusUnused_userUnlocked_customizationDisabled_notesLockScreenShortcutSelected_shouldEmitHidden() = 225 runTest { 226 val underTest = createUnderTest() 227 TestConfig() 228 .setStylusEverUsed(false) 229 .setUserUnlocked(true) 230 .setLockScreenCustomizationEnabled(false) 231 .setConfigSelections(underTest) 232 233 val actual by collectLastValue(underTest.lockScreenState) 234 235 assertThat(actual).isEqualTo(LockScreenState.Hidden) 236 } 237 238 @Suppress("ktlint:standard:max-line-length") 239 @Test lockScreenState_stylusUnused_userUnlocked_customizationEnabled_notesLockScreenShortcutNotSelected_shouldEmitHiddennull240 fun lockScreenState_stylusUnused_userUnlocked_customizationEnabled_notesLockScreenShortcutNotSelected_shouldEmitHidden() = 241 runTest { 242 val underTest = createUnderTest() 243 TestConfig() 244 .setStylusEverUsed(false) 245 .setUserUnlocked(true) 246 .setLockScreenCustomizationEnabled(true) 247 .setConfigSelections() 248 249 val actual by collectLastValue(underTest.lockScreenState) 250 251 assertThat(actual).isEqualTo(LockScreenState.Hidden) 252 } 253 254 @Suppress("ktlint:standard:max-line-length") 255 @Test lockScreenState_stylusUnused_userUnlocked_customizationEnabled_notesLockScreenShortcutSelected_shouldEmitVisiblenull256 fun lockScreenState_stylusUnused_userUnlocked_customizationEnabled_notesLockScreenShortcutSelected_shouldEmitVisible() = 257 runTest { 258 val underTest = createUnderTest() 259 TestConfig() 260 .setStylusEverUsed(false) 261 .setUserUnlocked(true) 262 .setLockScreenCustomizationEnabled(true) 263 .setConfigSelections(underTest) 264 265 val actual by collectLastValue(underTest.lockScreenState) 266 267 assertThat(actual).isEqualTo(createLockScreenStateVisible()) 268 } 269 270 @Suppress("ktlint:standard:max-line-length") 271 @Test lockScreenState_stylusUsed_userLocked_customizationDisabled_notesLockScreenShortcutNotSelected_shouldEmitHiddennull272 fun lockScreenState_stylusUsed_userLocked_customizationDisabled_notesLockScreenShortcutNotSelected_shouldEmitHidden() = 273 runTest { 274 val underTest = createUnderTest() 275 TestConfig() 276 .setStylusEverUsed(true) 277 .setUserUnlocked(false) 278 .setLockScreenCustomizationEnabled(false) 279 .setConfigSelections() 280 281 val actual by collectLastValue(underTest.lockScreenState) 282 283 assertThat(actual).isEqualTo(LockScreenState.Hidden) 284 } 285 286 @Suppress("ktlint:standard:max-line-length") 287 @Test lockScreenState_stylusUsed_userLocked_customizationDisabled_notesLockScreenShortcutSelected_shouldEmitHiddennull288 fun lockScreenState_stylusUsed_userLocked_customizationDisabled_notesLockScreenShortcutSelected_shouldEmitHidden() = 289 runTest { 290 val underTest = createUnderTest() 291 TestConfig() 292 .setStylusEverUsed(true) 293 .setUserUnlocked(false) 294 .setLockScreenCustomizationEnabled(false) 295 .setConfigSelections(underTest) 296 297 val actual by collectLastValue(underTest.lockScreenState) 298 299 assertThat(actual).isEqualTo(LockScreenState.Hidden) 300 } 301 302 @Suppress("ktlint:standard:max-line-length") 303 @Test lockScreenState_stylusUsed_userLocked_customizationEnabled_notesLockScreenShortcutNotSelected_shouldEmitHiddennull304 fun lockScreenState_stylusUsed_userLocked_customizationEnabled_notesLockScreenShortcutNotSelected_shouldEmitHidden() = 305 runTest { 306 val underTest = createUnderTest() 307 TestConfig() 308 .setStylusEverUsed(true) 309 .setUserUnlocked(false) 310 .setLockScreenCustomizationEnabled(true) 311 .setConfigSelections() 312 313 val actual by collectLastValue(underTest.lockScreenState) 314 315 assertThat(actual).isEqualTo(LockScreenState.Hidden) 316 } 317 318 @Suppress("ktlint:standard:max-line-length") 319 @Test lockScreenState_stylusUsed_userLocked_customizationEnabled_notesLockScreenShortcutSelected_shouldEmitHiddennull320 fun lockScreenState_stylusUsed_userLocked_customizationEnabled_notesLockScreenShortcutSelected_shouldEmitHidden() = 321 runTest { 322 val underTest = createUnderTest() 323 TestConfig() 324 .setStylusEverUsed(true) 325 .setUserUnlocked(false) 326 .setLockScreenCustomizationEnabled(true) 327 .setConfigSelections(underTest) 328 329 val actual by collectLastValue(underTest.lockScreenState) 330 331 assertThat(actual).isEqualTo(LockScreenState.Hidden) 332 } 333 334 @Suppress("ktlint:standard:max-line-length") 335 @Test lockScreenState_stylusUsed_userUnlocked_customizationDisabled_notesLockScreenShortcutNotSelected_shouldEmitVisiblenull336 fun lockScreenState_stylusUsed_userUnlocked_customizationDisabled_notesLockScreenShortcutNotSelected_shouldEmitVisible() = 337 runTest { 338 val underTest = createUnderTest() 339 TestConfig() 340 .setStylusEverUsed(true) 341 .setUserUnlocked(true) 342 .setLockScreenCustomizationEnabled(false) 343 .setConfigSelections() 344 345 val actual by collectLastValue(underTest.lockScreenState) 346 347 assertThat(actual).isEqualTo(createLockScreenStateVisible()) 348 } 349 350 @Suppress("ktlint:standard:max-line-length") 351 @Test lockScreenState_stylusUsed_userUnlocked_customizationDisabled_notesLockScreenShortcutSelected_shouldEmitVisiblenull352 fun lockScreenState_stylusUsed_userUnlocked_customizationDisabled_notesLockScreenShortcutSelected_shouldEmitVisible() = 353 runTest { 354 val underTest = createUnderTest() 355 TestConfig() 356 .setStylusEverUsed(true) 357 .setUserUnlocked(true) 358 .setLockScreenCustomizationEnabled(false) 359 .setConfigSelections(underTest) 360 361 val actual by collectLastValue(underTest.lockScreenState) 362 363 assertThat(actual).isEqualTo(createLockScreenStateVisible()) 364 } 365 366 @Suppress("ktlint:standard:max-line-length") 367 @Test lockScreenState_stylusUsed_userUnlocked_customizationEnabled_notesLockScreenShortcutNotSelected_shouldEmitHiddennull368 fun lockScreenState_stylusUsed_userUnlocked_customizationEnabled_notesLockScreenShortcutNotSelected_shouldEmitHidden() = 369 runTest { 370 val underTest = createUnderTest() 371 TestConfig() 372 .setStylusEverUsed(true) 373 .setUserUnlocked(true) 374 .setLockScreenCustomizationEnabled(true) 375 .setConfigSelections() 376 377 val actual by collectLastValue(underTest.lockScreenState) 378 379 assertThat(actual).isEqualTo(LockScreenState.Hidden) 380 } 381 382 @Suppress("ktlint:standard:max-line-length") 383 @Test lockScreenState_stylusUsed_userUnlocked_customizationEnabled_notesLockScreenShortcutSelected_shouldEmitVisiblenull384 fun lockScreenState_stylusUsed_userUnlocked_customizationEnabled_notesLockScreenShortcutSelected_shouldEmitVisible() = 385 runTest { 386 val underTest = createUnderTest() 387 TestConfig() 388 .setStylusEverUsed(true) 389 .setUserUnlocked(true) 390 .setLockScreenCustomizationEnabled(true) 391 .setConfigSelections(underTest) 392 393 val actual by collectLastValue(underTest.lockScreenState) 394 395 assertThat(actual).isEqualTo(createLockScreenStateVisible()) 396 } 397 398 // endregion 399 400 @Test onTriggered_shouldLaunchNoteTasknull401 fun onTriggered_shouldLaunchNoteTask() { 402 val underTest = createUnderTest(isEnabled = false) 403 404 underTest.onTriggered(expandable = null) 405 406 verify(controller).showNoteTask(entryPoint = NoteTaskEntryPoint.QUICK_AFFORDANCE) 407 } 408 409 // region getPickerScreenState 410 @Test <lambda>null411 fun getPickerScreenState_defaultNoteAppSet_shouldReturnDefault() = runTest { 412 val underTest = createUnderTest(isEnabled = true) 413 414 assertThat(underTest.getPickerScreenState()) 415 .isEqualTo(KeyguardQuickAffordanceConfig.PickerScreenState.Default()) 416 } 417 418 @Test <lambda>null419 fun getPickerScreenState_noDefaultNoteAppSet_shouldReturnDisabled() = runTest { 420 val underTest = createUnderTest(isEnabled = true) 421 whenever( 422 roleManager.getRoleHoldersAsUser( 423 eq(RoleManager.ROLE_NOTES), 424 any(UserHandle::class.java) 425 ) 426 ) 427 .thenReturn(emptyList()) 428 429 val pickerScreenState = underTest.getPickerScreenState() 430 assertThat(pickerScreenState is KeyguardQuickAffordanceConfig.PickerScreenState.Disabled) 431 .isTrue() 432 val disabled = pickerScreenState as KeyguardQuickAffordanceConfig.PickerScreenState.Disabled 433 assertThat(disabled.explanation) 434 .isEqualTo("Select a default notes app to use the notetaking shortcut") 435 assertThat(disabled.actionText).isEqualTo("Select app") 436 assertThat(disabled.actionIntent?.action) 437 .isEqualTo(ACTION_MANAGE_NOTES_ROLE_FROM_QUICK_AFFORDANCE) 438 assertThat(disabled.actionIntent?.`package`).isEqualTo(context.packageName) 439 } 440 441 // endregion 442 443 private inner class TestConfig { 444 <lambda>null445 fun setStylusEverUsed(value: Boolean) = also { 446 whenever(InputSettings.isStylusEverUsed(spiedContext)).thenReturn(value) 447 } 448 <lambda>null449 fun setUserUnlocked(value: Boolean) = also { 450 whenever(userManager.isUserUnlocked).thenReturn(value) 451 } 452 <lambda>null453 fun setLockScreenCustomizationEnabled(value: Boolean) = also { 454 `when`(spiedResources.getBoolean(R.bool.custom_lockscreen_shortcuts_enabled)) 455 .thenReturn(value) 456 } 457 <lambda>null458 fun setConfigSelections(vararg values: KeyguardQuickAffordanceConfig) = also { 459 val slotKey = "bottom-right" 460 val configSnapshots = values.toList() 461 val map = mapOf(slotKey to configSnapshots) 462 whenever(repository.selections).thenReturn(MutableStateFlow(map)) 463 } 464 } 465 } 466