Merge changes Ie810d37d,I6947e1f7 into tm-qpr-dev
* changes: Quick affordance interactor uses repository. Quick affordance repository.
This commit is contained in:
@@ -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"
|
||||||
|
}
|
||||||
@@ -125,6 +125,14 @@ object Flags {
|
|||||||
// TODO(b/255607168): Tracking Bug
|
// TODO(b/255607168): Tracking Bug
|
||||||
@JvmField val DOZING_MIGRATION_1 = UnreleasedFlag(213)
|
@JvmField val DOZING_MIGRATION_1 = UnreleasedFlag(213)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether to enable the code powering customizable lock screen quick affordances.
|
||||||
|
*
|
||||||
|
* Note that this flag does not enable individual implementations of quick affordances like the
|
||||||
|
* new camera quick affordance. Look for individual flags for those.
|
||||||
|
*/
|
||||||
|
@JvmField val CUSTOMIZABLE_LOCK_SCREEN_QUICK_AFFORDANCES = UnreleasedFlag(214, teamfood = false)
|
||||||
|
|
||||||
// 300 - power menu
|
// 300 - power menu
|
||||||
// TODO(b/254512600): Tracking Bug
|
// TODO(b/254512600): Tracking Bug
|
||||||
@JvmField val POWER_MENU_LITE = ReleasedFlag(300)
|
@JvmField val POWER_MENU_LITE = ReleasedFlag(300)
|
||||||
|
|||||||
@@ -42,6 +42,7 @@ import com.android.systemui.dump.DumpManager;
|
|||||||
import com.android.systemui.keyguard.DismissCallbackRegistry;
|
import com.android.systemui.keyguard.DismissCallbackRegistry;
|
||||||
import com.android.systemui.keyguard.KeyguardUnlockAnimationController;
|
import com.android.systemui.keyguard.KeyguardUnlockAnimationController;
|
||||||
import com.android.systemui.keyguard.KeyguardViewMediator;
|
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.data.repository.KeyguardRepositoryModule;
|
||||||
import com.android.systemui.keyguard.domain.interactor.StartKeyguardTransitionModule;
|
import com.android.systemui.keyguard.domain.interactor.StartKeyguardTransitionModule;
|
||||||
import com.android.systemui.keyguard.domain.quickaffordance.KeyguardQuickAffordanceModule;
|
import com.android.systemui.keyguard.domain.quickaffordance.KeyguardQuickAffordanceModule;
|
||||||
@@ -71,6 +72,7 @@ import dagger.Provides;
|
|||||||
KeyguardUserSwitcherComponent.class},
|
KeyguardUserSwitcherComponent.class},
|
||||||
includes = {
|
includes = {
|
||||||
FalsingModule.class,
|
FalsingModule.class,
|
||||||
|
KeyguardDataQuickAffordanceModule.class,
|
||||||
KeyguardQuickAffordanceModule.class,
|
KeyguardQuickAffordanceModule.class,
|
||||||
KeyguardRepositoryModule.class,
|
KeyguardRepositoryModule.class,
|
||||||
StartKeyguardTransitionModule.class,
|
StartKeyguardTransitionModule.class,
|
||||||
|
|||||||
@@ -53,6 +53,10 @@ constructor(
|
|||||||
|
|
||||||
override val key: String = BuiltInKeyguardQuickAffordanceKeys.HOME_CONTROLS
|
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<KeyguardQuickAffordanceConfig.LockScreenState> =
|
override val lockScreenState: Flow<KeyguardQuickAffordanceConfig.LockScreenState> =
|
||||||
component.canShowWhileLockedSetting.flatMapLatest { canShowWhileLocked ->
|
component.canShowWhileLockedSetting.flatMapLatest { canShowWhileLocked ->
|
||||||
if (canShowWhileLocked) {
|
if (canShowWhileLocked) {
|
||||||
|
|||||||
@@ -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<KeyguardQuickAffordanceConfig> {
|
||||||
|
return setOf(
|
||||||
|
home,
|
||||||
|
quickAccessWallet,
|
||||||
|
qrCodeScanner,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -29,6 +29,10 @@ interface KeyguardQuickAffordanceConfig {
|
|||||||
/** Unique identifier for this quick affordance. It must be globally unique. */
|
/** Unique identifier for this quick affordance. It must be globally unique. */
|
||||||
val key: String
|
val key: String
|
||||||
|
|
||||||
|
val pickerName: String
|
||||||
|
|
||||||
|
val pickerIconResourceId: Int
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The ever-changing state of the affordance.
|
* The ever-changing state of the affordance.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -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<Map<String, List<String>>>(emptyMap())
|
||||||
|
|
||||||
|
/** IDs of affordances to show, indexed by slot ID, and sorted in descending priority order. */
|
||||||
|
val selections: Flow<Map<String, List<String>>> = _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<String, List<String>> {
|
||||||
|
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<String>,
|
||||||
|
) {
|
||||||
|
// 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
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -17,6 +17,7 @@
|
|||||||
|
|
||||||
package com.android.systemui.keyguard.data.quickaffordance
|
package com.android.systemui.keyguard.data.quickaffordance
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
import com.android.systemui.R
|
import com.android.systemui.R
|
||||||
import com.android.systemui.animation.Expandable
|
import com.android.systemui.animation.Expandable
|
||||||
import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
|
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.ContentDescription
|
||||||
import com.android.systemui.common.shared.model.Icon
|
import com.android.systemui.common.shared.model.Icon
|
||||||
import com.android.systemui.dagger.SysUISingleton
|
import com.android.systemui.dagger.SysUISingleton
|
||||||
|
import com.android.systemui.dagger.qualifiers.Application
|
||||||
import com.android.systemui.qrcodescanner.controller.QRCodeScannerController
|
import com.android.systemui.qrcodescanner.controller.QRCodeScannerController
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
import kotlinx.coroutines.channels.awaitClose
|
import kotlinx.coroutines.channels.awaitClose
|
||||||
@@ -34,11 +36,16 @@ import kotlinx.coroutines.flow.Flow
|
|||||||
class QrCodeScannerKeyguardQuickAffordanceConfig
|
class QrCodeScannerKeyguardQuickAffordanceConfig
|
||||||
@Inject
|
@Inject
|
||||||
constructor(
|
constructor(
|
||||||
|
@Application context: Context,
|
||||||
private val controller: QRCodeScannerController,
|
private val controller: QRCodeScannerController,
|
||||||
) : KeyguardQuickAffordanceConfig {
|
) : KeyguardQuickAffordanceConfig {
|
||||||
|
|
||||||
override val key: String = BuiltInKeyguardQuickAffordanceKeys.QR_CODE_SCANNER
|
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<KeyguardQuickAffordanceConfig.LockScreenState> =
|
override val lockScreenState: Flow<KeyguardQuickAffordanceConfig.LockScreenState> =
|
||||||
conflatedCallbackFlow {
|
conflatedCallbackFlow {
|
||||||
val callback =
|
val callback =
|
||||||
|
|||||||
@@ -17,6 +17,7 @@
|
|||||||
|
|
||||||
package com.android.systemui.keyguard.data.quickaffordance
|
package com.android.systemui.keyguard.data.quickaffordance
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
import android.graphics.drawable.Drawable
|
import android.graphics.drawable.Drawable
|
||||||
import android.service.quickaccesswallet.GetWalletCardsError
|
import android.service.quickaccesswallet.GetWalletCardsError
|
||||||
import android.service.quickaccesswallet.GetWalletCardsResponse
|
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.ContentDescription
|
||||||
import com.android.systemui.common.shared.model.Icon
|
import com.android.systemui.common.shared.model.Icon
|
||||||
import com.android.systemui.dagger.SysUISingleton
|
import com.android.systemui.dagger.SysUISingleton
|
||||||
|
import com.android.systemui.dagger.qualifiers.Application
|
||||||
import com.android.systemui.plugins.ActivityStarter
|
import com.android.systemui.plugins.ActivityStarter
|
||||||
import com.android.systemui.wallet.controller.QuickAccessWalletController
|
import com.android.systemui.wallet.controller.QuickAccessWalletController
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
@@ -40,12 +42,17 @@ import kotlinx.coroutines.flow.Flow
|
|||||||
class QuickAccessWalletKeyguardQuickAffordanceConfig
|
class QuickAccessWalletKeyguardQuickAffordanceConfig
|
||||||
@Inject
|
@Inject
|
||||||
constructor(
|
constructor(
|
||||||
|
@Application context: Context,
|
||||||
private val walletController: QuickAccessWalletController,
|
private val walletController: QuickAccessWalletController,
|
||||||
private val activityStarter: ActivityStarter,
|
private val activityStarter: ActivityStarter,
|
||||||
) : KeyguardQuickAffordanceConfig {
|
) : KeyguardQuickAffordanceConfig {
|
||||||
|
|
||||||
override val key: String = BuiltInKeyguardQuickAffordanceKeys.QUICK_ACCESS_WALLET
|
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<KeyguardQuickAffordanceConfig.LockScreenState> =
|
override val lockScreenState: Flow<KeyguardQuickAffordanceConfig.LockScreenState> =
|
||||||
conflatedCallbackFlow {
|
conflatedCallbackFlow {
|
||||||
val callback =
|
val callback =
|
||||||
|
|||||||
@@ -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<Map<String, List<KeyguardQuickAffordanceConfig>>> =
|
||||||
|
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<KeyguardQuickAffordanceConfig> {
|
||||||
|
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<String, List<String>> {
|
||||||
|
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<String>,
|
||||||
|
) {
|
||||||
|
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<KeyguardQuickAffordancePickerRepresentation> {
|
||||||
|
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<KeyguardSlotPickerRepresentation> {
|
||||||
|
// 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,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -18,19 +18,30 @@
|
|||||||
package com.android.systemui.keyguard.domain.interactor
|
package com.android.systemui.keyguard.domain.interactor
|
||||||
|
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
|
import android.util.Log
|
||||||
import com.android.internal.widget.LockPatternUtils
|
import com.android.internal.widget.LockPatternUtils
|
||||||
import com.android.systemui.animation.Expandable
|
import com.android.systemui.animation.Expandable
|
||||||
import com.android.systemui.dagger.SysUISingleton
|
import com.android.systemui.dagger.SysUISingleton
|
||||||
|
import com.android.systemui.flags.FeatureFlags
|
||||||
|
import com.android.systemui.flags.Flags
|
||||||
import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceConfig
|
import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceConfig
|
||||||
|
import com.android.systemui.keyguard.data.repository.KeyguardQuickAffordanceRepository
|
||||||
import com.android.systemui.keyguard.domain.model.KeyguardQuickAffordanceModel
|
import com.android.systemui.keyguard.domain.model.KeyguardQuickAffordanceModel
|
||||||
import com.android.systemui.keyguard.domain.quickaffordance.KeyguardQuickAffordanceRegistry
|
import com.android.systemui.keyguard.domain.quickaffordance.KeyguardQuickAffordanceRegistry
|
||||||
|
import com.android.systemui.keyguard.shared.model.KeyguardQuickAffordancePickerRepresentation
|
||||||
|
import com.android.systemui.keyguard.shared.model.KeyguardSlotPickerRepresentation
|
||||||
import com.android.systemui.keyguard.shared.quickaffordance.KeyguardQuickAffordancePosition
|
import com.android.systemui.keyguard.shared.quickaffordance.KeyguardQuickAffordancePosition
|
||||||
import com.android.systemui.plugins.ActivityStarter
|
import com.android.systemui.plugins.ActivityStarter
|
||||||
import com.android.systemui.settings.UserTracker
|
import com.android.systemui.settings.UserTracker
|
||||||
|
import com.android.systemui.shared.keyguard.shared.model.KeyguardQuickAffordanceSlots
|
||||||
import com.android.systemui.statusbar.policy.KeyguardStateController
|
import com.android.systemui.statusbar.policy.KeyguardStateController
|
||||||
|
import dagger.Lazy
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
import kotlinx.coroutines.flow.combine
|
import kotlinx.coroutines.flow.combine
|
||||||
|
import kotlinx.coroutines.flow.flatMapLatest
|
||||||
|
import kotlinx.coroutines.flow.flowOf
|
||||||
|
import kotlinx.coroutines.flow.map
|
||||||
import kotlinx.coroutines.flow.onStart
|
import kotlinx.coroutines.flow.onStart
|
||||||
|
|
||||||
@SysUISingleton
|
@SysUISingleton
|
||||||
@@ -43,7 +54,12 @@ constructor(
|
|||||||
private val keyguardStateController: KeyguardStateController,
|
private val keyguardStateController: KeyguardStateController,
|
||||||
private val userTracker: UserTracker,
|
private val userTracker: UserTracker,
|
||||||
private val activityStarter: ActivityStarter,
|
private val activityStarter: ActivityStarter,
|
||||||
|
private val featureFlags: FeatureFlags,
|
||||||
|
private val repository: Lazy<KeyguardQuickAffordanceRepository>,
|
||||||
) {
|
) {
|
||||||
|
private val isUsingRepository: Boolean
|
||||||
|
get() = featureFlags.isEnabled(Flags.CUSTOMIZABLE_LOCK_SCREEN_QUICK_AFFORDANCES)
|
||||||
|
|
||||||
/** Returns an observable for the quick affordance at the given position. */
|
/** Returns an observable for the quick affordance at the given position. */
|
||||||
fun quickAffordance(
|
fun quickAffordance(
|
||||||
position: KeyguardQuickAffordancePosition
|
position: KeyguardQuickAffordancePosition
|
||||||
@@ -72,7 +88,19 @@ constructor(
|
|||||||
configKey: String,
|
configKey: String,
|
||||||
expandable: Expandable?,
|
expandable: Expandable?,
|
||||||
) {
|
) {
|
||||||
@Suppress("UNCHECKED_CAST") val config = registry.get(configKey)
|
@Suppress("UNCHECKED_CAST")
|
||||||
|
val config =
|
||||||
|
if (isUsingRepository) {
|
||||||
|
val (slotId, decodedConfigKey) = configKey.decode()
|
||||||
|
repository.get().selections.value[slotId]?.find { it.key == decodedConfigKey }
|
||||||
|
} else {
|
||||||
|
registry.get(configKey)
|
||||||
|
}
|
||||||
|
if (config == null) {
|
||||||
|
Log.e(TAG, "Affordance config with key of \"$configKey\" not found!")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
when (val result = config.onTriggered(expandable)) {
|
when (val result = config.onTriggered(expandable)) {
|
||||||
is KeyguardQuickAffordanceConfig.OnTriggeredResult.StartActivity ->
|
is KeyguardQuickAffordanceConfig.OnTriggeredResult.StartActivity ->
|
||||||
launchQuickAffordance(
|
launchQuickAffordance(
|
||||||
@@ -84,28 +112,138 @@ constructor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Selects an affordance with the given ID on the slot with the given ID.
|
||||||
|
*
|
||||||
|
* @return `true` if the affordance was selected successfully; `false` otherwise.
|
||||||
|
*/
|
||||||
|
suspend fun select(slotId: String, affordanceId: String): Boolean {
|
||||||
|
check(isUsingRepository)
|
||||||
|
|
||||||
|
val slots = repository.get().getSlotPickerRepresentations()
|
||||||
|
val slot = slots.find { it.id == slotId } ?: return false
|
||||||
|
val selections =
|
||||||
|
repository.get().getSelections().getOrDefault(slotId, emptyList()).toMutableList()
|
||||||
|
val alreadySelected = selections.remove(affordanceId)
|
||||||
|
if (!alreadySelected) {
|
||||||
|
while (selections.size > 0 && selections.size >= slot.maxSelectedAffordances) {
|
||||||
|
selections.removeAt(0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
selections.add(affordanceId)
|
||||||
|
|
||||||
|
repository
|
||||||
|
.get()
|
||||||
|
.setSelections(
|
||||||
|
slotId = slotId,
|
||||||
|
affordanceIds = selections,
|
||||||
|
)
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unselects one or all affordances from the slot with the given ID.
|
||||||
|
*
|
||||||
|
* @param slotId The ID of the slot.
|
||||||
|
* @param affordanceId The ID of the affordance to remove; if `null`, removes all affordances
|
||||||
|
* from the slot.
|
||||||
|
* @return `true` if the affordance was successfully removed; `false` otherwise (for example, if
|
||||||
|
* the affordance was not on the slot to begin with).
|
||||||
|
*/
|
||||||
|
suspend fun unselect(slotId: String, affordanceId: String?): Boolean {
|
||||||
|
check(isUsingRepository)
|
||||||
|
|
||||||
|
val slots = repository.get().getSlotPickerRepresentations()
|
||||||
|
if (slots.find { it.id == slotId } == null) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if (affordanceId.isNullOrEmpty()) {
|
||||||
|
return if (
|
||||||
|
repository.get().getSelections().getOrDefault(slotId, emptyList()).isEmpty()
|
||||||
|
) {
|
||||||
|
false
|
||||||
|
} else {
|
||||||
|
repository.get().setSelections(slotId = slotId, affordanceIds = emptyList())
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val selections =
|
||||||
|
repository.get().getSelections().getOrDefault(slotId, emptyList()).toMutableList()
|
||||||
|
return if (selections.remove(affordanceId)) {
|
||||||
|
repository
|
||||||
|
.get()
|
||||||
|
.setSelections(
|
||||||
|
slotId = slotId,
|
||||||
|
affordanceIds = selections,
|
||||||
|
)
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Returns affordance IDs indexed by slot ID, for all known slots. */
|
||||||
|
suspend fun getSelections(): Map<String, List<String>> {
|
||||||
|
check(isUsingRepository)
|
||||||
|
|
||||||
|
val selections = repository.get().getSelections()
|
||||||
|
return repository.get().getSlotPickerRepresentations().associate { slotRepresentation ->
|
||||||
|
slotRepresentation.id to (selections[slotRepresentation.id] ?: emptyList())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun quickAffordanceInternal(
|
private fun quickAffordanceInternal(
|
||||||
position: KeyguardQuickAffordancePosition
|
position: KeyguardQuickAffordancePosition
|
||||||
): Flow<KeyguardQuickAffordanceModel> {
|
): Flow<KeyguardQuickAffordanceModel> {
|
||||||
val configs = registry.getAll(position)
|
return if (isUsingRepository) {
|
||||||
|
repository
|
||||||
|
.get()
|
||||||
|
.selections
|
||||||
|
.map { it[position.toSlotId()] ?: emptyList() }
|
||||||
|
.flatMapLatest { configs -> combinedConfigs(position, configs) }
|
||||||
|
} else {
|
||||||
|
combinedConfigs(position, registry.getAll(position))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun combinedConfigs(
|
||||||
|
position: KeyguardQuickAffordancePosition,
|
||||||
|
configs: List<KeyguardQuickAffordanceConfig>,
|
||||||
|
): Flow<KeyguardQuickAffordanceModel> {
|
||||||
|
if (configs.isEmpty()) {
|
||||||
|
return flowOf(KeyguardQuickAffordanceModel.Hidden)
|
||||||
|
}
|
||||||
|
|
||||||
return combine(
|
return combine(
|
||||||
configs.map { config ->
|
configs.map { config ->
|
||||||
// We emit an initial "Hidden" value to make sure that there's always an initial
|
// We emit an initial "Hidden" value to make sure that there's always an
|
||||||
// value and avoid subtle bugs where the downstream isn't receiving any values
|
// initial value and avoid subtle bugs where the downstream isn't receiving
|
||||||
// because one config implementation is not emitting an initial value. For example,
|
// any values because one config implementation is not emitting an initial
|
||||||
// see b/244296596.
|
// value. For example, see b/244296596.
|
||||||
config.lockScreenState.onStart {
|
config.lockScreenState.onStart {
|
||||||
emit(KeyguardQuickAffordanceConfig.LockScreenState.Hidden)
|
emit(KeyguardQuickAffordanceConfig.LockScreenState.Hidden)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
) { states ->
|
) { states ->
|
||||||
val index =
|
val index =
|
||||||
states.indexOfFirst { it is KeyguardQuickAffordanceConfig.LockScreenState.Visible }
|
states.indexOfFirst { state ->
|
||||||
|
state is KeyguardQuickAffordanceConfig.LockScreenState.Visible
|
||||||
|
}
|
||||||
if (index != -1) {
|
if (index != -1) {
|
||||||
val visibleState =
|
val visibleState =
|
||||||
states[index] as KeyguardQuickAffordanceConfig.LockScreenState.Visible
|
states[index] as KeyguardQuickAffordanceConfig.LockScreenState.Visible
|
||||||
|
val configKey = configs[index].key
|
||||||
KeyguardQuickAffordanceModel.Visible(
|
KeyguardQuickAffordanceModel.Visible(
|
||||||
configKey = configs[index].key,
|
configKey =
|
||||||
|
if (isUsingRepository) {
|
||||||
|
configKey.encode(position.toSlotId())
|
||||||
|
} else {
|
||||||
|
configKey
|
||||||
|
},
|
||||||
icon = visibleState.icon,
|
icon = visibleState.icon,
|
||||||
activationState = visibleState.activationState,
|
activationState = visibleState.activationState,
|
||||||
)
|
)
|
||||||
@@ -145,4 +283,39 @@ constructor(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun KeyguardQuickAffordancePosition.toSlotId(): String {
|
||||||
|
return when (this) {
|
||||||
|
KeyguardQuickAffordancePosition.BOTTOM_START ->
|
||||||
|
KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START
|
||||||
|
KeyguardQuickAffordancePosition.BOTTOM_END ->
|
||||||
|
KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_END
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun String.encode(slotId: String): String {
|
||||||
|
return "$slotId$DELIMITER$this"
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun String.decode(): Pair<String, String> {
|
||||||
|
val splitUp = this.split(DELIMITER)
|
||||||
|
return Pair(splitUp[0], splitUp[1])
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getAffordancePickerRepresentations(): List<KeyguardQuickAffordancePickerRepresentation> {
|
||||||
|
check(isUsingRepository)
|
||||||
|
|
||||||
|
return repository.get().getAffordancePickerRepresentations()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getSlotPickerRepresentations(): List<KeyguardSlotPickerRepresentation> {
|
||||||
|
check(isUsingRepository)
|
||||||
|
|
||||||
|
return repository.get().getSlotPickerRepresentations()
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private const val TAG = "KeyguardQuickAffordanceInteractor"
|
||||||
|
private const val DELIMITER = "::"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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,
|
||||||
|
)
|
||||||
@@ -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,
|
||||||
|
)
|
||||||
@@ -23,14 +23,11 @@ import kotlinx.coroutines.flow.Flow
|
|||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
import kotlinx.coroutines.yield
|
import kotlinx.coroutines.yield
|
||||||
|
|
||||||
/**
|
/** Fake implementation of a quick affordance data source. */
|
||||||
* Fake implementation of a quick affordance data source.
|
class FakeKeyguardQuickAffordanceConfig(
|
||||||
*
|
|
||||||
* 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(
|
|
||||||
override val key: String,
|
override val key: String,
|
||||||
|
override val pickerName: String = key,
|
||||||
|
override val pickerIconResourceId: Int = 0,
|
||||||
) : KeyguardQuickAffordanceConfig {
|
) : KeyguardQuickAffordanceConfig {
|
||||||
|
|
||||||
var onTriggeredResult: OnTriggeredResult = OnTriggeredResult.Handled
|
var onTriggeredResult: OnTriggeredResult = OnTriggeredResult.Handled
|
||||||
|
|||||||
@@ -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<String, List<String>>? = 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<String, List<String>>?,
|
||||||
|
expected: Map<String, List<String>>,
|
||||||
|
) {
|
||||||
|
assertThat(underTest.getSelections()).isEqualTo(expected)
|
||||||
|
assertThat(observed).isEqualTo(expected)
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private val IMMEDIATE = Dispatchers.Main.immediate
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -50,7 +50,7 @@ class QrCodeScannerKeyguardQuickAffordanceConfigTest : SysuiTestCase() {
|
|||||||
MockitoAnnotations.initMocks(this)
|
MockitoAnnotations.initMocks(this)
|
||||||
whenever(controller.intent).thenReturn(INTENT_1)
|
whenever(controller.intent).thenReturn(INTENT_1)
|
||||||
|
|
||||||
underTest = QrCodeScannerKeyguardQuickAffordanceConfig(controller)
|
underTest = QrCodeScannerKeyguardQuickAffordanceConfig(mock(), controller)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|||||||
@@ -59,6 +59,7 @@ class QuickAccessWalletKeyguardQuickAffordanceConfigTest : SysuiTestCase() {
|
|||||||
|
|
||||||
underTest =
|
underTest =
|
||||||
QuickAccessWalletKeyguardQuickAffordanceConfig(
|
QuickAccessWalletKeyguardQuickAffordanceConfig(
|
||||||
|
mock(),
|
||||||
walletController,
|
walletController,
|
||||||
activityStarter,
|
activityStarter,
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -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<String, List<KeyguardQuickAffordanceConfig>>? = 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<String, List<KeyguardQuickAffordanceConfig>>?,
|
||||||
|
expected: Map<String, List<KeyguardQuickAffordanceConfig>>,
|
||||||
|
) {
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -25,10 +25,14 @@ import com.android.systemui.animation.ActivityLaunchAnimator
|
|||||||
import com.android.systemui.animation.Expandable
|
import com.android.systemui.animation.Expandable
|
||||||
import com.android.systemui.common.shared.model.ContentDescription
|
import com.android.systemui.common.shared.model.ContentDescription
|
||||||
import com.android.systemui.common.shared.model.Icon
|
import com.android.systemui.common.shared.model.Icon
|
||||||
|
import com.android.systemui.flags.FakeFeatureFlags
|
||||||
|
import com.android.systemui.flags.Flags
|
||||||
import com.android.systemui.keyguard.data.quickaffordance.BuiltInKeyguardQuickAffordanceKeys
|
import com.android.systemui.keyguard.data.quickaffordance.BuiltInKeyguardQuickAffordanceKeys
|
||||||
import com.android.systemui.keyguard.data.quickaffordance.FakeKeyguardQuickAffordanceConfig
|
import com.android.systemui.keyguard.data.quickaffordance.FakeKeyguardQuickAffordanceConfig
|
||||||
import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceConfig
|
import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceConfig
|
||||||
|
import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceSelectionManager
|
||||||
import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
|
import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
|
||||||
|
import com.android.systemui.keyguard.data.repository.KeyguardQuickAffordanceRepository
|
||||||
import com.android.systemui.keyguard.domain.quickaffordance.FakeKeyguardQuickAffordanceRegistry
|
import com.android.systemui.keyguard.domain.quickaffordance.FakeKeyguardQuickAffordanceRegistry
|
||||||
import com.android.systemui.keyguard.shared.quickaffordance.KeyguardQuickAffordancePosition
|
import com.android.systemui.keyguard.shared.quickaffordance.KeyguardQuickAffordancePosition
|
||||||
import com.android.systemui.plugins.ActivityStarter
|
import com.android.systemui.plugins.ActivityStarter
|
||||||
@@ -37,6 +41,8 @@ import com.android.systemui.statusbar.policy.KeyguardStateController
|
|||||||
import com.android.systemui.util.mockito.any
|
import com.android.systemui.util.mockito.any
|
||||||
import com.android.systemui.util.mockito.mock
|
import com.android.systemui.util.mockito.mock
|
||||||
import com.android.systemui.util.mockito.whenever
|
import com.android.systemui.util.mockito.whenever
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.test.runBlockingTest
|
import kotlinx.coroutines.test.runBlockingTest
|
||||||
import org.junit.Before
|
import org.junit.Before
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
@@ -189,6 +195,8 @@ class KeyguardQuickAffordanceInteractorParameterizedTest : SysuiTestCase() {
|
|||||||
/* startActivity= */ true,
|
/* startActivity= */ true,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
private val IMMEDIATE = Dispatchers.Main.immediate
|
||||||
}
|
}
|
||||||
|
|
||||||
@Mock private lateinit var lockPatternUtils: LockPatternUtils
|
@Mock private lateinit var lockPatternUtils: LockPatternUtils
|
||||||
@@ -213,10 +221,20 @@ class KeyguardQuickAffordanceInteractorParameterizedTest : SysuiTestCase() {
|
|||||||
whenever(expandable.activityLaunchController()).thenReturn(animationController)
|
whenever(expandable.activityLaunchController()).thenReturn(animationController)
|
||||||
|
|
||||||
homeControls =
|
homeControls =
|
||||||
object :
|
FakeKeyguardQuickAffordanceConfig(BuiltInKeyguardQuickAffordanceKeys.HOME_CONTROLS)
|
||||||
FakeKeyguardQuickAffordanceConfig(
|
val quickAccessWallet =
|
||||||
BuiltInKeyguardQuickAffordanceKeys.HOME_CONTROLS
|
FakeKeyguardQuickAffordanceConfig(
|
||||||
) {}
|
BuiltInKeyguardQuickAffordanceKeys.QUICK_ACCESS_WALLET
|
||||||
|
)
|
||||||
|
val qrCodeScanner =
|
||||||
|
FakeKeyguardQuickAffordanceConfig(BuiltInKeyguardQuickAffordanceKeys.QR_CODE_SCANNER)
|
||||||
|
val quickAffordanceRepository =
|
||||||
|
KeyguardQuickAffordanceRepository(
|
||||||
|
scope = CoroutineScope(IMMEDIATE),
|
||||||
|
backgroundDispatcher = IMMEDIATE,
|
||||||
|
selectionManager = KeyguardQuickAffordanceSelectionManager(),
|
||||||
|
configs = setOf(homeControls, quickAccessWallet, qrCodeScanner),
|
||||||
|
)
|
||||||
underTest =
|
underTest =
|
||||||
KeyguardQuickAffordanceInteractor(
|
KeyguardQuickAffordanceInteractor(
|
||||||
keyguardInteractor = KeyguardInteractor(repository = FakeKeyguardRepository()),
|
keyguardInteractor = KeyguardInteractor(repository = FakeKeyguardRepository()),
|
||||||
@@ -229,14 +247,8 @@ class KeyguardQuickAffordanceInteractorParameterizedTest : SysuiTestCase() {
|
|||||||
),
|
),
|
||||||
KeyguardQuickAffordancePosition.BOTTOM_END to
|
KeyguardQuickAffordancePosition.BOTTOM_END to
|
||||||
listOf(
|
listOf(
|
||||||
object :
|
quickAccessWallet,
|
||||||
FakeKeyguardQuickAffordanceConfig(
|
qrCodeScanner,
|
||||||
BuiltInKeyguardQuickAffordanceKeys.QUICK_ACCESS_WALLET
|
|
||||||
) {},
|
|
||||||
object :
|
|
||||||
FakeKeyguardQuickAffordanceConfig(
|
|
||||||
BuiltInKeyguardQuickAffordanceKeys.QR_CODE_SCANNER
|
|
||||||
) {},
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -244,6 +256,11 @@ class KeyguardQuickAffordanceInteractorParameterizedTest : SysuiTestCase() {
|
|||||||
keyguardStateController = keyguardStateController,
|
keyguardStateController = keyguardStateController,
|
||||||
userTracker = userTracker,
|
userTracker = userTracker,
|
||||||
activityStarter = activityStarter,
|
activityStarter = activityStarter,
|
||||||
|
featureFlags =
|
||||||
|
FakeFeatureFlags().apply {
|
||||||
|
set(Flags.CUSTOMIZABLE_LOCK_SCREEN_QUICK_AFFORDANCES, false)
|
||||||
|
},
|
||||||
|
repository = { quickAffordanceRepository },
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -22,22 +22,31 @@ import com.android.internal.widget.LockPatternUtils
|
|||||||
import com.android.systemui.SysuiTestCase
|
import com.android.systemui.SysuiTestCase
|
||||||
import com.android.systemui.common.shared.model.ContentDescription
|
import com.android.systemui.common.shared.model.ContentDescription
|
||||||
import com.android.systemui.common.shared.model.Icon
|
import com.android.systemui.common.shared.model.Icon
|
||||||
|
import com.android.systemui.flags.FakeFeatureFlags
|
||||||
|
import com.android.systemui.flags.Flags
|
||||||
import com.android.systemui.keyguard.data.quickaffordance.BuiltInKeyguardQuickAffordanceKeys
|
import com.android.systemui.keyguard.data.quickaffordance.BuiltInKeyguardQuickAffordanceKeys
|
||||||
import com.android.systemui.keyguard.data.quickaffordance.FakeKeyguardQuickAffordanceConfig
|
import com.android.systemui.keyguard.data.quickaffordance.FakeKeyguardQuickAffordanceConfig
|
||||||
import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceConfig
|
import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceConfig
|
||||||
|
import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceSelectionManager
|
||||||
import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
|
import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
|
||||||
|
import com.android.systemui.keyguard.data.repository.KeyguardQuickAffordanceRepository
|
||||||
import com.android.systemui.keyguard.domain.model.KeyguardQuickAffordanceModel
|
import com.android.systemui.keyguard.domain.model.KeyguardQuickAffordanceModel
|
||||||
import com.android.systemui.keyguard.domain.quickaffordance.FakeKeyguardQuickAffordanceRegistry
|
import com.android.systemui.keyguard.domain.quickaffordance.FakeKeyguardQuickAffordanceRegistry
|
||||||
import com.android.systemui.keyguard.shared.quickaffordance.ActivationState
|
import com.android.systemui.keyguard.shared.quickaffordance.ActivationState
|
||||||
import com.android.systemui.keyguard.shared.quickaffordance.KeyguardQuickAffordancePosition
|
import com.android.systemui.keyguard.shared.quickaffordance.KeyguardQuickAffordancePosition
|
||||||
import com.android.systemui.plugins.ActivityStarter
|
import com.android.systemui.plugins.ActivityStarter
|
||||||
import com.android.systemui.settings.UserTracker
|
import com.android.systemui.settings.UserTracker
|
||||||
|
import com.android.systemui.shared.keyguard.shared.model.KeyguardQuickAffordanceSlots
|
||||||
import com.android.systemui.statusbar.policy.KeyguardStateController
|
import com.android.systemui.statusbar.policy.KeyguardStateController
|
||||||
import com.android.systemui.util.mockito.mock
|
import com.android.systemui.util.mockito.mock
|
||||||
import com.android.systemui.util.mockito.whenever
|
import com.android.systemui.util.mockito.whenever
|
||||||
import com.google.common.truth.Truth.assertThat
|
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.launchIn
|
||||||
import kotlinx.coroutines.flow.onEach
|
import kotlinx.coroutines.flow.onEach
|
||||||
|
import kotlinx.coroutines.runBlocking
|
||||||
import kotlinx.coroutines.test.runBlockingTest
|
import kotlinx.coroutines.test.runBlockingTest
|
||||||
import kotlinx.coroutines.yield
|
import kotlinx.coroutines.yield
|
||||||
import org.junit.Before
|
import org.junit.Before
|
||||||
@@ -47,6 +56,7 @@ import org.junit.runners.JUnit4
|
|||||||
import org.mockito.Mock
|
import org.mockito.Mock
|
||||||
import org.mockito.MockitoAnnotations
|
import org.mockito.MockitoAnnotations
|
||||||
|
|
||||||
|
@OptIn(ExperimentalCoroutinesApi::class)
|
||||||
@SmallTest
|
@SmallTest
|
||||||
@RunWith(JUnit4::class)
|
@RunWith(JUnit4::class)
|
||||||
class KeyguardQuickAffordanceInteractorTest : SysuiTestCase() {
|
class KeyguardQuickAffordanceInteractorTest : SysuiTestCase() {
|
||||||
@@ -62,6 +72,7 @@ class KeyguardQuickAffordanceInteractorTest : SysuiTestCase() {
|
|||||||
private lateinit var homeControls: FakeKeyguardQuickAffordanceConfig
|
private lateinit var homeControls: FakeKeyguardQuickAffordanceConfig
|
||||||
private lateinit var quickAccessWallet: FakeKeyguardQuickAffordanceConfig
|
private lateinit var quickAccessWallet: FakeKeyguardQuickAffordanceConfig
|
||||||
private lateinit var qrCodeScanner: FakeKeyguardQuickAffordanceConfig
|
private lateinit var qrCodeScanner: FakeKeyguardQuickAffordanceConfig
|
||||||
|
private lateinit var featureFlags: FakeFeatureFlags
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
fun setUp() {
|
fun setUp() {
|
||||||
@@ -71,20 +82,25 @@ class KeyguardQuickAffordanceInteractorTest : SysuiTestCase() {
|
|||||||
repository.setKeyguardShowing(true)
|
repository.setKeyguardShowing(true)
|
||||||
|
|
||||||
homeControls =
|
homeControls =
|
||||||
object :
|
FakeKeyguardQuickAffordanceConfig(BuiltInKeyguardQuickAffordanceKeys.HOME_CONTROLS)
|
||||||
FakeKeyguardQuickAffordanceConfig(
|
|
||||||
BuiltInKeyguardQuickAffordanceKeys.HOME_CONTROLS
|
|
||||||
) {}
|
|
||||||
quickAccessWallet =
|
quickAccessWallet =
|
||||||
object :
|
FakeKeyguardQuickAffordanceConfig(
|
||||||
FakeKeyguardQuickAffordanceConfig(
|
BuiltInKeyguardQuickAffordanceKeys.QUICK_ACCESS_WALLET
|
||||||
BuiltInKeyguardQuickAffordanceKeys.QUICK_ACCESS_WALLET
|
)
|
||||||
) {}
|
|
||||||
qrCodeScanner =
|
qrCodeScanner =
|
||||||
object :
|
FakeKeyguardQuickAffordanceConfig(BuiltInKeyguardQuickAffordanceKeys.QR_CODE_SCANNER)
|
||||||
FakeKeyguardQuickAffordanceConfig(
|
|
||||||
BuiltInKeyguardQuickAffordanceKeys.QR_CODE_SCANNER
|
val quickAffordanceRepository =
|
||||||
) {}
|
KeyguardQuickAffordanceRepository(
|
||||||
|
scope = CoroutineScope(IMMEDIATE),
|
||||||
|
backgroundDispatcher = IMMEDIATE,
|
||||||
|
selectionManager = KeyguardQuickAffordanceSelectionManager(),
|
||||||
|
configs = setOf(homeControls, quickAccessWallet, qrCodeScanner),
|
||||||
|
)
|
||||||
|
featureFlags =
|
||||||
|
FakeFeatureFlags().apply {
|
||||||
|
set(Flags.CUSTOMIZABLE_LOCK_SCREEN_QUICK_AFFORDANCES, false)
|
||||||
|
}
|
||||||
|
|
||||||
underTest =
|
underTest =
|
||||||
KeyguardQuickAffordanceInteractor(
|
KeyguardQuickAffordanceInteractor(
|
||||||
@@ -107,6 +123,8 @@ class KeyguardQuickAffordanceInteractorTest : SysuiTestCase() {
|
|||||||
keyguardStateController = keyguardStateController,
|
keyguardStateController = keyguardStateController,
|
||||||
userTracker = userTracker,
|
userTracker = userTracker,
|
||||||
activityStarter = activityStarter,
|
activityStarter = activityStarter,
|
||||||
|
featureFlags = featureFlags,
|
||||||
|
repository = { quickAffordanceRepository },
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -210,6 +228,270 @@ class KeyguardQuickAffordanceInteractorTest : SysuiTestCase() {
|
|||||||
job.cancel()
|
job.cancel()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun select() =
|
||||||
|
runBlocking(IMMEDIATE) {
|
||||||
|
featureFlags.set(Flags.CUSTOMIZABLE_LOCK_SCREEN_QUICK_AFFORDANCES, true)
|
||||||
|
homeControls.setState(
|
||||||
|
KeyguardQuickAffordanceConfig.LockScreenState.Visible(icon = ICON)
|
||||||
|
)
|
||||||
|
quickAccessWallet.setState(
|
||||||
|
KeyguardQuickAffordanceConfig.LockScreenState.Visible(icon = ICON)
|
||||||
|
)
|
||||||
|
qrCodeScanner.setState(
|
||||||
|
KeyguardQuickAffordanceConfig.LockScreenState.Visible(icon = ICON)
|
||||||
|
)
|
||||||
|
|
||||||
|
assertThat(underTest.getSelections())
|
||||||
|
.isEqualTo(
|
||||||
|
mapOf<String, List<String>>(
|
||||||
|
KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START to emptyList(),
|
||||||
|
KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_END to emptyList(),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
var startConfig: KeyguardQuickAffordanceModel? = null
|
||||||
|
val job1 =
|
||||||
|
underTest
|
||||||
|
.quickAffordance(KeyguardQuickAffordancePosition.BOTTOM_START)
|
||||||
|
.onEach { startConfig = it }
|
||||||
|
.launchIn(this)
|
||||||
|
var endConfig: KeyguardQuickAffordanceModel? = null
|
||||||
|
val job2 =
|
||||||
|
underTest
|
||||||
|
.quickAffordance(KeyguardQuickAffordancePosition.BOTTOM_END)
|
||||||
|
.onEach { endConfig = it }
|
||||||
|
.launchIn(this)
|
||||||
|
|
||||||
|
underTest.select(KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START, homeControls.key)
|
||||||
|
yield()
|
||||||
|
yield()
|
||||||
|
assertThat(startConfig)
|
||||||
|
.isEqualTo(
|
||||||
|
KeyguardQuickAffordanceModel.Visible(
|
||||||
|
configKey =
|
||||||
|
KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START +
|
||||||
|
"::${homeControls.key}",
|
||||||
|
icon = ICON,
|
||||||
|
activationState = ActivationState.NotSupported,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
assertThat(endConfig)
|
||||||
|
.isEqualTo(
|
||||||
|
KeyguardQuickAffordanceModel.Hidden,
|
||||||
|
)
|
||||||
|
assertThat(underTest.getSelections())
|
||||||
|
.isEqualTo(
|
||||||
|
mapOf(
|
||||||
|
KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START to
|
||||||
|
listOf(homeControls.key),
|
||||||
|
KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_END to emptyList(),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
underTest.select(
|
||||||
|
KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START,
|
||||||
|
quickAccessWallet.key
|
||||||
|
)
|
||||||
|
yield()
|
||||||
|
yield()
|
||||||
|
assertThat(startConfig)
|
||||||
|
.isEqualTo(
|
||||||
|
KeyguardQuickAffordanceModel.Visible(
|
||||||
|
configKey =
|
||||||
|
KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START +
|
||||||
|
"::${quickAccessWallet.key}",
|
||||||
|
icon = ICON,
|
||||||
|
activationState = ActivationState.NotSupported,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
assertThat(endConfig)
|
||||||
|
.isEqualTo(
|
||||||
|
KeyguardQuickAffordanceModel.Hidden,
|
||||||
|
)
|
||||||
|
assertThat(underTest.getSelections())
|
||||||
|
.isEqualTo(
|
||||||
|
mapOf(
|
||||||
|
KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START to
|
||||||
|
listOf(quickAccessWallet.key),
|
||||||
|
KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_END to emptyList(),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
underTest.select(KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_END, qrCodeScanner.key)
|
||||||
|
yield()
|
||||||
|
yield()
|
||||||
|
assertThat(startConfig)
|
||||||
|
.isEqualTo(
|
||||||
|
KeyguardQuickAffordanceModel.Visible(
|
||||||
|
configKey =
|
||||||
|
KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START +
|
||||||
|
"::${quickAccessWallet.key}",
|
||||||
|
icon = ICON,
|
||||||
|
activationState = ActivationState.NotSupported,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
assertThat(endConfig)
|
||||||
|
.isEqualTo(
|
||||||
|
KeyguardQuickAffordanceModel.Visible(
|
||||||
|
configKey =
|
||||||
|
KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_END +
|
||||||
|
"::${qrCodeScanner.key}",
|
||||||
|
icon = ICON,
|
||||||
|
activationState = ActivationState.NotSupported,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
assertThat(underTest.getSelections())
|
||||||
|
.isEqualTo(
|
||||||
|
mapOf(
|
||||||
|
KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START to
|
||||||
|
listOf(quickAccessWallet.key),
|
||||||
|
KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_END to
|
||||||
|
listOf(qrCodeScanner.key),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
job1.cancel()
|
||||||
|
job2.cancel()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `unselect - one`() =
|
||||||
|
runBlocking(IMMEDIATE) {
|
||||||
|
featureFlags.set(Flags.CUSTOMIZABLE_LOCK_SCREEN_QUICK_AFFORDANCES, true)
|
||||||
|
homeControls.setState(
|
||||||
|
KeyguardQuickAffordanceConfig.LockScreenState.Visible(icon = ICON)
|
||||||
|
)
|
||||||
|
quickAccessWallet.setState(
|
||||||
|
KeyguardQuickAffordanceConfig.LockScreenState.Visible(icon = ICON)
|
||||||
|
)
|
||||||
|
qrCodeScanner.setState(
|
||||||
|
KeyguardQuickAffordanceConfig.LockScreenState.Visible(icon = ICON)
|
||||||
|
)
|
||||||
|
|
||||||
|
var startConfig: KeyguardQuickAffordanceModel? = null
|
||||||
|
val job1 =
|
||||||
|
underTest
|
||||||
|
.quickAffordance(KeyguardQuickAffordancePosition.BOTTOM_START)
|
||||||
|
.onEach { startConfig = it }
|
||||||
|
.launchIn(this)
|
||||||
|
var endConfig: KeyguardQuickAffordanceModel? = null
|
||||||
|
val job2 =
|
||||||
|
underTest
|
||||||
|
.quickAffordance(KeyguardQuickAffordancePosition.BOTTOM_END)
|
||||||
|
.onEach { endConfig = it }
|
||||||
|
.launchIn(this)
|
||||||
|
underTest.select(KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START, homeControls.key)
|
||||||
|
yield()
|
||||||
|
yield()
|
||||||
|
underTest.select(KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_END, quickAccessWallet.key)
|
||||||
|
yield()
|
||||||
|
yield()
|
||||||
|
|
||||||
|
underTest.unselect(KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START, homeControls.key)
|
||||||
|
yield()
|
||||||
|
yield()
|
||||||
|
|
||||||
|
assertThat(startConfig)
|
||||||
|
.isEqualTo(
|
||||||
|
KeyguardQuickAffordanceModel.Hidden,
|
||||||
|
)
|
||||||
|
assertThat(endConfig)
|
||||||
|
.isEqualTo(
|
||||||
|
KeyguardQuickAffordanceModel.Visible(
|
||||||
|
configKey =
|
||||||
|
KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_END +
|
||||||
|
"::${quickAccessWallet.key}",
|
||||||
|
icon = ICON,
|
||||||
|
activationState = ActivationState.NotSupported,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
assertThat(underTest.getSelections())
|
||||||
|
.isEqualTo(
|
||||||
|
mapOf(
|
||||||
|
KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START to emptyList(),
|
||||||
|
KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_END to
|
||||||
|
listOf(quickAccessWallet.key),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
underTest.unselect(
|
||||||
|
KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_END,
|
||||||
|
quickAccessWallet.key
|
||||||
|
)
|
||||||
|
yield()
|
||||||
|
yield()
|
||||||
|
|
||||||
|
assertThat(startConfig)
|
||||||
|
.isEqualTo(
|
||||||
|
KeyguardQuickAffordanceModel.Hidden,
|
||||||
|
)
|
||||||
|
assertThat(endConfig)
|
||||||
|
.isEqualTo(
|
||||||
|
KeyguardQuickAffordanceModel.Hidden,
|
||||||
|
)
|
||||||
|
assertThat(underTest.getSelections())
|
||||||
|
.isEqualTo(
|
||||||
|
mapOf<String, List<String>>(
|
||||||
|
KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START to emptyList(),
|
||||||
|
KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_END to emptyList(),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
job1.cancel()
|
||||||
|
job2.cancel()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `unselect - all`() =
|
||||||
|
runBlocking(IMMEDIATE) {
|
||||||
|
featureFlags.set(Flags.CUSTOMIZABLE_LOCK_SCREEN_QUICK_AFFORDANCES, true)
|
||||||
|
homeControls.setState(
|
||||||
|
KeyguardQuickAffordanceConfig.LockScreenState.Visible(icon = ICON)
|
||||||
|
)
|
||||||
|
quickAccessWallet.setState(
|
||||||
|
KeyguardQuickAffordanceConfig.LockScreenState.Visible(icon = ICON)
|
||||||
|
)
|
||||||
|
qrCodeScanner.setState(
|
||||||
|
KeyguardQuickAffordanceConfig.LockScreenState.Visible(icon = ICON)
|
||||||
|
)
|
||||||
|
|
||||||
|
underTest.select(KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START, homeControls.key)
|
||||||
|
yield()
|
||||||
|
yield()
|
||||||
|
underTest.select(KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_END, quickAccessWallet.key)
|
||||||
|
yield()
|
||||||
|
yield()
|
||||||
|
|
||||||
|
underTest.unselect(KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START, null)
|
||||||
|
yield()
|
||||||
|
yield()
|
||||||
|
|
||||||
|
assertThat(underTest.getSelections())
|
||||||
|
.isEqualTo(
|
||||||
|
mapOf(
|
||||||
|
KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START to emptyList(),
|
||||||
|
KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_END to
|
||||||
|
listOf(quickAccessWallet.key),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
underTest.unselect(
|
||||||
|
KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_END,
|
||||||
|
null,
|
||||||
|
)
|
||||||
|
yield()
|
||||||
|
yield()
|
||||||
|
|
||||||
|
assertThat(underTest.getSelections())
|
||||||
|
.isEqualTo(
|
||||||
|
mapOf<String, List<String>>(
|
||||||
|
KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START to emptyList(),
|
||||||
|
KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_END to emptyList(),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private val ICON: Icon = mock {
|
private val ICON: Icon = mock {
|
||||||
whenever(this.contentDescription)
|
whenever(this.contentDescription)
|
||||||
@@ -220,5 +502,6 @@ class KeyguardQuickAffordanceInteractorTest : SysuiTestCase() {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
private const val CONTENT_DESCRIPTION_RESOURCE_ID = 1337
|
private const val CONTENT_DESCRIPTION_RESOURCE_ID = 1337
|
||||||
|
private val IMMEDIATE = Dispatchers.Main.immediate
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,10 +23,14 @@ import com.android.systemui.SysuiTestCase
|
|||||||
import com.android.systemui.animation.Expandable
|
import com.android.systemui.animation.Expandable
|
||||||
import com.android.systemui.common.shared.model.Icon
|
import com.android.systemui.common.shared.model.Icon
|
||||||
import com.android.systemui.doze.util.BurnInHelperWrapper
|
import com.android.systemui.doze.util.BurnInHelperWrapper
|
||||||
|
import com.android.systemui.flags.FakeFeatureFlags
|
||||||
|
import com.android.systemui.flags.Flags
|
||||||
import com.android.systemui.keyguard.data.quickaffordance.BuiltInKeyguardQuickAffordanceKeys
|
import com.android.systemui.keyguard.data.quickaffordance.BuiltInKeyguardQuickAffordanceKeys
|
||||||
import com.android.systemui.keyguard.data.quickaffordance.FakeKeyguardQuickAffordanceConfig
|
import com.android.systemui.keyguard.data.quickaffordance.FakeKeyguardQuickAffordanceConfig
|
||||||
import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceConfig
|
import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceConfig
|
||||||
|
import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceSelectionManager
|
||||||
import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
|
import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
|
||||||
|
import com.android.systemui.keyguard.data.repository.KeyguardQuickAffordanceRepository
|
||||||
import com.android.systemui.keyguard.domain.interactor.KeyguardBottomAreaInteractor
|
import com.android.systemui.keyguard.domain.interactor.KeyguardBottomAreaInteractor
|
||||||
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
|
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
|
||||||
import com.android.systemui.keyguard.domain.interactor.KeyguardQuickAffordanceInteractor
|
import com.android.systemui.keyguard.domain.interactor.KeyguardQuickAffordanceInteractor
|
||||||
@@ -41,6 +45,8 @@ import com.android.systemui.util.mockito.mock
|
|||||||
import com.google.common.truth.Truth.assertThat
|
import com.google.common.truth.Truth.assertThat
|
||||||
import kotlin.math.max
|
import kotlin.math.max
|
||||||
import kotlin.math.min
|
import kotlin.math.min
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.flow.launchIn
|
import kotlinx.coroutines.flow.launchIn
|
||||||
import kotlinx.coroutines.flow.onEach
|
import kotlinx.coroutines.flow.onEach
|
||||||
import kotlinx.coroutines.test.runBlockingTest
|
import kotlinx.coroutines.test.runBlockingTest
|
||||||
@@ -82,20 +88,13 @@ class KeyguardBottomAreaViewModelTest : SysuiTestCase() {
|
|||||||
.thenReturn(RETURNED_BURN_IN_OFFSET)
|
.thenReturn(RETURNED_BURN_IN_OFFSET)
|
||||||
|
|
||||||
homeControlsQuickAffordanceConfig =
|
homeControlsQuickAffordanceConfig =
|
||||||
object :
|
FakeKeyguardQuickAffordanceConfig(BuiltInKeyguardQuickAffordanceKeys.HOME_CONTROLS)
|
||||||
FakeKeyguardQuickAffordanceConfig(
|
|
||||||
BuiltInKeyguardQuickAffordanceKeys.HOME_CONTROLS
|
|
||||||
) {}
|
|
||||||
quickAccessWalletAffordanceConfig =
|
quickAccessWalletAffordanceConfig =
|
||||||
object :
|
FakeKeyguardQuickAffordanceConfig(
|
||||||
FakeKeyguardQuickAffordanceConfig(
|
BuiltInKeyguardQuickAffordanceKeys.QUICK_ACCESS_WALLET
|
||||||
BuiltInKeyguardQuickAffordanceKeys.QUICK_ACCESS_WALLET
|
)
|
||||||
) {}
|
|
||||||
qrCodeScannerAffordanceConfig =
|
qrCodeScannerAffordanceConfig =
|
||||||
object :
|
FakeKeyguardQuickAffordanceConfig(BuiltInKeyguardQuickAffordanceKeys.QR_CODE_SCANNER)
|
||||||
FakeKeyguardQuickAffordanceConfig(
|
|
||||||
BuiltInKeyguardQuickAffordanceKeys.QR_CODE_SCANNER
|
|
||||||
) {}
|
|
||||||
registry =
|
registry =
|
||||||
FakeKeyguardQuickAffordanceRegistry(
|
FakeKeyguardQuickAffordanceRegistry(
|
||||||
mapOf(
|
mapOf(
|
||||||
@@ -116,6 +115,18 @@ class KeyguardBottomAreaViewModelTest : SysuiTestCase() {
|
|||||||
whenever(userTracker.userHandle).thenReturn(mock())
|
whenever(userTracker.userHandle).thenReturn(mock())
|
||||||
whenever(lockPatternUtils.getStrongAuthForUser(anyInt()))
|
whenever(lockPatternUtils.getStrongAuthForUser(anyInt()))
|
||||||
.thenReturn(LockPatternUtils.StrongAuthTracker.STRONG_AUTH_NOT_REQUIRED)
|
.thenReturn(LockPatternUtils.StrongAuthTracker.STRONG_AUTH_NOT_REQUIRED)
|
||||||
|
val quickAffordanceRepository =
|
||||||
|
KeyguardQuickAffordanceRepository(
|
||||||
|
scope = CoroutineScope(IMMEDIATE),
|
||||||
|
backgroundDispatcher = IMMEDIATE,
|
||||||
|
selectionManager = KeyguardQuickAffordanceSelectionManager(),
|
||||||
|
configs =
|
||||||
|
setOf(
|
||||||
|
homeControlsQuickAffordanceConfig,
|
||||||
|
quickAccessWalletAffordanceConfig,
|
||||||
|
qrCodeScannerAffordanceConfig,
|
||||||
|
),
|
||||||
|
)
|
||||||
underTest =
|
underTest =
|
||||||
KeyguardBottomAreaViewModel(
|
KeyguardBottomAreaViewModel(
|
||||||
keyguardInteractor = keyguardInteractor,
|
keyguardInteractor = keyguardInteractor,
|
||||||
@@ -127,6 +138,11 @@ class KeyguardBottomAreaViewModelTest : SysuiTestCase() {
|
|||||||
keyguardStateController = keyguardStateController,
|
keyguardStateController = keyguardStateController,
|
||||||
userTracker = userTracker,
|
userTracker = userTracker,
|
||||||
activityStarter = activityStarter,
|
activityStarter = activityStarter,
|
||||||
|
featureFlags =
|
||||||
|
FakeFeatureFlags().apply {
|
||||||
|
set(Flags.CUSTOMIZABLE_LOCK_SCREEN_QUICK_AFFORDANCES, false)
|
||||||
|
},
|
||||||
|
repository = { quickAffordanceRepository },
|
||||||
),
|
),
|
||||||
bottomAreaInteractor = KeyguardBottomAreaInteractor(repository = repository),
|
bottomAreaInteractor = KeyguardBottomAreaInteractor(repository = repository),
|
||||||
burnInHelperWrapper = burnInHelperWrapper,
|
burnInHelperWrapper = burnInHelperWrapper,
|
||||||
@@ -576,5 +592,6 @@ class KeyguardBottomAreaViewModelTest : SysuiTestCase() {
|
|||||||
companion object {
|
companion object {
|
||||||
private const val DEFAULT_BURN_IN_OFFSET = 5
|
private const val DEFAULT_BURN_IN_OFFSET = 5
|
||||||
private const val RETURNED_BURN_IN_OFFSET = 3
|
private const val RETURNED_BURN_IN_OFFSET = 3
|
||||||
|
private val IMMEDIATE = Dispatchers.Main.immediate
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user