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.verify 61 import org.mockito.MockitoSession 62 import org.mockito.quality.Strictness 63 64 /** atest SystemUITests:NoteTaskQuickAffordanceConfigTest */ 65 @SmallTest 66 @RunWith(AndroidJUnit4::class) 67 internal class NoteTaskQuickAffordanceConfigTest : SysuiTestCase() { 68 69 @Mock lateinit var controller: NoteTaskController 70 @Mock lateinit var stylusManager: StylusManager 71 @Mock lateinit var repository: KeyguardQuickAffordanceRepository 72 @Mock lateinit var userManager: UserManager 73 @Mock lateinit var roleManager: RoleManager 74 @Mock lateinit var packageManager: PackageManager 75 76 private lateinit var mockitoSession: MockitoSession 77 78 @Before setUpnull79 fun setUp() { 80 mockitoSession = 81 ExtendedMockito.mockitoSession() 82 .initMocks(this) 83 .mockStatic(InputSettings::class.java) 84 .strictness(Strictness.LENIENT) 85 .startMocking() 86 87 whenever( 88 packageManager.getApplicationInfoAsUser( 89 anyString(), 90 any(ApplicationInfoFlags::class.java), 91 any(UserHandle::class.java) 92 ) 93 ) 94 .thenReturn(ApplicationInfo()) 95 whenever(controller.getUserForHandlingNotesTaking(any())).thenReturn(UserHandle.SYSTEM) 96 whenever( 97 roleManager.getRoleHoldersAsUser( 98 eq(RoleManager.ROLE_NOTES), 99 any(UserHandle::class.java) 100 ) 101 ) 102 .thenReturn(listOf("com.google.test.notes")) 103 } 104 105 @After tearDownnull106 fun tearDown() { 107 mockitoSession.finishMocking() 108 } 109 createUnderTestnull110 private fun createUnderTest(isEnabled: Boolean = true): KeyguardQuickAffordanceConfig = 111 NoteTaskQuickAffordanceConfig( 112 context = context, 113 controller = controller, 114 stylusManager = stylusManager, 115 userManager = userManager, 116 keyguardMonitor = mock(), 117 lazyRepository = { repository }, 118 isEnabled = isEnabled, 119 backgroundExecutor = FakeExecutor(FakeSystemClock()), 120 roleManager = roleManager, 121 noteTaskInfoResolver = NoteTaskInfoResolver(roleManager, packageManager) 122 ) 123 createLockScreenStateVisiblenull124 private fun createLockScreenStateVisible(): LockScreenState = 125 LockScreenState.Visible( 126 icon = 127 Icon.Resource( 128 res = R.drawable.ic_note_task_shortcut_keyguard, 129 contentDescription = 130 ContentDescription.Resource(R.string.note_task_button_label), 131 ) 132 ) 133 134 // region lockScreenState 135 @Test 136 fun lockScreenState_stylusUsed_userUnlocked_isSelected_shouldEmitVisible() = runTest { 137 val underTest = createUnderTest() 138 TestConfig().setStylusEverUsed(true).setUserUnlocked(true).setConfigSelections(underTest) 139 140 val actual by collectLastValue(underTest.lockScreenState) 141 142 assertThat(actual).isEqualTo(createLockScreenStateVisible()) 143 } 144 145 @Test lockScreenState_stylusUsed_userUnlocked_isSelected_noDefaultNotesAppSet_shouldEmitHiddennull146 fun lockScreenState_stylusUsed_userUnlocked_isSelected_noDefaultNotesAppSet_shouldEmitHidden() = 147 runTest { 148 val underTest = createUnderTest() 149 TestConfig() 150 .setStylusEverUsed(true) 151 .setUserUnlocked(true) 152 .setConfigSelections(underTest) 153 whenever( 154 roleManager.getRoleHoldersAsUser( 155 eq(RoleManager.ROLE_NOTES), 156 any(UserHandle::class.java) 157 ) 158 ) 159 .thenReturn(emptyList()) 160 161 val actual by collectLastValue(underTest.lockScreenState) 162 163 assertThat(actual).isEqualTo(LockScreenState.Hidden) 164 } 165 166 @Test <lambda>null167 fun lockScreenState_stylusUnused_userUnlocked_isSelected_shouldEmitHidden() = runTest { 168 val underTest = createUnderTest() 169 TestConfig().setStylusEverUsed(false).setUserUnlocked(true).setConfigSelections(underTest) 170 171 val actual by collectLastValue(underTest.lockScreenState) 172 173 assertThat(actual).isEqualTo(LockScreenState.Hidden) 174 } 175 176 @Test <lambda>null177 fun lockScreenState_stylusUsed_userLocked_isSelected_shouldEmitHidden() = runTest { 178 val underTest = createUnderTest() 179 TestConfig().setStylusEverUsed(true).setUserUnlocked(false).setConfigSelections(underTest) 180 181 val actual by collectLastValue(underTest.lockScreenState) 182 183 assertThat(actual).isEqualTo(LockScreenState.Hidden) 184 } 185 186 @Test <lambda>null187 fun lockScreenState_stylusUsed_userUnlocked_noSelected_shouldEmitHidden() = runTest { 188 TestConfig().setStylusEverUsed(true).setUserUnlocked(true).setConfigSelections() 189 190 val underTest = createUnderTest() 191 val actual by collectLastValue(underTest.lockScreenState) 192 193 assertThat(actual).isEqualTo(LockScreenState.Hidden) 194 } 195 196 @Test <lambda>null197 fun lockScreenState_stylusUnused_userUnlocked_noSelected_shouldEmitHidden() = runTest { 198 TestConfig().setStylusEverUsed(false).setUserUnlocked(true).setConfigSelections() 199 200 val underTest = createUnderTest() 201 val actual by collectLastValue(underTest.lockScreenState) 202 203 assertThat(actual).isEqualTo(LockScreenState.Hidden) 204 } 205 206 @Test <lambda>null207 fun lockScreenState_stylusUsed_userLocked_noSelected_shouldEmitHidden() = runTest { 208 TestConfig().setStylusEverUsed(true).setUserUnlocked(false).setConfigSelections() 209 210 val underTest = createUnderTest() 211 val actual by collectLastValue(underTest.lockScreenState) 212 213 assertThat(actual).isEqualTo(LockScreenState.Hidden) 214 } 215 216 @Test <lambda>null217 fun lockScreenState_stylusUsed_userUnlocked_customSelections_shouldEmitHidden() = runTest { 218 TestConfig().setStylusEverUsed(true).setUserUnlocked(true).setConfigSelections(mock()) 219 220 val underTest = createUnderTest() 221 val actual by collectLastValue(underTest.lockScreenState) 222 223 assertThat(actual).isEqualTo(LockScreenState.Hidden) 224 } 225 226 @Test <lambda>null227 fun lockScreenState_stylusUnused_userUnlocked_customSelections_shouldEmitHidden() = runTest { 228 TestConfig().setStylusEverUsed(false).setUserUnlocked(true).setConfigSelections(mock()) 229 230 val underTest = createUnderTest() 231 val actual by collectLastValue(underTest.lockScreenState) 232 233 assertThat(actual).isEqualTo(LockScreenState.Hidden) 234 } 235 236 @Test <lambda>null237 fun lockScreenState_stylusUsed_userLocked_customSelections_shouldEmitHidden() = runTest { 238 TestConfig().setStylusEverUsed(true).setUserUnlocked(false).setConfigSelections(mock()) 239 240 val underTest = createUnderTest() 241 val actual by collectLastValue(underTest.lockScreenState) 242 243 assertThat(actual).isEqualTo(LockScreenState.Hidden) 244 } 245 246 @Test <lambda>null247 fun lockScreenState_isNotEnabled_shouldEmitHidden() = runTest { 248 TestConfig().setStylusEverUsed(true).setUserUnlocked(true).setConfigSelections() 249 250 val underTest = createUnderTest(isEnabled = false) 251 val actual by collectLastValue(underTest.lockScreenState) 252 253 assertThat(actual).isEqualTo(LockScreenState.Hidden) 254 } 255 // endregion 256 257 @Test onTriggered_shouldLaunchNoteTasknull258 fun onTriggered_shouldLaunchNoteTask() { 259 val underTest = createUnderTest(isEnabled = false) 260 261 underTest.onTriggered(expandable = null) 262 263 verify(controller).showNoteTask(entryPoint = NoteTaskEntryPoint.QUICK_AFFORDANCE) 264 } 265 266 // region getPickerScreenState 267 @Test <lambda>null268 fun getPickerScreenState_defaultNoteAppSet_shouldReturnDefault() = runTest { 269 val underTest = createUnderTest(isEnabled = true) 270 271 assertThat(underTest.getPickerScreenState()) 272 .isEqualTo(KeyguardQuickAffordanceConfig.PickerScreenState.Default()) 273 } 274 275 @Test <lambda>null276 fun getPickerScreenState_noDefaultNoteAppSet_shouldReturnDisabled() = runTest { 277 val underTest = createUnderTest(isEnabled = true) 278 whenever( 279 roleManager.getRoleHoldersAsUser( 280 eq(RoleManager.ROLE_NOTES), 281 any(UserHandle::class.java) 282 ) 283 ) 284 .thenReturn(emptyList()) 285 286 val pickerScreenState = underTest.getPickerScreenState() 287 assertThat(pickerScreenState is KeyguardQuickAffordanceConfig.PickerScreenState.Disabled) 288 .isTrue() 289 val disabled = pickerScreenState as KeyguardQuickAffordanceConfig.PickerScreenState.Disabled 290 assertThat(disabled.explanation) 291 .isEqualTo("Select a default notes app to use the notetaking shortcut") 292 assertThat(disabled.actionText).isEqualTo("Select app") 293 assertThat(disabled.actionIntent?.action) 294 .isEqualTo(ACTION_MANAGE_NOTES_ROLE_FROM_QUICK_AFFORDANCE) 295 assertThat(disabled.actionIntent?.`package`).isEqualTo(context.packageName) 296 } 297 // endregion 298 299 private inner class TestConfig { 300 <lambda>null301 fun setStylusEverUsed(value: Boolean) = also { 302 whenever(InputSettings.isStylusEverUsed(mContext)).thenReturn(value) 303 } 304 <lambda>null305 fun setUserUnlocked(value: Boolean) = also { 306 whenever(userManager.isUserUnlocked).thenReturn(value) 307 } 308 <lambda>null309 fun setConfigSelections(vararg values: KeyguardQuickAffordanceConfig) = also { 310 val slotKey = "bottom-right" 311 val configSnapshots = values.toList() 312 val map = mapOf(slotKey to configSnapshots) 313 whenever(repository.selections).thenReturn(MutableStateFlow(map)) 314 } 315 } 316 } 317