diff options
11 files changed, 331 insertions, 28 deletions
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/quickaffordance/shared/model/KeyguardPreviewConstants.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/quickaffordance/shared/model/KeyguardPreviewConstants.kt index 08ee60204178..a3f40d47cc3f 100644 --- a/packages/SystemUI/customization/src/com/android/systemui/shared/quickaffordance/shared/model/KeyguardPreviewConstants.kt +++ b/packages/SystemUI/customization/src/com/android/systemui/shared/quickaffordance/shared/model/KeyguardPreviewConstants.kt @@ -18,10 +18,17 @@ package com.android.systemui.shared.quickaffordance.shared.model object KeyguardPreviewConstants { + const val MESSAGE_ID_DEFAULT_PREVIEW = 707 const val MESSAGE_ID_HIDE_SMART_SPACE = 1111 - const val KEY_HIDE_SMART_SPACE = "hide_smart_space" + const val MESSAGE_ID_PREVIEW_QUICK_AFFORDANCE_SELECTED = 1988 const val MESSAGE_ID_SLOT_SELECTED = 1337 - const val KEY_SLOT_ID = "slot_id" - const val KEY_INITIALLY_SELECTED_SLOT_ID = "initially_selected_slot_id" + const val MESSAGE_ID_START_CUSTOMIZING_QUICK_AFFORDANCES = 214 + + const val KEY_HIDE_SMART_SPACE = "hide_smart_space" const val KEY_HIGHLIGHT_QUICK_AFFORDANCES = "highlight_quick_affordances" + const val KEY_INITIALLY_SELECTED_SLOT_ID = "initially_selected_slot_id" + const val KEY_QUICK_AFFORDANCE_ID = "quick_affordance_id" + const val KEY_SLOT_ID = "slot_id" + + const val KEYGUARD_QUICK_AFFORDANCE_ID_NONE = "none" } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/KeyguardQuickAffordanceRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/KeyguardQuickAffordanceRepositoryTest.kt index 8e109b4aab20..c85cd662dac6 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/KeyguardQuickAffordanceRepositoryTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/KeyguardQuickAffordanceRepositoryTest.kt @@ -308,6 +308,15 @@ class KeyguardQuickAffordanceRepositoryTest : SysuiTestCase() { ) } + @Test + fun getConfig() = + testScope.runTest { + assertThat(underTest.getConfig(FakeCustomizationProviderClient.AFFORDANCE_1)) + .isEqualTo(config1) + assertThat(underTest.getConfig(FakeCustomizationProviderClient.AFFORDANCE_2)) + .isEqualTo(config2) + } + private fun assertSelections( observed: Map<String, List<KeyguardQuickAffordanceConfig>>?, expected: Map<String, List<KeyguardQuickAffordanceConfig>>, diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt index 75c0d3b60ecb..ad07c1c1b88f 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt @@ -426,6 +426,64 @@ class KeyguardQuickAffordanceInteractorTest : SysuiTestCase() { } @Test + fun quickAffordanceAlwaysVisible_withNonNullOverrideKeyguardQuickAffordanceId() = + testScope.runTest { + quickAccessWallet.setState( + KeyguardQuickAffordanceConfig.LockScreenState.Visible( + icon = ICON, + activationState = ActivationState.Active, + ) + ) + homeControls.setState( + KeyguardQuickAffordanceConfig.LockScreenState.Visible( + icon = ICON, + activationState = ActivationState.Active, + ) + ) + + // The default case + val collectedValue = + collectLastValue( + underTest.quickAffordanceAlwaysVisible( + KeyguardQuickAffordancePosition.BOTTOM_START, + ) + ) + assertThat(collectedValue()) + .isInstanceOf(KeyguardQuickAffordanceModel.Visible::class.java) + val visibleModel = collectedValue() as KeyguardQuickAffordanceModel.Visible + assertThat(visibleModel.configKey) + .isEqualTo( + "${KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START}::${homeControls.key}" + ) + assertThat(visibleModel.icon).isEqualTo(ICON) + assertThat(visibleModel.icon.contentDescription) + .isEqualTo(ContentDescription.Resource(res = CONTENT_DESCRIPTION_RESOURCE_ID)) + assertThat(visibleModel.activationState).isEqualTo(ActivationState.Active) + + // With override + val collectedValueWithOverride = + collectLastValue( + underTest.quickAffordanceAlwaysVisible( + position = KeyguardQuickAffordancePosition.BOTTOM_START, + overrideQuickAffordanceId = + BuiltInKeyguardQuickAffordanceKeys.QUICK_ACCESS_WALLET, + ) + ) + assertThat(collectedValueWithOverride()) + .isInstanceOf(KeyguardQuickAffordanceModel.Visible::class.java) + val visibleModelWithOverride = + collectedValueWithOverride() as KeyguardQuickAffordanceModel.Visible + assertThat(visibleModelWithOverride.configKey) + .isEqualTo( + "${KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START}::${quickAccessWallet.key}" + ) + assertThat(visibleModelWithOverride.icon).isEqualTo(ICON) + assertThat(visibleModelWithOverride.icon.contentDescription) + .isEqualTo(ContentDescription.Resource(res = CONTENT_DESCRIPTION_RESOURCE_ID)) + assertThat(visibleModelWithOverride.activationState).isEqualTo(ActivationState.Active) + } + + @Test fun select() = testScope.runTest { overrideResource( diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/NewPickerUiKeyguardPreview.kt b/packages/SystemUI/src/com/android/systemui/keyguard/NewPickerUiKeyguardPreview.kt new file mode 100644 index 000000000000..7e09a108ea40 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/keyguard/NewPickerUiKeyguardPreview.kt @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2024 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 + +import com.android.systemui.Flags + +/** Helper for reading or using the new picker UI flag. */ +@Suppress("NOTHING_TO_INLINE") +object NewPickerUiKeyguardPreview { + + /** Is the new picker UI enabled */ + @JvmStatic + inline val isEnabled + get() = Flags.newPickerUi() +} 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 index 698328ecd7fb..d49550ef4c83 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardQuickAffordanceRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardQuickAffordanceRepository.kt @@ -22,7 +22,6 @@ import android.content.Intent import android.os.UserHandle import android.util.LayoutDirection import com.android.systemui.Dumpable -import com.android.systemui.res.R import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging import com.android.systemui.common.coroutine.ConflatedCallbackFlow import com.android.systemui.dagger.SysUISingleton @@ -35,6 +34,7 @@ import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanc 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.res.R import com.android.systemui.settings.UserTracker import java.io.PrintWriter import javax.inject.Inject @@ -61,10 +61,13 @@ constructor( private val remoteUserSelectionManager: KeyguardQuickAffordanceRemoteUserSelectionManager, private val userTracker: UserTracker, legacySettingSyncer: KeyguardQuickAffordanceLegacySettingSyncer, - private val configs: Set<@JvmSuppressWildcards KeyguardQuickAffordanceConfig>, + configs: Set<@JvmSuppressWildcards KeyguardQuickAffordanceConfig>, dumpManager: DumpManager, userHandle: UserHandle, ) { + // Configs for all keyguard quick affordances, mapped by the quick affordance ID as key + private val configsByAffordanceId: Map<String, KeyguardQuickAffordanceConfig> = + configs.associateBy { it.key } private val userId: Flow<Int> = ConflatedCallbackFlow.conflatedCallbackFlow { val callback = @@ -126,7 +129,7 @@ constructor( */ fun getCurrentSelections(slotId: String): List<KeyguardQuickAffordanceConfig> { val selections = selectionManager.value.getSelections().getOrDefault(slotId, emptyList()) - return configs.filter { selections.contains(it.key) } + return configsByAffordanceId.values.filter { selections.contains(it.key) } } /** @@ -159,7 +162,7 @@ constructor( */ suspend fun getAffordancePickerRepresentations(): List<KeyguardQuickAffordancePickerRepresentation> { - return configs + return configsByAffordanceId.values .associateWith { config -> config.getPickerScreenState() } .filterNot { (_, pickerState) -> pickerState is KeyguardQuickAffordanceConfig.PickerScreenState.UnavailableOnDevice @@ -226,6 +229,11 @@ constructor( } } + /** Get the config of a quick affordance. */ + fun getConfig(quickAffordanceId: String): KeyguardQuickAffordanceConfig? { + return configsByAffordanceId[quickAffordanceId] + } + private inner class Dumpster : Dumpable { override fun dump(pw: PrintWriter, args: Array<out String>) { val slotPickerRepresentations = getSlotPickerRepresentations() @@ -246,7 +254,7 @@ constructor( pw.println(" $slotId$selectionText (capacity = $capacity)") } pw.println("Available affordances on device:") - configs.forEach { config -> + configsByAffordanceId.values.forEach { config -> pw.println(" ${config.key} (\"${config.pickerName()}\")") } } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt index 0682d8777107..2af95f20c7d7 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt @@ -53,6 +53,7 @@ import com.android.systemui.scene.shared.model.Scenes import com.android.systemui.settings.UserTracker import com.android.systemui.shade.domain.interactor.ShadeInteractor import com.android.systemui.shared.customization.data.content.CustomizationProviderContract as Contract +import com.android.systemui.shared.quickaffordance.shared.model.KeyguardPreviewConstants.KEYGUARD_QUICK_AFFORDANCE_ID_NONE import com.android.systemui.statusbar.phone.SystemUIDialog import com.android.systemui.statusbar.policy.KeyguardStateController import dagger.Lazy @@ -142,14 +143,18 @@ constructor( * * This is useful for experiences like the lock screen preview mode, where the affordances must * always be visible. + * + * @param overrideQuickAffordanceId If null, return the currently-set quick affordance; + * otherwise, override and return the correspondent [KeyguardQuickAffordanceModel]. */ suspend fun quickAffordanceAlwaysVisible( position: KeyguardQuickAffordancePosition, + overrideQuickAffordanceId: String? = null, ): Flow<KeyguardQuickAffordanceModel> { return if (isFeatureDisabledByDevicePolicy()) { flowOf(KeyguardQuickAffordanceModel.Hidden) } else { - quickAffordanceInternal(position) + quickAffordanceInternal(position, overrideQuickAffordanceId) } } @@ -299,12 +304,24 @@ constructor( } private fun quickAffordanceInternal( - position: KeyguardQuickAffordancePosition + position: KeyguardQuickAffordancePosition, + overrideAffordanceId: String? = null, ): Flow<KeyguardQuickAffordanceModel> = repository .get() .selections - .map { it[position.toSlotId()] ?: emptyList() } + .map { selections -> + val overrideQuickAffordanceConfigs = + overrideAffordanceId?.let { + if (it == KEYGUARD_QUICK_AFFORDANCE_ID_NONE) { + emptyList() + } else { + val config = repository.get().getConfig(it) + listOfNotNull(config) + } + } + overrideQuickAffordanceConfigs ?: selections[position.toSlotId()] ?: emptyList() + } .flatMapLatest { configs -> combinedConfigs(position, configs) } private fun combinedConfigs( diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/shared/quickaffordance/KeyguardQuickAffordancePosition.kt b/packages/SystemUI/src/com/android/systemui/keyguard/shared/quickaffordance/KeyguardQuickAffordancePosition.kt index 2581b595d812..1bbe84383e48 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/shared/quickaffordance/KeyguardQuickAffordancePosition.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/shared/quickaffordance/KeyguardQuickAffordancePosition.kt @@ -16,7 +16,8 @@ package com.android.systemui.keyguard.shared.quickaffordance -import com.android.systemui.shared.keyguard.shared.model.KeyguardQuickAffordanceSlots +import com.android.systemui.shared.keyguard.shared.model.KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_END +import com.android.systemui.shared.keyguard.shared.model.KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START /** Enumerates all possible positions for quick affordances that can appear on the lock-screen. */ enum class KeyguardQuickAffordancePosition { @@ -25,8 +26,19 @@ enum class KeyguardQuickAffordancePosition { fun toSlotId(): String { return when (this) { - BOTTOM_START -> KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START - BOTTOM_END -> KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_END + BOTTOM_START -> SLOT_ID_BOTTOM_START + BOTTOM_END -> SLOT_ID_BOTTOM_END } } + + companion object { + + /** If the slot ID does not match any string, return null. */ + fun parseKeyguardQuickAffordancePosition(slotId: String): KeyguardQuickAffordancePosition? = + when (slotId) { + SLOT_ID_BOTTOM_START -> BOTTOM_START + SLOT_ID_BOTTOM_END -> BOTTOM_END + else -> null + } + } } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt index 6031ef60e1be..51ce3556ffbd 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt @@ -275,6 +275,15 @@ constructor( } } + fun onStartCustomizingQuickAffordances( + initiallySelectedSlotId: String?, + ) { + quickAffordancesCombinedViewModel.enablePreviewMode( + initiallySelectedSlotId = initiallySelectedSlotId, + shouldHighlightSelectedAffordance = true, + ) + } + fun onSlotSelected(slotId: String) { if (KeyguardBottomAreaRefactor.isEnabled) { quickAffordancesCombinedViewModel.onPreviewSlotSelected(slotId = slotId) @@ -283,6 +292,21 @@ constructor( } } + fun onPreviewQuickAffordanceSelected(slotId: String, quickAffordanceId: String) { + quickAffordancesCombinedViewModel.onPreviewQuickAffordanceSelected( + slotId, + quickAffordanceId, + ) + } + + fun onDefaultPreview() { + quickAffordancesCombinedViewModel.onClearPreviewQuickAffordances() + quickAffordancesCombinedViewModel.enablePreviewMode( + initiallySelectedSlotId = null, + shouldHighlightSelectedAffordance = false, + ) + } + fun destroy() { isDestroyed = true lockscreenSmartspaceController.disconnect() diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardRemotePreviewManager.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardRemotePreviewManager.kt index 0532ee285d1d..a6108c45a479 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardRemotePreviewManager.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardRemotePreviewManager.kt @@ -31,7 +31,16 @@ import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.dagger.qualifiers.Main -import com.android.systemui.shared.quickaffordance.shared.model.KeyguardPreviewConstants +import com.android.systemui.shared.keyguard.shared.model.KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START +import com.android.systemui.shared.quickaffordance.shared.model.KeyguardPreviewConstants.KEY_HIDE_SMART_SPACE +import com.android.systemui.shared.quickaffordance.shared.model.KeyguardPreviewConstants.KEY_INITIALLY_SELECTED_SLOT_ID +import com.android.systemui.shared.quickaffordance.shared.model.KeyguardPreviewConstants.KEY_QUICK_AFFORDANCE_ID +import com.android.systemui.shared.quickaffordance.shared.model.KeyguardPreviewConstants.KEY_SLOT_ID +import com.android.systemui.shared.quickaffordance.shared.model.KeyguardPreviewConstants.MESSAGE_ID_DEFAULT_PREVIEW +import com.android.systemui.shared.quickaffordance.shared.model.KeyguardPreviewConstants.MESSAGE_ID_HIDE_SMART_SPACE +import com.android.systemui.shared.quickaffordance.shared.model.KeyguardPreviewConstants.MESSAGE_ID_PREVIEW_QUICK_AFFORDANCE_SELECTED +import com.android.systemui.shared.quickaffordance.shared.model.KeyguardPreviewConstants.MESSAGE_ID_SLOT_SELECTED +import com.android.systemui.shared.quickaffordance.shared.model.KeyguardPreviewConstants.MESSAGE_ID_START_CUSTOMIZING_QUICK_AFFORDANCES import com.android.systemui.util.kotlin.logD import javax.inject.Inject import kotlinx.coroutines.CoroutineDispatcher @@ -59,7 +68,7 @@ constructor( return try { val renderer = if (Flags.lockscreenPreviewRendererCreateOnMainThread()) { - runBlocking ("$TAG#previewRendererFactory.create", mainDispatcher) { + runBlocking("$TAG#previewRendererFactory.create", mainDispatcher) { previewRendererFactory.create(request) } } else { @@ -157,16 +166,35 @@ class PreviewLifecycleObserver( } when (message.what) { - KeyguardPreviewConstants.MESSAGE_ID_SLOT_SELECTED -> { - message.data.getString(KeyguardPreviewConstants.KEY_SLOT_ID)?.let { slotId -> + MESSAGE_ID_START_CUSTOMIZING_QUICK_AFFORDANCES -> { + checkNotNull(renderer) + .onStartCustomizingQuickAffordances( + initiallySelectedSlotId = + message.data.getString(KEY_INITIALLY_SELECTED_SLOT_ID) + ?: SLOT_ID_BOTTOM_START + ) + } + MESSAGE_ID_SLOT_SELECTED -> { + message.data.getString(KEY_SLOT_ID)?.let { slotId -> checkNotNull(renderer).onSlotSelected(slotId = slotId) } } - KeyguardPreviewConstants.MESSAGE_ID_HIDE_SMART_SPACE -> { - checkNotNull(renderer) - .hideSmartspace( - message.data.getBoolean(KeyguardPreviewConstants.KEY_HIDE_SMART_SPACE) - ) + MESSAGE_ID_PREVIEW_QUICK_AFFORDANCE_SELECTED -> { + val slotId = message.data.getString(KEY_SLOT_ID) + val quickAffordanceId = message.data.getString(KEY_QUICK_AFFORDANCE_ID) + if (slotId != null && quickAffordanceId != null) { + checkNotNull(renderer) + .onPreviewQuickAffordanceSelected( + slotId = slotId, + quickAffordanceId = quickAffordanceId, + ) + } + } + MESSAGE_ID_DEFAULT_PREVIEW -> { + checkNotNull(renderer).onDefaultPreview() + } + MESSAGE_ID_HIDE_SMART_SPACE -> { + checkNotNull(renderer).hideSmartspace(message.data.getBoolean(KEY_HIDE_SMART_SPACE)) } else -> checkNotNull(onDestroy).invoke(this) } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModel.kt index 2426f9745885..c885c9a5a29b 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModel.kt @@ -20,6 +20,7 @@ package com.android.systemui.keyguard.ui.viewmodel import androidx.annotation.VisibleForTesting import com.android.app.tracing.FlowTracing.traceEmissionCount import com.android.systemui.dagger.qualifiers.Application +import com.android.systemui.keyguard.NewPickerUiKeyguardPreview import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor import com.android.systemui.keyguard.domain.interactor.KeyguardQuickAffordanceInteractor import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor @@ -29,6 +30,7 @@ import com.android.systemui.keyguard.shared.quickaffordance.ActivationState import com.android.systemui.keyguard.shared.quickaffordance.KeyguardQuickAffordancePosition import com.android.systemui.shade.domain.interactor.ShadeInteractor import com.android.systemui.shared.keyguard.shared.model.KeyguardQuickAffordanceSlots +import com.android.systemui.utils.coroutines.flow.flatMapLatestConflated import javax.inject.Inject import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.ExperimentalCoroutinesApi @@ -164,13 +166,36 @@ constructor( .map { alpha -> alpha >= AFFORDANCE_FULLY_OPAQUE_ALPHA_THRESHOLD } .distinctUntilChanged() + private val previewAffordances = + MutableStateFlow<Map<KeyguardQuickAffordancePosition, String>>(emptyMap()) + /** An observable for the view-model of the "start button" quick affordance. */ val startButton: Flow<KeyguardQuickAffordanceViewModel> = - button(KeyguardQuickAffordancePosition.BOTTOM_START) + if (NewPickerUiKeyguardPreview.isEnabled) { + previewAffordances.flatMapLatestConflated { + button( + position = KeyguardQuickAffordancePosition.BOTTOM_START, + overrideQuickAffordanceId = it[KeyguardQuickAffordancePosition.BOTTOM_START], + ) + } + } else { + button( + KeyguardQuickAffordancePosition.BOTTOM_START, + ) + } /** An observable for the view-model of the "end button" quick affordance. */ val endButton: Flow<KeyguardQuickAffordanceViewModel> = - button(KeyguardQuickAffordancePosition.BOTTOM_END) + if (NewPickerUiKeyguardPreview.isEnabled) { + previewAffordances.flatMapLatestConflated { + button( + position = KeyguardQuickAffordancePosition.BOTTOM_END, + overrideQuickAffordanceId = it[KeyguardQuickAffordancePosition.BOTTOM_END], + ) + } + } else { + button(KeyguardQuickAffordancePosition.BOTTOM_END) + } /** * Notifies that a slot with the given ID has been selected in the preview experience that is @@ -183,6 +208,28 @@ constructor( } /** + * Notifies to preview an affordance at a given slot ID. This is ignored for the real lock + * screen experience. + */ + fun onPreviewQuickAffordanceSelected(slotId: String, affordanceId: String) { + val position = + KeyguardQuickAffordancePosition.parseKeyguardQuickAffordancePosition(slotId) ?: return + previewAffordances.value = + previewAffordances.value.toMutableMap().let { + it[position] = affordanceId + HashMap(it) + } + } + + /** + * Notifies to clear up the preview affordances map. This is ignored for the real lock screen + * experience. + */ + fun onClearPreviewQuickAffordances() { + previewAffordances.value = emptyMap() + } + + /** * Puts this view-model in "preview mode", which means it's being used for UI that is rendering * the lock screen preview in wallpaper picker / settings and not the real experience on the * lock screen. @@ -207,14 +254,16 @@ constructor( } private fun button( - position: KeyguardQuickAffordancePosition + position: KeyguardQuickAffordancePosition, + overrideQuickAffordanceId: String? = null, ): Flow<KeyguardQuickAffordanceViewModel> { return previewMode .flatMapLatest { previewMode -> combine( if (previewMode.isInPreviewMode) { quickAffordanceInteractor.quickAffordanceAlwaysVisible( - position = position + position = position, + overrideQuickAffordanceId = overrideQuickAffordanceId, ) } else { quickAffordanceInteractor.quickAffordance(position = position) diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModelTest.kt index 24bea2ce51c7..73b9f5783004 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModelTest.kt @@ -20,6 +20,7 @@ package com.android.systemui.keyguard.ui.viewmodel import android.app.admin.DevicePolicyManager import android.content.Intent import android.os.UserHandle +import android.platform.test.annotations.EnableFlags import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.internal.widget.LockPatternUtils @@ -402,6 +403,67 @@ class KeyguardQuickAffordancesCombinedViewModelTest : SysuiTestCase() { } @Test + @EnableFlags(com.android.systemui.Flags.FLAG_NEW_PICKER_UI) + fun startButton_inPreviewMode_onPreviewQuickAffordanceSelected() = + testScope.runTest { + underTest.onPreviewSlotSelected(KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START) + underTest.enablePreviewMode(KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START, true) + + repository.setKeyguardShowing(false) + val latest = collectLastValue(underTest.startButton) + + val icon: Icon = mock() + val testConfig = + TestConfig( + isVisible = true, + isClickable = true, + isActivated = true, + icon = icon, + canShowWhileLocked = false, + intent = null, + slotId = KeyguardQuickAffordancePosition.BOTTOM_START.toSlotId(), + ) + val defaultConfigKey = + setUpQuickAffordanceModel( + position = KeyguardQuickAffordancePosition.BOTTOM_START, + testConfig = testConfig, + ) + + // Set up the quick access wallet config + val quickAccessWalletAffordanceConfigKey = + quickAccessWalletAffordanceConfig + .apply { + onTriggeredResult = + KeyguardQuickAffordanceConfig.OnTriggeredResult.StartActivity( + intent = Intent("action"), + canShowWhileLocked = false, + ) + setState( + KeyguardQuickAffordanceConfig.LockScreenState.Visible( + icon = icon, + activationState = ActivationState.Active, + ) + ) + } + .let { + KeyguardQuickAffordancePosition.BOTTOM_START.toSlotId() + + "::${quickAccessWalletAffordanceConfig.key}" + } + + // onPreviewQuickAffordanceSelected should trigger the override with the quick access + // wallet quick affordance + underTest.onPreviewQuickAffordanceSelected( + KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START, + BuiltInKeyguardQuickAffordanceKeys.QUICK_ACCESS_WALLET, + ) + Truth.assertThat(latest()?.configKey).isEqualTo(quickAccessWalletAffordanceConfigKey) + + // onClearPreviewQuickAffordances should make the default quick affordance shows again + underTest.onClearPreviewQuickAffordances() + Truth.assertThat(latest()?.configKey).isEqualTo(defaultConfigKey) + } + + @Test fun startButton_inPreviewMode_visibleEvenWhenKeyguardNotShowing() = testScope.runTest { underTest.onPreviewSlotSelected(KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START) @@ -445,7 +507,7 @@ class KeyguardQuickAffordancesCombinedViewModelTest : SysuiTestCase() { } @Test - fun endButton_inHiglightedPreviewMode_dimmedWhenOtherIsSelected() = + fun endButton_inHighlightedPreviewMode_dimmedWhenOtherIsSelected() = testScope.runTest { underTest.onPreviewSlotSelected(KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START) underTest.enablePreviewMode(KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START, true) |