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  */
17 
18 package com.android.systemui.keyguard.domain.interactor
19 
20 import android.app.admin.DevicePolicyManager
21 import android.os.UserHandle
22 import androidx.test.ext.junit.runners.AndroidJUnit4
23 import androidx.test.filters.SmallTest
24 import com.android.internal.widget.LockPatternUtils
25 import com.android.keyguard.logging.KeyguardQuickAffordancesLogger
26 import com.android.systemui.SysuiTestCase
27 import com.android.systemui.animation.DialogTransitionAnimator
28 import com.android.systemui.common.shared.model.ContentDescription
29 import com.android.systemui.common.shared.model.Icon
30 import com.android.systemui.coroutines.collectLastValue
31 import com.android.systemui.coroutines.collectValues
32 import com.android.systemui.dock.DockManager
33 import com.android.systemui.dock.DockManagerFake
34 import com.android.systemui.flags.EnableSceneContainer
35 import com.android.systemui.flags.FakeFeatureFlags
36 import com.android.systemui.keyguard.data.quickaffordance.BuiltInKeyguardQuickAffordanceKeys
37 import com.android.systemui.keyguard.data.quickaffordance.FakeKeyguardQuickAffordanceConfig
38 import com.android.systemui.keyguard.data.quickaffordance.FakeKeyguardQuickAffordanceProviderClientFactory
39 import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceConfig
40 import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceLegacySettingSyncer
41 import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceLocalUserSelectionManager
42 import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceRemoteUserSelectionManager
43 import com.android.systemui.keyguard.data.repository.FakeBiometricSettingsRepository
44 import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
45 import com.android.systemui.keyguard.data.repository.KeyguardQuickAffordanceRepository
46 import com.android.systemui.keyguard.domain.model.KeyguardQuickAffordanceModel
47 import com.android.systemui.keyguard.shared.model.KeyguardQuickAffordancePickerRepresentation
48 import com.android.systemui.keyguard.shared.quickaffordance.ActivationState
49 import com.android.systemui.keyguard.shared.quickaffordance.KeyguardQuickAffordancePosition
50 import com.android.systemui.keyguard.shared.quickaffordance.KeyguardQuickAffordancesMetricsLogger
51 import com.android.systemui.kosmos.testDispatcher
52 import com.android.systemui.kosmos.testScope
53 import com.android.systemui.plugins.ActivityStarter
54 import com.android.systemui.res.R
55 import com.android.systemui.scene.domain.interactor.sceneInteractor
56 import com.android.systemui.settings.UserFileManager
57 import com.android.systemui.settings.UserTracker
58 import com.android.systemui.shade.cameraLauncher
59 import com.android.systemui.shade.domain.interactor.ShadeInteractor
60 import com.android.systemui.shared.keyguard.shared.model.KeyguardQuickAffordanceSlots
61 import com.android.systemui.statusbar.policy.KeyguardStateController
62 import com.android.systemui.testKosmos
63 import com.android.systemui.util.FakeSharedPreferences
64 import com.android.systemui.util.mockito.mock
65 import com.android.systemui.util.mockito.whenever
66 import com.android.systemui.util.settings.fakeSettings
67 import com.google.common.truth.Truth.assertThat
68 import kotlinx.coroutines.ExperimentalCoroutinesApi
69 import kotlinx.coroutines.flow.MutableStateFlow
70 import kotlinx.coroutines.test.runCurrent
71 import kotlinx.coroutines.test.runTest
72 import org.junit.Before
73 import org.junit.Test
74 import org.junit.runner.RunWith
75 import org.mockito.ArgumentMatchers.anyInt
76 import org.mockito.ArgumentMatchers.anyString
77 import org.mockito.Mock
78 import org.mockito.MockitoAnnotations
79 
80 @OptIn(ExperimentalCoroutinesApi::class)
81 @SmallTest
82 @RunWith(AndroidJUnit4::class)
83 class KeyguardQuickAffordanceInteractorTest : SysuiTestCase() {
84 
85     private val kosmos = testKosmos()
86     private val testScope = kosmos.testScope
87     private val settings = kosmos.fakeSettings
88 
89     @Mock private lateinit var lockPatternUtils: LockPatternUtils
90     @Mock private lateinit var keyguardStateController: KeyguardStateController
91     @Mock private lateinit var userTracker: UserTracker
92     @Mock private lateinit var activityStarter: ActivityStarter
93     @Mock private lateinit var launchAnimator: DialogTransitionAnimator
94     @Mock private lateinit var devicePolicyManager: DevicePolicyManager
95     @Mock private lateinit var shadeInteractor: ShadeInteractor
96     @Mock private lateinit var logger: KeyguardQuickAffordancesLogger
97     @Mock private lateinit var metricsLogger: KeyguardQuickAffordancesMetricsLogger
98 
99     private lateinit var underTest: KeyguardQuickAffordanceInteractor
100 
101     private lateinit var repository: FakeKeyguardRepository
102     private lateinit var homeControls: FakeKeyguardQuickAffordanceConfig
103     private lateinit var quickAccessWallet: FakeKeyguardQuickAffordanceConfig
104     private lateinit var qrCodeScanner: FakeKeyguardQuickAffordanceConfig
105     private lateinit var featureFlags: FakeFeatureFlags
106     private lateinit var dockManager: DockManagerFake
107     private lateinit var biometricSettingsRepository: FakeBiometricSettingsRepository
108 
109     @Before
setUpnull110     fun setUp() {
111         MockitoAnnotations.initMocks(this)
112 
113         overrideResource(R.bool.custom_lockscreen_shortcuts_enabled, true)
114         overrideResource(
115             R.array.config_keyguardQuickAffordanceDefaults,
116             arrayOf(
117                 KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START +
118                     ":" +
119                     BuiltInKeyguardQuickAffordanceKeys.HOME_CONTROLS,
120                 KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_END +
121                     ":" +
122                     BuiltInKeyguardQuickAffordanceKeys.QUICK_ACCESS_WALLET,
123             ),
124         )
125 
126         repository = FakeKeyguardRepository()
127         repository.setKeyguardShowing(true)
128 
129         homeControls =
130             FakeKeyguardQuickAffordanceConfig(BuiltInKeyguardQuickAffordanceKeys.HOME_CONTROLS)
131         quickAccessWallet =
132             FakeKeyguardQuickAffordanceConfig(
133                 BuiltInKeyguardQuickAffordanceKeys.QUICK_ACCESS_WALLET
134             )
135         qrCodeScanner =
136             FakeKeyguardQuickAffordanceConfig(BuiltInKeyguardQuickAffordanceKeys.QR_CODE_SCANNER)
137 
138         dockManager = DockManagerFake()
139         biometricSettingsRepository = FakeBiometricSettingsRepository()
140 
141         val localUserSelectionManager =
142             KeyguardQuickAffordanceLocalUserSelectionManager(
143                 context = context,
144                 userFileManager =
145                     mock<UserFileManager>().apply {
146                         whenever(getSharedPreferences(anyString(), anyInt(), anyInt()))
147                             .thenReturn(FakeSharedPreferences())
148                     },
149                 userTracker = userTracker,
150                 broadcastDispatcher = fakeBroadcastDispatcher,
151             )
152         val remoteUserSelectionManager =
153             KeyguardQuickAffordanceRemoteUserSelectionManager(
154                 scope = testScope.backgroundScope,
155                 userTracker = userTracker,
156                 clientFactory = FakeKeyguardQuickAffordanceProviderClientFactory(userTracker),
157                 userHandle = UserHandle.SYSTEM,
158             )
159         val quickAffordanceRepository =
160             KeyguardQuickAffordanceRepository(
161                 appContext = context,
162                 scope = testScope.backgroundScope,
163                 localUserSelectionManager = localUserSelectionManager,
164                 remoteUserSelectionManager = remoteUserSelectionManager,
165                 userTracker = userTracker,
166                 legacySettingSyncer =
167                     KeyguardQuickAffordanceLegacySettingSyncer(
168                         scope = testScope.backgroundScope,
169                         backgroundDispatcher = kosmos.testDispatcher,
170                         secureSettings = settings,
171                         selectionsManager = localUserSelectionManager,
172                     ),
173                 configs = setOf(homeControls, quickAccessWallet, qrCodeScanner),
174                 dumpManager = mock(),
175                 userHandle = UserHandle.SYSTEM,
176             )
177         featureFlags = FakeFeatureFlags()
178 
179         val withDeps =
180             KeyguardInteractorFactory.create(featureFlags = featureFlags, repository = repository)
181         underTest =
182             KeyguardQuickAffordanceInteractor(
183                 keyguardInteractor = withDeps.keyguardInteractor,
184                 shadeInteractor = shadeInteractor,
185                 lockPatternUtils = lockPatternUtils,
186                 keyguardStateController = keyguardStateController,
187                 userTracker = userTracker,
188                 activityStarter = activityStarter,
189                 featureFlags = featureFlags,
190                 repository = { quickAffordanceRepository },
191                 launchAnimator = launchAnimator,
192                 logger = logger,
193                 metricsLogger = metricsLogger,
194                 devicePolicyManager = devicePolicyManager,
195                 dockManager = dockManager,
196                 biometricSettingsRepository = biometricSettingsRepository,
197                 backgroundDispatcher = kosmos.testDispatcher,
198                 appContext = context,
199                 sceneInteractor = { kosmos.sceneInteractor },
200             )
201         kosmos.keyguardQuickAffordanceInteractor = underTest
202 
203         whenever(shadeInteractor.anyExpansion).thenReturn(MutableStateFlow(0f))
204     }
205 
206     @Test
quickAffordance_bottomStartAffordanceIsVisiblenull207     fun quickAffordance_bottomStartAffordanceIsVisible() =
208         testScope.runTest {
209             val configKey = BuiltInKeyguardQuickAffordanceKeys.HOME_CONTROLS
210             homeControls.setState(
211                 KeyguardQuickAffordanceConfig.LockScreenState.Visible(
212                     icon = ICON,
213                     activationState = ActivationState.Active,
214                 )
215             )
216 
217             val collectedValue =
218                 collectLastValue(
219                     underTest.quickAffordance(KeyguardQuickAffordancePosition.BOTTOM_START)
220                 )
221 
222             assertThat(collectedValue())
223                 .isInstanceOf(KeyguardQuickAffordanceModel.Visible::class.java)
224             val visibleModel = collectedValue() as KeyguardQuickAffordanceModel.Visible
225             assertThat(visibleModel.configKey)
226                 .isEqualTo("${KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START}::$configKey")
227             assertThat(visibleModel.icon).isEqualTo(ICON)
228             assertThat(visibleModel.icon.contentDescription)
229                 .isEqualTo(ContentDescription.Resource(res = CONTENT_DESCRIPTION_RESOURCE_ID))
230             assertThat(visibleModel.activationState).isEqualTo(ActivationState.Active)
231         }
232 
233     @Test
quickAffordance_bottomEndAffordanceIsVisiblenull234     fun quickAffordance_bottomEndAffordanceIsVisible() =
235         testScope.runTest {
236             val configKey = BuiltInKeyguardQuickAffordanceKeys.QUICK_ACCESS_WALLET
237             quickAccessWallet.setState(
238                 KeyguardQuickAffordanceConfig.LockScreenState.Visible(icon = ICON)
239             )
240 
241             val collectedValue =
242                 collectLastValue(
243                     underTest.quickAffordance(KeyguardQuickAffordancePosition.BOTTOM_END)
244                 )
245 
246             assertThat(collectedValue())
247                 .isInstanceOf(KeyguardQuickAffordanceModel.Visible::class.java)
248             val visibleModel = collectedValue() as KeyguardQuickAffordanceModel.Visible
249             assertThat(visibleModel.configKey)
250                 .isEqualTo("${KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_END}::$configKey")
251             assertThat(visibleModel.icon).isEqualTo(ICON)
252             assertThat(visibleModel.icon.contentDescription)
253                 .isEqualTo(ContentDescription.Resource(res = CONTENT_DESCRIPTION_RESOURCE_ID))
254             assertThat(visibleModel.activationState).isEqualTo(ActivationState.NotSupported)
255         }
256 
257     @Test
quickAffordance_hiddenWhenAllFeaturesAreDisabledByDevicePolicynull258     fun quickAffordance_hiddenWhenAllFeaturesAreDisabledByDevicePolicy() =
259         testScope.runTest {
260             whenever(devicePolicyManager.getKeyguardDisabledFeatures(null, userTracker.userId))
261                 .thenReturn(DevicePolicyManager.KEYGUARD_DISABLE_FEATURES_ALL)
262             quickAccessWallet.setState(
263                 KeyguardQuickAffordanceConfig.LockScreenState.Visible(icon = ICON)
264             )
265 
266             val collectedValue by
267                 collectLastValue(
268                     underTest.quickAffordance(KeyguardQuickAffordancePosition.BOTTOM_END)
269                 )
270 
271             assertThat(collectedValue).isInstanceOf(KeyguardQuickAffordanceModel.Hidden::class.java)
272         }
273 
274     @Test
quickAffordance_hiddenWhenShortcutsFeatureIsDisabledByDevicePolicynull275     fun quickAffordance_hiddenWhenShortcutsFeatureIsDisabledByDevicePolicy() =
276         testScope.runTest {
277             whenever(devicePolicyManager.getKeyguardDisabledFeatures(null, userTracker.userId))
278                 .thenReturn(DevicePolicyManager.KEYGUARD_DISABLE_SHORTCUTS_ALL)
279             quickAccessWallet.setState(
280                 KeyguardQuickAffordanceConfig.LockScreenState.Visible(icon = ICON)
281             )
282 
283             val collectedValue by
284                 collectLastValue(
285                     underTest.quickAffordance(KeyguardQuickAffordancePosition.BOTTOM_END)
286                 )
287 
288             assertThat(collectedValue).isInstanceOf(KeyguardQuickAffordanceModel.Hidden::class.java)
289         }
290 
291     @Test
quickAffordance_hiddenWhenUserIsInLockdownModenull292     fun quickAffordance_hiddenWhenUserIsInLockdownMode() =
293         testScope.runTest {
294             biometricSettingsRepository.setIsUserInLockdown(true)
295             quickAccessWallet.setState(
296                 KeyguardQuickAffordanceConfig.LockScreenState.Visible(icon = ICON)
297             )
298 
299             val collectedValue by
300                 collectLastValue(
301                     underTest.quickAffordance(KeyguardQuickAffordancePosition.BOTTOM_END)
302                 )
303 
304             assertThat(collectedValue).isEqualTo(KeyguardQuickAffordanceModel.Hidden)
305         }
306 
307     @Test
quickAffordance_bottomStartAffordanceHiddenWhileDozingnull308     fun quickAffordance_bottomStartAffordanceHiddenWhileDozing() =
309         testScope.runTest {
310             repository.setIsDozing(true)
311             homeControls.setState(
312                 KeyguardQuickAffordanceConfig.LockScreenState.Visible(icon = ICON)
313             )
314 
315             val collectedValue =
316                 collectLastValue(
317                     underTest.quickAffordance(KeyguardQuickAffordancePosition.BOTTOM_START)
318                 )
319             assertThat(collectedValue()).isEqualTo(KeyguardQuickAffordanceModel.Hidden)
320         }
321 
322     @Test
quickAffordance_bottomStartAffordanceHiddenWhenLockscreenIsNotShowingnull323     fun quickAffordance_bottomStartAffordanceHiddenWhenLockscreenIsNotShowing() =
324         testScope.runTest {
325             repository.setKeyguardShowing(false)
326             homeControls.setState(
327                 KeyguardQuickAffordanceConfig.LockScreenState.Visible(icon = ICON)
328             )
329 
330             val collectedValue =
331                 collectLastValue(
332                     underTest.quickAffordance(KeyguardQuickAffordancePosition.BOTTOM_START)
333                 )
334             assertThat(collectedValue()).isEqualTo(KeyguardQuickAffordanceModel.Hidden)
335         }
336 
337     @Test
quickAffordance_updateOncePerShadeExpansionnull338     fun quickAffordance_updateOncePerShadeExpansion() =
339         testScope.runTest {
340             val shadeExpansion = MutableStateFlow(0f)
341             whenever(shadeInteractor.anyExpansion).thenReturn(shadeExpansion)
342 
343             val collectedValue by
344                 collectValues(
345                     underTest.quickAffordance(KeyguardQuickAffordancePosition.BOTTOM_START)
346                 )
347 
348             val initialSize = collectedValue.size
349             for (i in 0..10) {
350                 shadeExpansion.value = i / 10f
351             }
352 
353             assertThat(collectedValue.size).isEqualTo(initialSize + 1)
354         }
355 
356     @Test
quickAffordanceAlwaysVisible_notVisible_restrictedByPolicyManagernull357     fun quickAffordanceAlwaysVisible_notVisible_restrictedByPolicyManager() =
358         testScope.runTest {
359             whenever(devicePolicyManager.getKeyguardDisabledFeatures(null, userTracker.userId))
360                 .thenReturn(DevicePolicyManager.KEYGUARD_DISABLE_SHORTCUTS_ALL)
361 
362             repository.setKeyguardShowing(false)
363             repository.setIsDozing(true)
364             homeControls.setState(
365                 KeyguardQuickAffordanceConfig.LockScreenState.Visible(
366                     icon = ICON,
367                     activationState = ActivationState.Active,
368                 )
369             )
370 
371             val collectedValue by
372                 collectLastValue(
373                     underTest.quickAffordanceAlwaysVisible(
374                         KeyguardQuickAffordancePosition.BOTTOM_START
375                     )
376                 )
377 
378             assertThat(collectedValue).isInstanceOf(KeyguardQuickAffordanceModel.Hidden::class.java)
379         }
380 
381     @Test
quickAffordanceAlwaysVisible_evenWhenLockScreenNotShowingAndDozingnull382     fun quickAffordanceAlwaysVisible_evenWhenLockScreenNotShowingAndDozing() =
383         testScope.runTest {
384             repository.setKeyguardShowing(false)
385             repository.setIsDozing(true)
386             val configKey = BuiltInKeyguardQuickAffordanceKeys.HOME_CONTROLS
387             homeControls.setState(
388                 KeyguardQuickAffordanceConfig.LockScreenState.Visible(
389                     icon = ICON,
390                     activationState = ActivationState.Active,
391                 )
392             )
393 
394             val collectedValue =
395                 collectLastValue(
396                     underTest.quickAffordanceAlwaysVisible(
397                         KeyguardQuickAffordancePosition.BOTTOM_START
398                     )
399                 )
400             assertThat(collectedValue())
401                 .isInstanceOf(KeyguardQuickAffordanceModel.Visible::class.java)
402             val visibleModel = collectedValue() as KeyguardQuickAffordanceModel.Visible
403             assertThat(visibleModel.configKey)
404                 .isEqualTo("${KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START}::$configKey")
405             assertThat(visibleModel.icon).isEqualTo(ICON)
406             assertThat(visibleModel.icon.contentDescription)
407                 .isEqualTo(ContentDescription.Resource(res = CONTENT_DESCRIPTION_RESOURCE_ID))
408             assertThat(visibleModel.activationState).isEqualTo(ActivationState.Active)
409         }
410 
411     @Test
quickAffordanceAlwaysVisible_withNonNullOverrideKeyguardQuickAffordanceIdnull412     fun quickAffordanceAlwaysVisible_withNonNullOverrideKeyguardQuickAffordanceId() =
413         testScope.runTest {
414             quickAccessWallet.setState(
415                 KeyguardQuickAffordanceConfig.LockScreenState.Visible(
416                     icon = ICON,
417                     activationState = ActivationState.Active,
418                 )
419             )
420             homeControls.setState(
421                 KeyguardQuickAffordanceConfig.LockScreenState.Visible(
422                     icon = ICON,
423                     activationState = ActivationState.Active,
424                 )
425             )
426 
427             // The default case
428             val collectedValue =
429                 collectLastValue(
430                     underTest.quickAffordanceAlwaysVisible(
431                         KeyguardQuickAffordancePosition.BOTTOM_START
432                     )
433                 )
434             assertThat(collectedValue())
435                 .isInstanceOf(KeyguardQuickAffordanceModel.Visible::class.java)
436             val visibleModel = collectedValue() as KeyguardQuickAffordanceModel.Visible
437             assertThat(visibleModel.configKey)
438                 .isEqualTo(
439                     "${KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START}::${homeControls.key}"
440                 )
441             assertThat(visibleModel.icon).isEqualTo(ICON)
442             assertThat(visibleModel.icon.contentDescription)
443                 .isEqualTo(ContentDescription.Resource(res = CONTENT_DESCRIPTION_RESOURCE_ID))
444             assertThat(visibleModel.activationState).isEqualTo(ActivationState.Active)
445 
446             // With override
447             val collectedValueWithOverride =
448                 collectLastValue(
449                     underTest.quickAffordanceAlwaysVisible(
450                         position = KeyguardQuickAffordancePosition.BOTTOM_START,
451                         overrideQuickAffordanceId =
452                             BuiltInKeyguardQuickAffordanceKeys.QUICK_ACCESS_WALLET,
453                     )
454                 )
455             assertThat(collectedValueWithOverride())
456                 .isInstanceOf(KeyguardQuickAffordanceModel.Visible::class.java)
457             val visibleModelWithOverride =
458                 collectedValueWithOverride() as KeyguardQuickAffordanceModel.Visible
459             assertThat(visibleModelWithOverride.configKey)
460                 .isEqualTo(
461                     "${KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START}::${quickAccessWallet.key}"
462                 )
463             assertThat(visibleModelWithOverride.icon).isEqualTo(ICON)
464             assertThat(visibleModelWithOverride.icon.contentDescription)
465                 .isEqualTo(ContentDescription.Resource(res = CONTENT_DESCRIPTION_RESOURCE_ID))
466             assertThat(visibleModelWithOverride.activationState).isEqualTo(ActivationState.Active)
467         }
468 
469     @Test
selectnull470     fun select() =
471         testScope.runTest {
472             overrideResource(R.array.config_keyguardQuickAffordanceDefaults, arrayOf<String>())
473             homeControls.setState(
474                 KeyguardQuickAffordanceConfig.LockScreenState.Visible(icon = ICON)
475             )
476             quickAccessWallet.setState(
477                 KeyguardQuickAffordanceConfig.LockScreenState.Visible(icon = ICON)
478             )
479             qrCodeScanner.setState(
480                 KeyguardQuickAffordanceConfig.LockScreenState.Visible(icon = ICON)
481             )
482 
483             assertThat(underTest.getSelections())
484                 .isEqualTo(
485                     mapOf<String, List<String>>(
486                         KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START to emptyList(),
487                         KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_END to emptyList(),
488                     )
489                 )
490 
491             val startConfig =
492                 collectLastValue(
493                     underTest.quickAffordance(KeyguardQuickAffordancePosition.BOTTOM_START)
494                 )
495             val endConfig =
496                 collectLastValue(
497                     underTest.quickAffordance(KeyguardQuickAffordancePosition.BOTTOM_END)
498                 )
499 
500             underTest.select(KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START, homeControls.key)
501 
502             assertThat(startConfig())
503                 .isEqualTo(
504                     KeyguardQuickAffordanceModel.Visible(
505                         configKey =
506                             KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START +
507                                 "::${homeControls.key}",
508                         icon = ICON,
509                         activationState = ActivationState.NotSupported,
510                     )
511                 )
512             assertThat(endConfig()).isEqualTo(KeyguardQuickAffordanceModel.Hidden)
513             assertThat(underTest.getSelections())
514                 .isEqualTo(
515                     mapOf(
516                         KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START to
517                             listOf(
518                                 KeyguardQuickAffordancePickerRepresentation(
519                                     id = homeControls.key,
520                                     name = homeControls.pickerName(),
521                                     iconResourceId = homeControls.pickerIconResourceId,
522                                 )
523                             ),
524                         KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_END to emptyList(),
525                     )
526                 )
527 
528             underTest.select(
529                 KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START,
530                 quickAccessWallet.key,
531             )
532 
533             assertThat(startConfig())
534                 .isEqualTo(
535                     KeyguardQuickAffordanceModel.Visible(
536                         configKey =
537                             KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START +
538                                 "::${quickAccessWallet.key}",
539                         icon = ICON,
540                         activationState = ActivationState.NotSupported,
541                     )
542                 )
543             assertThat(endConfig()).isEqualTo(KeyguardQuickAffordanceModel.Hidden)
544             assertThat(underTest.getSelections())
545                 .isEqualTo(
546                     mapOf(
547                         KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START to
548                             listOf(
549                                 KeyguardQuickAffordancePickerRepresentation(
550                                     id = quickAccessWallet.key,
551                                     name = quickAccessWallet.pickerName(),
552                                     iconResourceId = quickAccessWallet.pickerIconResourceId,
553                                 )
554                             ),
555                         KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_END to emptyList(),
556                     )
557                 )
558 
559             underTest.select(KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_END, qrCodeScanner.key)
560 
561             assertThat(startConfig())
562                 .isEqualTo(
563                     KeyguardQuickAffordanceModel.Visible(
564                         configKey =
565                             KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START +
566                                 "::${quickAccessWallet.key}",
567                         icon = ICON,
568                         activationState = ActivationState.NotSupported,
569                     )
570                 )
571             assertThat(endConfig())
572                 .isEqualTo(
573                     KeyguardQuickAffordanceModel.Visible(
574                         configKey =
575                             KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_END +
576                                 "::${qrCodeScanner.key}",
577                         icon = ICON,
578                         activationState = ActivationState.NotSupported,
579                     )
580                 )
581             assertThat(underTest.getSelections())
582                 .isEqualTo(
583                     mapOf(
584                         KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START to
585                             listOf(
586                                 KeyguardQuickAffordancePickerRepresentation(
587                                     id = quickAccessWallet.key,
588                                     name = quickAccessWallet.pickerName(),
589                                     iconResourceId = quickAccessWallet.pickerIconResourceId,
590                                 )
591                             ),
592                         KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_END to
593                             listOf(
594                                 KeyguardQuickAffordancePickerRepresentation(
595                                     id = qrCodeScanner.key,
596                                     name = qrCodeScanner.pickerName(),
597                                     iconResourceId = qrCodeScanner.pickerIconResourceId,
598                                 )
599                             ),
600                     )
601                 )
602         }
603 
604     @Test
unselect_onenull605     fun unselect_one() =
606         testScope.runTest {
607             homeControls.setState(
608                 KeyguardQuickAffordanceConfig.LockScreenState.Visible(icon = ICON)
609             )
610             quickAccessWallet.setState(
611                 KeyguardQuickAffordanceConfig.LockScreenState.Visible(icon = ICON)
612             )
613             qrCodeScanner.setState(
614                 KeyguardQuickAffordanceConfig.LockScreenState.Visible(icon = ICON)
615             )
616 
617             val startConfig =
618                 collectLastValue(
619                     underTest.quickAffordance(KeyguardQuickAffordancePosition.BOTTOM_START)
620                 )
621             val endConfig =
622                 collectLastValue(
623                     underTest.quickAffordance(KeyguardQuickAffordancePosition.BOTTOM_END)
624                 )
625             underTest.select(KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START, homeControls.key)
626             underTest.select(KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_END, quickAccessWallet.key)
627             underTest.unselect(KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START, homeControls.key)
628 
629             assertThat(startConfig()).isEqualTo(KeyguardQuickAffordanceModel.Hidden)
630             assertThat(endConfig())
631                 .isEqualTo(
632                     KeyguardQuickAffordanceModel.Visible(
633                         configKey =
634                             KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_END +
635                                 "::${quickAccessWallet.key}",
636                         icon = ICON,
637                         activationState = ActivationState.NotSupported,
638                     )
639                 )
640             assertThat(underTest.getSelections())
641                 .isEqualTo(
642                     mapOf(
643                         KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START to emptyList(),
644                         KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_END to
645                             listOf(
646                                 KeyguardQuickAffordancePickerRepresentation(
647                                     id = quickAccessWallet.key,
648                                     name = quickAccessWallet.pickerName(),
649                                     iconResourceId = quickAccessWallet.pickerIconResourceId,
650                                 )
651                             ),
652                     )
653                 )
654 
655             underTest.unselect(
656                 KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_END,
657                 quickAccessWallet.key,
658             )
659 
660             assertThat(startConfig()).isEqualTo(KeyguardQuickAffordanceModel.Hidden)
661             assertThat(endConfig()).isEqualTo(KeyguardQuickAffordanceModel.Hidden)
662             assertThat(underTest.getSelections())
663                 .isEqualTo(
664                     mapOf<String, List<String>>(
665                         KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START to emptyList(),
666                         KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_END to emptyList(),
667                     )
668                 )
669         }
670 
671     @Test
useLongPress_whenDocked_isFalsenull672     fun useLongPress_whenDocked_isFalse() =
673         testScope.runTest {
674             dockManager.setIsDocked(true)
675 
676             val useLongPress by collectLastValue(underTest.useLongPress())
677 
678             assertThat(useLongPress).isFalse()
679         }
680 
681     @Test
useLongPress_whenNotDocked_isTruenull682     fun useLongPress_whenNotDocked_isTrue() =
683         testScope.runTest {
684             dockManager.setIsDocked(false)
685 
686             val useLongPress by collectLastValue(underTest.useLongPress())
687 
688             assertThat(useLongPress).isTrue()
689         }
690 
691     @Test
useLongPress_whenNotDocked_isTrue_changedTo_whenDocked_isFalsenull692     fun useLongPress_whenNotDocked_isTrue_changedTo_whenDocked_isFalse() =
693         testScope.runTest {
694             dockManager.setIsDocked(false)
695             val firstUseLongPress by collectLastValue(underTest.useLongPress())
696             runCurrent()
697 
698             assertThat(firstUseLongPress).isTrue()
699 
700             dockManager.setIsDocked(true)
701             dockManager.setDockEvent(DockManager.STATE_DOCKED)
702             val secondUseLongPress by collectLastValue(underTest.useLongPress())
703             runCurrent()
704 
705             assertThat(secondUseLongPress).isFalse()
706         }
707 
708     @Test
unselect_allnull709     fun unselect_all() =
710         testScope.runTest {
711             homeControls.setState(
712                 KeyguardQuickAffordanceConfig.LockScreenState.Visible(icon = ICON)
713             )
714             quickAccessWallet.setState(
715                 KeyguardQuickAffordanceConfig.LockScreenState.Visible(icon = ICON)
716             )
717             qrCodeScanner.setState(
718                 KeyguardQuickAffordanceConfig.LockScreenState.Visible(icon = ICON)
719             )
720 
721             underTest.select(KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START, homeControls.key)
722             underTest.select(KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_END, quickAccessWallet.key)
723             underTest.unselect(KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START, null)
724 
725             assertThat(underTest.getSelections())
726                 .isEqualTo(
727                     mapOf(
728                         KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START to emptyList(),
729                         KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_END to
730                             listOf(
731                                 KeyguardQuickAffordancePickerRepresentation(
732                                     id = quickAccessWallet.key,
733                                     name = quickAccessWallet.pickerName(),
734                                     iconResourceId = quickAccessWallet.pickerIconResourceId,
735                                 )
736                             ),
737                     )
738                 )
739 
740             underTest.unselect(KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_END, null)
741 
742             assertThat(underTest.getSelections())
743                 .isEqualTo(
744                     mapOf<String, List<String>>(
745                         KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START to emptyList(),
746                         KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_END to emptyList(),
747                     )
748                 )
749         }
750 
751     @EnableSceneContainer
752     @Test
updatesLaunchingAffordanceFromCameraLaunchernull753     fun updatesLaunchingAffordanceFromCameraLauncher() =
754         testScope.runTest {
755             val launchingAffordance by collectLastValue(underTest.launchingAffordance)
756             runCurrent()
757 
758             kosmos.cameraLauncher.setLaunchingAffordance(true)
759             runCurrent()
760             assertThat(launchingAffordance).isTrue()
761 
762             kosmos.cameraLauncher.setLaunchingAffordance(false)
763             runCurrent()
764             assertThat(launchingAffordance).isFalse()
765         }
766 
767     companion object {
768         private const val CONTENT_DESCRIPTION_RESOURCE_ID = 1337
769         private val ICON: Icon =
770             Icon.Resource(
771                 res = CONTENT_DESCRIPTION_RESOURCE_ID,
772                 contentDescription =
773                     ContentDescription.Resource(res = CONTENT_DESCRIPTION_RESOURCE_ID),
774             )
775     }
776 }
777