diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/keyguard/shared/model/KeyguardQuickAffordanceSlots.kt b/packages/SystemUI/shared/src/com/android/systemui/shared/keyguard/shared/model/KeyguardQuickAffordanceSlots.kt new file mode 100644 index 0000000000000..2dc7a280e4238 --- /dev/null +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/keyguard/shared/model/KeyguardQuickAffordanceSlots.kt @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package com.android.systemui.shared.keyguard.shared.model + +/** + * Collection of all supported "slots", placements where keyguard quick affordances can appear on + * the lock screen. + */ +object KeyguardQuickAffordanceSlots { + const val SLOT_ID_BOTTOM_START = "bottom_start" + const val SLOT_ID_BOTTOM_END = "bottom_end" +} diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java b/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java index 56a1f1ae936e5..f08463bfd5e9c 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java @@ -42,6 +42,7 @@ import com.android.systemui.dump.DumpManager; import com.android.systemui.keyguard.DismissCallbackRegistry; import com.android.systemui.keyguard.KeyguardUnlockAnimationController; import com.android.systemui.keyguard.KeyguardViewMediator; +import com.android.systemui.keyguard.data.quickaffordance.KeyguardDataQuickAffordanceModule; import com.android.systemui.keyguard.data.repository.KeyguardRepositoryModule; import com.android.systemui.keyguard.domain.interactor.StartKeyguardTransitionModule; import com.android.systemui.keyguard.domain.quickaffordance.KeyguardQuickAffordanceModule; @@ -71,6 +72,7 @@ import dagger.Provides; KeyguardUserSwitcherComponent.class}, includes = { FalsingModule.class, + KeyguardDataQuickAffordanceModule.class, KeyguardQuickAffordanceModule.class, KeyguardRepositoryModule.class, StartKeyguardTransitionModule.class, diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/HomeControlsKeyguardQuickAffordanceConfig.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/HomeControlsKeyguardQuickAffordanceConfig.kt index c600e13cc2ddb..d6f521c6dc87d 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/HomeControlsKeyguardQuickAffordanceConfig.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/HomeControlsKeyguardQuickAffordanceConfig.kt @@ -53,6 +53,10 @@ constructor( override val key: String = BuiltInKeyguardQuickAffordanceKeys.HOME_CONTROLS + override val pickerName: String by lazy { context.getString(component.getTileTitleId()) } + + override val pickerIconResourceId: Int by lazy { component.getTileImageId() } + override val lockScreenState: Flow = component.canShowWhileLockedSetting.flatMapLatest { canShowWhileLocked -> if (canShowWhileLocked) { diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardDataQuickAffordanceModule.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardDataQuickAffordanceModule.kt new file mode 100644 index 0000000000000..bea9363efc81e --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardDataQuickAffordanceModule.kt @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package com.android.systemui.keyguard.data.quickaffordance + +import dagger.Module +import dagger.Provides +import dagger.multibindings.ElementsIntoSet + +@Module +object KeyguardDataQuickAffordanceModule { + @Provides + @ElementsIntoSet + fun quickAffordanceConfigs( + home: HomeControlsKeyguardQuickAffordanceConfig, + quickAccessWallet: QuickAccessWalletKeyguardQuickAffordanceConfig, + qrCodeScanner: QrCodeScannerKeyguardQuickAffordanceConfig, + ): Set { + return setOf( + home, + quickAccessWallet, + qrCodeScanner, + ) + } +} diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceConfig.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceConfig.kt index 0a8090bb11ac3..fd40d1dd1a0eb 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceConfig.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceConfig.kt @@ -29,6 +29,10 @@ interface KeyguardQuickAffordanceConfig { /** Unique identifier for this quick affordance. It must be globally unique. */ val key: String + val pickerName: String + + val pickerIconResourceId: Int + /** * The ever-changing state of the affordance. * diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceSelectionManager.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceSelectionManager.kt new file mode 100644 index 0000000000000..9c9354fec6950 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceSelectionManager.kt @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package com.android.systemui.keyguard.data.quickaffordance + +import com.android.systemui.dagger.SysUISingleton +import javax.inject.Inject +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.asStateFlow + +/** + * Manages and provides access to the current "selections" of keyguard quick affordances, answering + * the question "which affordances should the keyguard show?". + */ +@SysUISingleton +class KeyguardQuickAffordanceSelectionManager @Inject constructor() { + + // TODO(b/254858695): implement a persistence layer (database). + private val _selections = MutableStateFlow>>(emptyMap()) + + /** IDs of affordances to show, indexed by slot ID, and sorted in descending priority order. */ + val selections: Flow>> = _selections.asStateFlow() + + /** + * Returns a snapshot of the IDs of affordances to show, indexed by slot ID, and sorted in + * descending priority order. + */ + suspend fun getSelections(): Map> { + return _selections.value + } + + /** + * Updates the IDs of affordances to show at the slot with the given ID. The order of affordance + * IDs should be descending priority order. + */ + suspend fun setSelections( + slotId: String, + affordanceIds: List, + ) { + // Must make a copy of the map and update it, otherwise, the MutableStateFlow won't emit + // when we set its value to the same instance of the original map, even if we change the + // map by updating the value of one of its keys. + val copy = _selections.value.toMutableMap() + copy[slotId] = affordanceIds + _selections.value = copy + } +} diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/QrCodeScannerKeyguardQuickAffordanceConfig.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/QrCodeScannerKeyguardQuickAffordanceConfig.kt index d620b2a1654cd..11f72ffa47579 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/QrCodeScannerKeyguardQuickAffordanceConfig.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/QrCodeScannerKeyguardQuickAffordanceConfig.kt @@ -17,6 +17,7 @@ package com.android.systemui.keyguard.data.quickaffordance +import android.content.Context import com.android.systemui.R import com.android.systemui.animation.Expandable import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging @@ -24,6 +25,7 @@ import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCall import com.android.systemui.common.shared.model.ContentDescription import com.android.systemui.common.shared.model.Icon import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.qrcodescanner.controller.QRCodeScannerController import javax.inject.Inject import kotlinx.coroutines.channels.awaitClose @@ -34,11 +36,16 @@ import kotlinx.coroutines.flow.Flow class QrCodeScannerKeyguardQuickAffordanceConfig @Inject constructor( + @Application context: Context, private val controller: QRCodeScannerController, ) : KeyguardQuickAffordanceConfig { override val key: String = BuiltInKeyguardQuickAffordanceKeys.QR_CODE_SCANNER + override val pickerName = context.getString(R.string.qr_code_scanner_title) + + override val pickerIconResourceId = R.drawable.ic_qr_code_scanner + override val lockScreenState: Flow = conflatedCallbackFlow { val callback = diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfig.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfig.kt index be57a323b5d1b..303e6a1a95cc9 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfig.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfig.kt @@ -17,6 +17,7 @@ package com.android.systemui.keyguard.data.quickaffordance +import android.content.Context import android.graphics.drawable.Drawable import android.service.quickaccesswallet.GetWalletCardsError import android.service.quickaccesswallet.GetWalletCardsResponse @@ -29,6 +30,7 @@ import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCall import com.android.systemui.common.shared.model.ContentDescription import com.android.systemui.common.shared.model.Icon import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.plugins.ActivityStarter import com.android.systemui.wallet.controller.QuickAccessWalletController import javax.inject.Inject @@ -40,12 +42,17 @@ import kotlinx.coroutines.flow.Flow class QuickAccessWalletKeyguardQuickAffordanceConfig @Inject constructor( + @Application context: Context, private val walletController: QuickAccessWalletController, private val activityStarter: ActivityStarter, ) : KeyguardQuickAffordanceConfig { override val key: String = BuiltInKeyguardQuickAffordanceKeys.QUICK_ACCESS_WALLET + override val pickerName = context.getString(R.string.accessibility_wallet_button) + + override val pickerIconResourceId = R.drawable.ic_wallet_lockscreen + override val lockScreenState: Flow = conflatedCallbackFlow { val callback = diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardQuickAffordanceRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardQuickAffordanceRepository.kt new file mode 100644 index 0000000000000..95f614fbf7b19 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardQuickAffordanceRepository.kt @@ -0,0 +1,128 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package com.android.systemui.keyguard.data.repository + +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.dagger.qualifiers.Application +import com.android.systemui.dagger.qualifiers.Background +import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceConfig +import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceSelectionManager +import com.android.systemui.keyguard.shared.model.KeyguardQuickAffordancePickerRepresentation +import com.android.systemui.keyguard.shared.model.KeyguardSlotPickerRepresentation +import com.android.systemui.shared.keyguard.shared.model.KeyguardQuickAffordanceSlots +import javax.inject.Inject +import kotlinx.coroutines.CoroutineDispatcher +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.flow.SharingStarted +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.stateIn +import kotlinx.coroutines.launch + +/** Abstracts access to application state related to keyguard quick affordances. */ +@SysUISingleton +class KeyguardQuickAffordanceRepository +@Inject +constructor( + @Application private val scope: CoroutineScope, + @Background private val backgroundDispatcher: CoroutineDispatcher, + private val selectionManager: KeyguardQuickAffordanceSelectionManager, + private val configs: Set<@JvmSuppressWildcards KeyguardQuickAffordanceConfig>, +) { + /** + * List of [KeyguardQuickAffordanceConfig] instances of the affordances at the slot with the + * given ID. The configs are sorted in descending priority order. + */ + val selections: StateFlow>> = + selectionManager.selections + .map { selectionsBySlotId -> + selectionsBySlotId.mapValues { (_, selections) -> + configs.filter { selections.contains(it.key) } + } + } + .stateIn( + scope = scope, + started = SharingStarted.Eagerly, + initialValue = emptyMap(), + ) + + /** + * Returns a snapshot of the [KeyguardQuickAffordanceConfig] instances of the affordances at the + * slot with the given ID. The configs are sorted in descending priority order. + */ + suspend fun getSelections(slotId: String): List { + val selections = selectionManager.getSelections().getOrDefault(slotId, emptyList()) + return configs.filter { selections.contains(it.key) } + } + + /** + * Returns a snapshot of the IDs of the selected affordances, indexed by slot ID. The configs + * are sorted in descending priority order. + */ + suspend fun getSelections(): Map> { + return selectionManager.getSelections() + } + + /** + * Updates the IDs of affordances to show at the slot with the given ID. The order of affordance + * IDs should be descending priority order. + */ + fun setSelections( + slotId: String, + affordanceIds: List, + ) { + scope.launch(backgroundDispatcher) { + selectionManager.setSelections( + slotId = slotId, + affordanceIds = affordanceIds, + ) + } + } + + /** + * Returns the list of representation objects for all known affordances, regardless of what is + * selected. This is useful for building experiences like the picker/selector or user settings + * so the user can see everything that can be selected in a menu. + */ + fun getAffordancePickerRepresentations(): List { + return configs.map { config -> + KeyguardQuickAffordancePickerRepresentation( + id = config.key, + name = config.pickerName, + iconResourceId = config.pickerIconResourceId, + ) + } + } + + /** + * Returns the list of representation objects for all available slots on the keyguard. This is + * useful for building experiences like the picker/selector or user settings so the user can see + * each slot and select which affordance(s) is/are installed in each slot on the keyguard. + */ + fun getSlotPickerRepresentations(): List { + // TODO(b/256195304): source these from a config XML file. + return listOf( + KeyguardSlotPickerRepresentation( + id = KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START, + ), + KeyguardSlotPickerRepresentation( + id = KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_END, + ), + ) + } +} diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/KeyguardQuickAffordancePickerRepresentation.kt b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/KeyguardQuickAffordancePickerRepresentation.kt new file mode 100644 index 0000000000000..a56bc900f9364 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/KeyguardQuickAffordancePickerRepresentation.kt @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package com.android.systemui.keyguard.shared.model + +import androidx.annotation.DrawableRes + +/** + * Representation of a quick affordance for use to build "picker", "selector", or "settings" + * experiences. + */ +data class KeyguardQuickAffordancePickerRepresentation( + val id: String, + val name: String, + @DrawableRes val iconResourceId: Int, +) diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/KeyguardSlotPickerRepresentation.kt b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/KeyguardSlotPickerRepresentation.kt new file mode 100644 index 0000000000000..86f27567ddf85 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/KeyguardSlotPickerRepresentation.kt @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package com.android.systemui.keyguard.shared.model + +/** + * Representation of a quick affordance slot (or position) for use to build "picker", "selector", or + * "settings" experiences. + */ +data class KeyguardSlotPickerRepresentation( + val id: String, + /** The maximum number of selected affordances that can be present on this slot. */ + val maxSelectedAffordances: Int = 1, +) diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/FakeKeyguardQuickAffordanceConfig.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/FakeKeyguardQuickAffordanceConfig.kt index f18acbad14f28..0fb181d414842 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/FakeKeyguardQuickAffordanceConfig.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/FakeKeyguardQuickAffordanceConfig.kt @@ -23,14 +23,11 @@ import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.yield -/** - * Fake implementation of a quick affordance data source. - * - * This class is abstract to force tests to provide extensions of it as the system that references - * these configs uses each implementation's class type to refer to them. - */ -abstract class FakeKeyguardQuickAffordanceConfig( +/** Fake implementation of a quick affordance data source. */ +class FakeKeyguardQuickAffordanceConfig( override val key: String, + override val pickerName: String = key, + override val pickerIconResourceId: Int = 0, ) : KeyguardQuickAffordanceConfig { var onTriggeredResult: OnTriggeredResult = OnTriggeredResult.Handled diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceSelectionManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceSelectionManagerTest.kt new file mode 100644 index 0000000000000..d2422ad7b53b3 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceSelectionManagerTest.kt @@ -0,0 +1,127 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package com.android.systemui.keyguard.data.quickaffordance + +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.google.common.truth.Truth.assertThat +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.onEach +import kotlinx.coroutines.runBlocking +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.JUnit4 + +@SmallTest +@RunWith(JUnit4::class) +class KeyguardQuickAffordanceSelectionManagerTest : SysuiTestCase() { + + private lateinit var underTest: KeyguardQuickAffordanceSelectionManager + + @Before + fun setUp() { + underTest = KeyguardQuickAffordanceSelectionManager() + } + + @Test + fun setSelections() = + runBlocking(IMMEDIATE) { + var affordanceIdsBySlotId: Map>? = null + val job = underTest.selections.onEach { affordanceIdsBySlotId = it }.launchIn(this) + val slotId1 = "slot1" + val slotId2 = "slot2" + val affordanceId1 = "affordance1" + val affordanceId2 = "affordance2" + val affordanceId3 = "affordance3" + + underTest.setSelections( + slotId = slotId1, + affordanceIds = listOf(affordanceId1), + ) + assertSelections( + affordanceIdsBySlotId, + mapOf( + slotId1 to listOf(affordanceId1), + ), + ) + + underTest.setSelections( + slotId = slotId2, + affordanceIds = listOf(affordanceId2), + ) + assertSelections( + affordanceIdsBySlotId, + mapOf( + slotId1 to listOf(affordanceId1), + slotId2 to listOf(affordanceId2), + ) + ) + + underTest.setSelections( + slotId = slotId1, + affordanceIds = listOf(affordanceId1, affordanceId3), + ) + assertSelections( + affordanceIdsBySlotId, + mapOf( + slotId1 to listOf(affordanceId1, affordanceId3), + slotId2 to listOf(affordanceId2), + ) + ) + + underTest.setSelections( + slotId = slotId1, + affordanceIds = listOf(affordanceId3), + ) + assertSelections( + affordanceIdsBySlotId, + mapOf( + slotId1 to listOf(affordanceId3), + slotId2 to listOf(affordanceId2), + ) + ) + + underTest.setSelections( + slotId = slotId2, + affordanceIds = listOf(), + ) + assertSelections( + affordanceIdsBySlotId, + mapOf( + slotId1 to listOf(affordanceId3), + slotId2 to listOf(), + ) + ) + + job.cancel() + } + + private suspend fun assertSelections( + observed: Map>?, + expected: Map>, + ) { + assertThat(underTest.getSelections()).isEqualTo(expected) + assertThat(observed).isEqualTo(expected) + } + + companion object { + private val IMMEDIATE = Dispatchers.Main.immediate + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/QrCodeScannerKeyguardQuickAffordanceConfigTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/QrCodeScannerKeyguardQuickAffordanceConfigTest.kt index 61a3f9f07600b..2bd8e9aabab30 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/QrCodeScannerKeyguardQuickAffordanceConfigTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/QrCodeScannerKeyguardQuickAffordanceConfigTest.kt @@ -50,7 +50,7 @@ class QrCodeScannerKeyguardQuickAffordanceConfigTest : SysuiTestCase() { MockitoAnnotations.initMocks(this) whenever(controller.intent).thenReturn(INTENT_1) - underTest = QrCodeScannerKeyguardQuickAffordanceConfig(controller) + underTest = QrCodeScannerKeyguardQuickAffordanceConfig(mock(), controller) } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfigTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfigTest.kt index c05beef6d6246..5178154bdeeef 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfigTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfigTest.kt @@ -59,6 +59,7 @@ class QuickAccessWalletKeyguardQuickAffordanceConfigTest : SysuiTestCase() { underTest = QuickAccessWalletKeyguardQuickAffordanceConfig( + mock(), walletController, activityStarter, ) diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardQuickAffordanceRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardQuickAffordanceRepositoryTest.kt new file mode 100644 index 0000000000000..5a7f2bb5cb37e --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardQuickAffordanceRepositoryTest.kt @@ -0,0 +1,152 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package com.android.systemui.keyguard.data.repository + +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.keyguard.data.quickaffordance.FakeKeyguardQuickAffordanceConfig +import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceConfig +import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceSelectionManager +import com.android.systemui.keyguard.shared.model.KeyguardQuickAffordancePickerRepresentation +import com.android.systemui.keyguard.shared.model.KeyguardSlotPickerRepresentation +import com.android.systemui.shared.keyguard.shared.model.KeyguardQuickAffordanceSlots +import com.google.common.truth.Truth.assertThat +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.onEach +import kotlinx.coroutines.runBlocking +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.JUnit4 + +@OptIn(ExperimentalCoroutinesApi::class) +@SmallTest +@RunWith(JUnit4::class) +class KeyguardQuickAffordanceRepositoryTest : SysuiTestCase() { + + private lateinit var underTest: KeyguardQuickAffordanceRepository + + private lateinit var config1: FakeKeyguardQuickAffordanceConfig + private lateinit var config2: FakeKeyguardQuickAffordanceConfig + + @Before + fun setUp() { + config1 = FakeKeyguardQuickAffordanceConfig("built_in:1") + config2 = FakeKeyguardQuickAffordanceConfig("built_in:2") + underTest = + KeyguardQuickAffordanceRepository( + scope = CoroutineScope(IMMEDIATE), + backgroundDispatcher = IMMEDIATE, + selectionManager = KeyguardQuickAffordanceSelectionManager(), + configs = setOf(config1, config2), + ) + } + + @Test + fun setSelections() = + runBlocking(IMMEDIATE) { + var configsBySlotId: Map>? = null + val job = underTest.selections.onEach { configsBySlotId = it }.launchIn(this) + val slotId1 = "slot1" + val slotId2 = "slot2" + + underTest.setSelections(slotId1, listOf(config1.key)) + assertSelections( + configsBySlotId, + mapOf( + slotId1 to listOf(config1), + ), + ) + + underTest.setSelections(slotId2, listOf(config2.key)) + assertSelections( + configsBySlotId, + mapOf( + slotId1 to listOf(config1), + slotId2 to listOf(config2), + ), + ) + + underTest.setSelections(slotId1, emptyList()) + underTest.setSelections(slotId2, listOf(config1.key)) + assertSelections( + configsBySlotId, + mapOf( + slotId1 to emptyList(), + slotId2 to listOf(config1), + ), + ) + + job.cancel() + } + + @Test + fun getAffordancePickerRepresentations() { + assertThat(underTest.getAffordancePickerRepresentations()) + .isEqualTo( + listOf( + KeyguardQuickAffordancePickerRepresentation( + id = config1.key, + name = config1.pickerName, + iconResourceId = config1.pickerIconResourceId, + ), + KeyguardQuickAffordancePickerRepresentation( + id = config2.key, + name = config2.pickerName, + iconResourceId = config2.pickerIconResourceId, + ), + ) + ) + } + + @Test + fun getSlotPickerRepresentations() { + assertThat(underTest.getSlotPickerRepresentations()) + .isEqualTo( + listOf( + KeyguardSlotPickerRepresentation( + id = KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START, + maxSelectedAffordances = 1, + ), + KeyguardSlotPickerRepresentation( + id = KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_END, + maxSelectedAffordances = 1, + ), + ) + ) + } + + private suspend fun assertSelections( + observed: Map>?, + expected: Map>, + ) { + assertThat(observed).isEqualTo(expected) + assertThat(underTest.getSelections()) + .isEqualTo(expected.mapValues { (_, configs) -> configs.map { it.key } }) + expected.forEach { (slotId, configs) -> + assertThat(underTest.getSelections(slotId)).isEqualTo(configs) + } + } + + companion object { + private val IMMEDIATE = Dispatchers.Main.immediate + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorParameterizedTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorParameterizedTest.kt index 7116cc101d3f8..d6469406920b0 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorParameterizedTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorParameterizedTest.kt @@ -213,10 +213,7 @@ class KeyguardQuickAffordanceInteractorParameterizedTest : SysuiTestCase() { whenever(expandable.activityLaunchController()).thenReturn(animationController) homeControls = - object : - FakeKeyguardQuickAffordanceConfig( - BuiltInKeyguardQuickAffordanceKeys.HOME_CONTROLS - ) {} + FakeKeyguardQuickAffordanceConfig(BuiltInKeyguardQuickAffordanceKeys.HOME_CONTROLS) underTest = KeyguardQuickAffordanceInteractor( keyguardInteractor = KeyguardInteractor(repository = FakeKeyguardRepository()), @@ -229,14 +226,12 @@ class KeyguardQuickAffordanceInteractorParameterizedTest : SysuiTestCase() { ), KeyguardQuickAffordancePosition.BOTTOM_END to listOf( - object : - FakeKeyguardQuickAffordanceConfig( - BuiltInKeyguardQuickAffordanceKeys.QUICK_ACCESS_WALLET - ) {}, - object : - FakeKeyguardQuickAffordanceConfig( - BuiltInKeyguardQuickAffordanceKeys.QR_CODE_SCANNER - ) {}, + FakeKeyguardQuickAffordanceConfig( + BuiltInKeyguardQuickAffordanceKeys.QUICK_ACCESS_WALLET + ), + FakeKeyguardQuickAffordanceConfig( + BuiltInKeyguardQuickAffordanceKeys.QR_CODE_SCANNER + ), ), ), ), diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt index ae32ba6676be7..a5307504e138e 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt @@ -71,20 +71,13 @@ class KeyguardQuickAffordanceInteractorTest : SysuiTestCase() { repository.setKeyguardShowing(true) homeControls = - object : - FakeKeyguardQuickAffordanceConfig( - BuiltInKeyguardQuickAffordanceKeys.HOME_CONTROLS - ) {} + FakeKeyguardQuickAffordanceConfig(BuiltInKeyguardQuickAffordanceKeys.HOME_CONTROLS) quickAccessWallet = - object : - FakeKeyguardQuickAffordanceConfig( - BuiltInKeyguardQuickAffordanceKeys.QUICK_ACCESS_WALLET - ) {} + FakeKeyguardQuickAffordanceConfig( + BuiltInKeyguardQuickAffordanceKeys.QUICK_ACCESS_WALLET + ) qrCodeScanner = - object : - FakeKeyguardQuickAffordanceConfig( - BuiltInKeyguardQuickAffordanceKeys.QR_CODE_SCANNER - ) {} + FakeKeyguardQuickAffordanceConfig(BuiltInKeyguardQuickAffordanceKeys.QR_CODE_SCANNER) underTest = KeyguardQuickAffordanceInteractor( diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt index f73d1ecf93734..d201d3b2334f8 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt @@ -82,20 +82,13 @@ class KeyguardBottomAreaViewModelTest : SysuiTestCase() { .thenReturn(RETURNED_BURN_IN_OFFSET) homeControlsQuickAffordanceConfig = - object : - FakeKeyguardQuickAffordanceConfig( - BuiltInKeyguardQuickAffordanceKeys.HOME_CONTROLS - ) {} + FakeKeyguardQuickAffordanceConfig(BuiltInKeyguardQuickAffordanceKeys.HOME_CONTROLS) quickAccessWalletAffordanceConfig = - object : - FakeKeyguardQuickAffordanceConfig( - BuiltInKeyguardQuickAffordanceKeys.QUICK_ACCESS_WALLET - ) {} + FakeKeyguardQuickAffordanceConfig( + BuiltInKeyguardQuickAffordanceKeys.QUICK_ACCESS_WALLET + ) qrCodeScannerAffordanceConfig = - object : - FakeKeyguardQuickAffordanceConfig( - BuiltInKeyguardQuickAffordanceKeys.QR_CODE_SCANNER - ) {} + FakeKeyguardQuickAffordanceConfig(BuiltInKeyguardQuickAffordanceKeys.QR_CODE_SCANNER) registry = FakeKeyguardQuickAffordanceRegistry( mapOf(