From 36b511f1b6f4742a7b841008cd4bf8ff3cac4c1a Mon Sep 17 00:00:00 2001 From: Alejandro Nijamkin Date: Wed, 26 Oct 2022 18:01:32 -0700 Subject: [PATCH] Quick affordance repository. This CL introduces KeyguardQuickAffordanceRepository which combines the known built-in configs and the current selections from KeyguardQuickAffordanceSelectionManager to produce the current set of available affordances and the current selected affordances for each slot. KeyguardQuickAffordanceSelectionManager is just an in-memory cache at the moment as we will flesh it out in upcoming CLs such that it actually uses a persistence layer. Note that the repository is meant for use for both displaying the affordances on the lock screen and for serving content provider queries coming from the wallpaper picker, which is why it's got both a flow and a "get" function as opposed to a single StateFlow. Bug: 254858696,254853190 Test: unit tests included. Tested with followup CL to make sure that quick affordances are still showing up on the lock screen. Change-Id: I6947e1f729c2aa6470ba21582d664ca5b3e3af70 --- .../model/KeyguardQuickAffordanceSlots.kt | 27 ++++ .../keyguard/dagger/KeyguardModule.java | 2 + ...meControlsKeyguardQuickAffordanceConfig.kt | 4 + .../KeyguardDataQuickAffordanceModule.kt | 39 +++++ .../KeyguardQuickAffordanceConfig.kt | 4 + ...KeyguardQuickAffordanceSelectionManager.kt | 62 +++++++ ...odeScannerKeyguardQuickAffordanceConfig.kt | 7 + ...cessWalletKeyguardQuickAffordanceConfig.kt | 7 + .../KeyguardQuickAffordanceRepository.kt | 128 +++++++++++++++ ...uardQuickAffordancePickerRepresentation.kt | 30 ++++ .../model/KeyguardSlotPickerRepresentation.kt | 28 ++++ .../FakeKeyguardQuickAffordanceConfig.kt | 11 +- ...uardQuickAffordanceSelectionManagerTest.kt | 127 +++++++++++++++ ...cannerKeyguardQuickAffordanceConfigTest.kt | 2 +- ...WalletKeyguardQuickAffordanceConfigTest.kt | 1 + .../KeyguardQuickAffordanceRepositoryTest.kt | 152 ++++++++++++++++++ ...ckAffordanceInteractorParameterizedTest.kt | 19 +-- .../KeyguardQuickAffordanceInteractorTest.kt | 17 +- .../KeyguardBottomAreaViewModelTest.kt | 17 +- 19 files changed, 640 insertions(+), 44 deletions(-) create mode 100644 packages/SystemUI/shared/src/com/android/systemui/shared/keyguard/shared/model/KeyguardQuickAffordanceSlots.kt create mode 100644 packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardDataQuickAffordanceModule.kt create mode 100644 packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceSelectionManager.kt create mode 100644 packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardQuickAffordanceRepository.kt create mode 100644 packages/SystemUI/src/com/android/systemui/keyguard/shared/model/KeyguardQuickAffordancePickerRepresentation.kt create mode 100644 packages/SystemUI/src/com/android/systemui/keyguard/shared/model/KeyguardSlotPickerRepresentation.kt create mode 100644 packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceSelectionManagerTest.kt create mode 100644 packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardQuickAffordanceRepositoryTest.kt 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(