diff options
18 files changed, 467 insertions, 69 deletions
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index 9eafdb959f07..785a1d403934 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -2798,4 +2798,50 @@ <!-- Learn more URL for the log access confirmation dialog. [DO NOT TRANSLATE]--> <string name="log_access_confirmation_learn_more" translatable="false"><a href="https://support.google.com/android?p=system_logs#topic=7313011">Learn more</a></string> + + <!-- + Template for an action that opens a specific app. [CHAR LIMIT=16] + --> + <string name="keyguard_affordance_enablement_dialog_action_template">Open <xliff:g id="appName" example="Wallet">%1$s</xliff:g></string> + + <!-- + Template for a message shown right before a list of instructions that tell the user what to do + in order to enable a shortcut to a specific app. [CHAR LIMIT=NONE] + --> + <string name="keyguard_affordance_enablement_dialog_message">To add the <xliff:g id="appName" example="Wallet">%1$s</xliff:g> app as a shortcut, make sure</string> + + <!-- + Requirement for the wallet app to be available for the user to use. This is shown as part of a + bulleted list of requirements. When all requirements are met, the app can be accessed through a + shortcut button on the lock screen. [CHAR LIMIT=NONE]. + --> + <string name="keyguard_affordance_enablement_dialog_wallet_instruction_1">• The app is set up</string> + + <!-- + Requirement for the wallet app to be available for the user to use. This is shown as part of a + bulleted list of requirements. When all requirements are met, the app can be accessed through a + shortcut button on the lock screen. [CHAR LIMIT=NONE]. + --> + <string name="keyguard_affordance_enablement_dialog_wallet_instruction_2">• At least one card has been added to Wallet</string> + + <!-- + Requirement for the QR code scanner functionality to be available for the user to use. This is + shown as part of a bulleted list of requirements. When all requirements are met, the piece of + functionality can be accessed through a shortcut button on the lock screen. [CHAR LIMIT=NONE]. + --> + <string name="keyguard_affordance_enablement_dialog_qr_scanner_instruction">• Install a camera app</string> + + <!-- + Requirement for the home app to be available for the user to use. This is shown as part of a + bulleted list of requirements. When all requirements are met, the app can be accessed through a + shortcut button on the lock screen. [CHAR LIMIT=NONE]. + --> + <string name="keyguard_affordance_enablement_dialog_home_instruction_1">• The app is set up</string> + + <!-- + Requirement for the home app to be available for the user to use. This is shown as part of a + bulleted list of requirements. When all requirements are met, the app can be accessed through a + shortcut button on the lock screen. [CHAR LIMIT=NONE]. + --> + <string name="keyguard_affordance_enablement_dialog_home_instruction_2">• At least one device is available</string> </resources> diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/keyguard/data/content/KeyguardQuickAffordanceProviderContract.kt b/packages/SystemUI/shared/src/com/android/systemui/shared/keyguard/data/content/KeyguardQuickAffordanceProviderContract.kt index 71469a363f74..98d8d3eb9a4a 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/keyguard/data/content/KeyguardQuickAffordanceProviderContract.kt +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/keyguard/data/content/KeyguardQuickAffordanceProviderContract.kt @@ -67,6 +67,8 @@ object KeyguardQuickAffordanceProviderContract { object AffordanceTable { const val TABLE_NAME = "affordances" val URI: Uri = BASE_URI.buildUpon().path(TABLE_NAME).build() + const val ENABLEMENT_INSTRUCTIONS_DELIMITER = "][" + const val COMPONENT_NAME_SEPARATOR = "/" object Columns { /** String. Unique ID for this affordance. */ @@ -78,6 +80,25 @@ object KeyguardQuickAffordanceProviderContract { * ID from the system UI package. */ const val ICON = "icon" + /** Integer. `1` if the affordance is enabled or `0` if it disabled. */ + const val IS_ENABLED = "is_enabled" + /** + * String. List of strings, delimited by [ENABLEMENT_INSTRUCTIONS_DELIMITER] to be shown + * to the user if the affordance is disabled and the user selects the affordance. The + * first one is a title while the rest are the steps needed to re-enable the affordance. + */ + const val ENABLEMENT_INSTRUCTIONS = "enablement_instructions" + /** + * String. Optional label for a button that, when clicked, opens a destination activity + * where the user can re-enable the disabled affordance. + */ + const val ENABLEMENT_ACTION_TEXT = "enablement_action_text" + /** + * String. Optional package name and activity action string, delimited by + * [COMPONENT_NAME_SEPARATOR] to use with an `Intent` to start an activity that opens a + * destination where the user can re-enable the disabled affordance. + */ + const val ENABLEMENT_COMPONENT_NAME = "enablement_action_intent" } } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardQuickAffordanceProvider.kt b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardQuickAffordanceProvider.kt index bfc60c17225f..29febb6dd0d9 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardQuickAffordanceProvider.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardQuickAffordanceProvider.kt @@ -31,6 +31,7 @@ import com.android.systemui.SystemUIAppComponentFactoryBase.ContextAvailableCall import com.android.systemui.keyguard.domain.interactor.KeyguardQuickAffordanceInteractor import com.android.systemui.shared.keyguard.data.content.KeyguardQuickAffordanceProviderContract as Contract import javax.inject.Inject +import kotlinx.coroutines.runBlocking class KeyguardQuickAffordanceProvider : ContentProvider(), SystemUIAppComponentFactoryBase.ContextInitializer { @@ -118,9 +119,9 @@ class KeyguardQuickAffordanceProvider : sortOrder: String?, ): Cursor? { return when (uriMatcher.match(uri)) { - MATCH_CODE_ALL_AFFORDANCES -> queryAffordances() + MATCH_CODE_ALL_AFFORDANCES -> runBlocking { queryAffordances() } MATCH_CODE_ALL_SLOTS -> querySlots() - MATCH_CODE_ALL_SELECTIONS -> querySelections() + MATCH_CODE_ALL_SELECTIONS -> runBlocking { querySelections() } MATCH_CODE_ALL_FLAGS -> queryFlags() else -> null } @@ -194,7 +195,7 @@ class KeyguardQuickAffordanceProvider : } } - private fun querySelections(): Cursor { + private suspend fun querySelections(): Cursor { return MatrixCursor( arrayOf( Contract.SelectionTable.Columns.SLOT_ID, @@ -219,12 +220,16 @@ class KeyguardQuickAffordanceProvider : } } - private fun queryAffordances(): Cursor { + private suspend fun queryAffordances(): Cursor { return MatrixCursor( arrayOf( Contract.AffordanceTable.Columns.ID, Contract.AffordanceTable.Columns.NAME, Contract.AffordanceTable.Columns.ICON, + Contract.AffordanceTable.Columns.IS_ENABLED, + Contract.AffordanceTable.Columns.ENABLEMENT_INSTRUCTIONS, + Contract.AffordanceTable.Columns.ENABLEMENT_ACTION_TEXT, + Contract.AffordanceTable.Columns.ENABLEMENT_COMPONENT_NAME, ) ) .apply { @@ -234,6 +239,12 @@ class KeyguardQuickAffordanceProvider : representation.id, representation.name, representation.iconResourceId, + if (representation.isEnabled) 1 else 0, + representation.instructions?.joinToString( + Contract.AffordanceTable.ENABLEMENT_INSTRUCTIONS_DELIMITER + ), + representation.actionText, + representation.actionComponentName, ) ) } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/HomeControlsKeyguardQuickAffordanceConfig.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/HomeControlsKeyguardQuickAffordanceConfig.kt index d6f521c6dc87..2558fab216a0 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/HomeControlsKeyguardQuickAffordanceConfig.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/HomeControlsKeyguardQuickAffordanceConfig.kt @@ -20,6 +20,7 @@ package com.android.systemui.keyguard.data.quickaffordance import android.content.Context import android.content.Intent import androidx.annotation.DrawableRes +import com.android.systemui.R import com.android.systemui.animation.Expandable import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow @@ -45,7 +46,7 @@ import kotlinx.coroutines.flow.flowOf class HomeControlsKeyguardQuickAffordanceConfig @Inject constructor( - @Application context: Context, + @Application private val context: Context, private val component: ControlsComponent, ) : KeyguardQuickAffordanceConfig { @@ -66,6 +67,36 @@ constructor( } } + override suspend fun getPickerScreenState(): KeyguardQuickAffordanceConfig.PickerScreenState { + if (!component.isEnabled()) { + return KeyguardQuickAffordanceConfig.PickerScreenState.UnavailableOnDevice + } + + val currentServices = + component.getControlsListingController().getOrNull()?.getCurrentServices() + val hasFavorites = + component.getControlsController().getOrNull()?.getFavorites()?.isNotEmpty() == true + if (currentServices.isNullOrEmpty() || !hasFavorites) { + return KeyguardQuickAffordanceConfig.PickerScreenState.Disabled( + instructions = + listOf( + context.getString( + R.string.keyguard_affordance_enablement_dialog_message, + pickerName, + ), + context.getString( + R.string.keyguard_affordance_enablement_dialog_home_instruction_1 + ), + context.getString( + R.string.keyguard_affordance_enablement_dialog_home_instruction_2 + ), + ), + ) + } + + return KeyguardQuickAffordanceConfig.PickerScreenState.Default + } + override fun onTriggered( expandable: Expandable?, ): KeyguardQuickAffordanceConfig.OnTriggeredResult { diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceConfig.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceConfig.kt index fd40d1dd1a0e..4477310dca41 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceConfig.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceConfig.kt @@ -21,6 +21,7 @@ import android.content.Intent import com.android.systemui.animation.Expandable import com.android.systemui.common.shared.model.Icon import com.android.systemui.keyguard.shared.quickaffordance.ActivationState +import com.android.systemui.shared.keyguard.data.content.KeyguardQuickAffordanceProviderContract as Contract import kotlinx.coroutines.flow.Flow /** Defines interface that can act as data source for a single quick affordance model. */ @@ -41,6 +42,12 @@ interface KeyguardQuickAffordanceConfig { val lockScreenState: Flow<LockScreenState> /** + * Returns the [PickerScreenState] representing the affordance in the settings or selector + * experience. + */ + suspend fun getPickerScreenState(): PickerScreenState = PickerScreenState.Default + + /** * Notifies that the affordance was clicked by the user. * * @param expandable An [Expandable] to use when animating dialogs or activities @@ -49,6 +56,58 @@ interface KeyguardQuickAffordanceConfig { fun onTriggered(expandable: Expandable?): OnTriggeredResult /** + * Encapsulates the state of a quick affordance within the context of the settings or selector + * experience. + */ + sealed class PickerScreenState { + + /** The picker shows the item for selecting this affordance as it normally would. */ + object Default : PickerScreenState() + + /** + * The picker does not show an item for selecting this affordance as it is not supported on + * the device at all. For example, missing hardware requirements. + */ + object UnavailableOnDevice : PickerScreenState() + + /** + * The picker shows the item for selecting this affordance as disabled. Clicking on it will + * show the given instructions to the user. If [actionText] and [actionComponentName] are + * provided (optional) a button will be shown to open an activity to help the user complete + * the steps described in the instructions. + */ + data class Disabled( + /** List of human-readable instructions for setting up the quick affordance. */ + val instructions: List<String>, + /** + * Optional text to display on a button that the user can click to start a flow to go + * and set up the quick affordance and make it enabled. + */ + val actionText: String? = null, + /** + * Optional component name to be able to build an `Intent` that opens an `Activity` for + * the user to be able to set up the quick affordance and make it enabled. + * + * This is either just an action for the `Intent` or a package name and action, + * separated by [Contract.AffordanceTable.COMPONENT_NAME_SEPARATOR] for convenience, you + * can use the [componentName] function. + */ + val actionComponentName: String? = null, + ) : PickerScreenState() { + init { + check(instructions.isNotEmpty()) { "Instructions must not be empty!" } + check( + (actionText.isNullOrEmpty() && actionComponentName.isNullOrEmpty()) || + (!actionText.isNullOrEmpty() && !actionComponentName.isNullOrEmpty()) + ) { + "actionText and actionComponentName must either both be null/empty or both be" + + " non-empty!" + } + } + } + } + + /** * Encapsulates the state of a "quick affordance" in the keyguard bottom area (for example, a * button on the lock-screen). */ @@ -83,4 +142,18 @@ interface KeyguardQuickAffordanceConfig { val canShowWhileLocked: Boolean, ) : OnTriggeredResult() } + + companion object { + fun componentName( + packageName: String? = null, + action: String?, + ): String? { + return when { + action.isNullOrEmpty() -> null + !packageName.isNullOrEmpty() -> + "$packageName${Contract.AffordanceTable.COMPONENT_NAME_SEPARATOR}$action" + else -> action + } + } + } } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/QrCodeScannerKeyguardQuickAffordanceConfig.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/QrCodeScannerKeyguardQuickAffordanceConfig.kt index 11f72ffa4757..a96ce77ee15c 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/QrCodeScannerKeyguardQuickAffordanceConfig.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/QrCodeScannerKeyguardQuickAffordanceConfig.kt @@ -36,7 +36,7 @@ import kotlinx.coroutines.flow.Flow class QrCodeScannerKeyguardQuickAffordanceConfig @Inject constructor( - @Application context: Context, + @Application private val context: Context, private val controller: QRCodeScannerController, ) : KeyguardQuickAffordanceConfig { @@ -75,6 +75,28 @@ constructor( } } + override suspend fun getPickerScreenState(): KeyguardQuickAffordanceConfig.PickerScreenState { + return when { + !controller.isAvailableOnDevice -> + KeyguardQuickAffordanceConfig.PickerScreenState.UnavailableOnDevice + !controller.isAbleToOpenCameraApp -> + KeyguardQuickAffordanceConfig.PickerScreenState.Disabled( + instructions = + listOf( + context.getString( + R.string.keyguard_affordance_enablement_dialog_message, + pickerName, + ), + context.getString( + R.string + .keyguard_affordance_enablement_dialog_qr_scanner_instruction + ), + ), + ) + else -> KeyguardQuickAffordanceConfig.PickerScreenState.Default + } + } + override fun onTriggered( expandable: Expandable?, ): KeyguardQuickAffordanceConfig.OnTriggeredResult { diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfig.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfig.kt index 303e6a1a95cc..beb20ce4540c 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfig.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfig.kt @@ -18,10 +18,12 @@ package com.android.systemui.keyguard.data.quickaffordance import android.content.Context +import android.content.Intent import android.graphics.drawable.Drawable import android.service.quickaccesswallet.GetWalletCardsError import android.service.quickaccesswallet.GetWalletCardsResponse import android.service.quickaccesswallet.QuickAccessWalletClient +import android.service.quickaccesswallet.WalletCard import android.util.Log import com.android.systemui.R import com.android.systemui.animation.Expandable @@ -31,25 +33,27 @@ import com.android.systemui.common.shared.model.ContentDescription import com.android.systemui.common.shared.model.Icon import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application +import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceConfig.Companion.componentName import com.android.systemui.plugins.ActivityStarter import com.android.systemui.wallet.controller.QuickAccessWalletController import javax.inject.Inject import kotlinx.coroutines.channels.awaitClose import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.suspendCancellableCoroutine /** Quick access wallet quick affordance data source. */ @SysUISingleton class QuickAccessWalletKeyguardQuickAffordanceConfig @Inject constructor( - @Application context: Context, + @Application private val context: Context, private val walletController: QuickAccessWalletController, private val activityStarter: ActivityStarter, ) : KeyguardQuickAffordanceConfig { override val key: String = BuiltInKeyguardQuickAffordanceKeys.QUICK_ACCESS_WALLET - override val pickerName = context.getString(R.string.accessibility_wallet_button) + override val pickerName: String = context.getString(R.string.accessibility_wallet_button) override val pickerIconResourceId = R.drawable.ic_wallet_lockscreen @@ -58,10 +62,11 @@ constructor( val callback = object : QuickAccessWalletClient.OnWalletCardsRetrievedCallback { override fun onWalletCardsRetrieved(response: GetWalletCardsResponse?) { + val hasCards = response?.walletCards?.isNotEmpty() == true trySendWithFailureLogging( state( isFeatureEnabled = walletController.isWalletEnabled, - hasCard = response?.walletCards?.isNotEmpty() == true, + hasCard = hasCards, tileIcon = walletController.walletClient.tileIcon, ), TAG, @@ -93,6 +98,44 @@ constructor( } } + override suspend fun getPickerScreenState(): KeyguardQuickAffordanceConfig.PickerScreenState { + return when { + !walletController.isWalletEnabled -> + KeyguardQuickAffordanceConfig.PickerScreenState.UnavailableOnDevice + walletController.walletClient.tileIcon == null || queryCards().isEmpty() -> { + val componentName = + walletController.walletClient.createWalletSettingsIntent().toComponentName() + val actionText = + if (componentName != null) { + context.getString( + R.string.keyguard_affordance_enablement_dialog_action_template, + pickerName, + ) + } else { + null + } + KeyguardQuickAffordanceConfig.PickerScreenState.Disabled( + instructions = + listOf( + context.getString( + R.string.keyguard_affordance_enablement_dialog_message, + pickerName, + ), + context.getString( + R.string.keyguard_affordance_enablement_dialog_wallet_instruction_1 + ), + context.getString( + R.string.keyguard_affordance_enablement_dialog_wallet_instruction_2 + ), + ), + actionText = actionText, + actionComponentName = componentName, + ) + } + else -> KeyguardQuickAffordanceConfig.PickerScreenState.Default + } + } + override fun onTriggered( expandable: Expandable?, ): KeyguardQuickAffordanceConfig.OnTriggeredResult { @@ -104,6 +147,24 @@ constructor( return KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled } + private suspend fun queryCards(): List<WalletCard> { + return suspendCancellableCoroutine { continuation -> + val callback = + object : QuickAccessWalletClient.OnWalletCardsRetrievedCallback { + override fun onWalletCardsRetrieved(response: GetWalletCardsResponse?) { + continuation.resumeWith( + Result.success(response?.walletCards ?: emptyList()) + ) + } + + override fun onWalletCardRetrievalError(error: GetWalletCardsError?) { + continuation.resumeWith(Result.success(emptyList())) + } + } + walletController.queryWalletCards(callback) + } + } + private fun state( isFeatureEnabled: Boolean, hasCard: Boolean, @@ -125,6 +186,14 @@ constructor( } } + private fun Intent?.toComponentName(): String? { + if (this == null) { + return null + } + + return componentName(packageName = `package`, action = action) + } + companion object { private const val TAG = "QuickAccessWalletKeyguardQuickAffordanceConfig" } 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 533b3abf4fb6..d30050090617 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 @@ -121,18 +121,32 @@ constructor( } /** - * 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. + * Returns the list of representation objects for all known, device-available 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, - ) - } + suspend fun getAffordancePickerRepresentations(): + List<KeyguardQuickAffordancePickerRepresentation> { + return configs + .associateWith { config -> config.getPickerScreenState() } + .filterNot { (_, pickerState) -> + pickerState is KeyguardQuickAffordanceConfig.PickerScreenState.UnavailableOnDevice + } + .map { (config, pickerState) -> + val disabledPickerState = + pickerState as? KeyguardQuickAffordanceConfig.PickerScreenState.Disabled + KeyguardQuickAffordancePickerRepresentation( + id = config.key, + name = config.pickerName, + iconResourceId = config.pickerIconResourceId, + isEnabled = + pickerState is KeyguardQuickAffordanceConfig.PickerScreenState.Default, + instructions = disabledPickerState?.instructions, + actionText = disabledPickerState?.actionText, + actionComponentName = disabledPickerState?.actionComponentName, + ) + } } /** 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 c8216c5d72d7..2d94d760cb54 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 @@ -189,7 +189,7 @@ constructor( } /** Returns affordance IDs indexed by slot ID, for all known slots. */ - fun getSelections(): Map<String, List<KeyguardQuickAffordancePickerRepresentation>> { + suspend fun getSelections(): Map<String, List<KeyguardQuickAffordancePickerRepresentation>> { check(isUsingRepository) val slots = repository.get().getSlotPickerRepresentations() @@ -310,7 +310,8 @@ constructor( return Pair(splitUp[0], splitUp[1]) } - fun getAffordancePickerRepresentations(): List<KeyguardQuickAffordancePickerRepresentation> { + suspend fun getAffordancePickerRepresentations(): + List<KeyguardQuickAffordancePickerRepresentation> { check(isUsingRepository) return repository.get().getAffordancePickerRepresentations() diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/KeyguardQuickAffordancePickerRepresentation.kt b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/KeyguardQuickAffordancePickerRepresentation.kt index a56bc900f936..7d133598e105 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/KeyguardQuickAffordancePickerRepresentation.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/KeyguardQuickAffordancePickerRepresentation.kt @@ -27,4 +27,22 @@ data class KeyguardQuickAffordancePickerRepresentation( val id: String, val name: String, @DrawableRes val iconResourceId: Int, + + /** Whether this quick affordance is enabled. */ + val isEnabled: Boolean = true, + + /** If not enabled, the list of user-visible steps to re-enable it. */ + val instructions: List<String>? = null, + + /** + * If not enabled, an optional label for a button that takes the user to a destination where + * they can re-enable it. + */ + val actionText: String? = null, + + /** + * If not enabled, an optional component name (package and action) for a button that takes the + * user to a destination where they can re-enable it. + */ + val actionComponentName: String? = null, ) diff --git a/packages/SystemUI/src/com/android/systemui/qrcodescanner/controller/QRCodeScannerController.java b/packages/SystemUI/src/com/android/systemui/qrcodescanner/controller/QRCodeScannerController.java index 2c20feb19342..fa3f878ff431 100644 --- a/packages/SystemUI/src/com/android/systemui/qrcodescanner/controller/QRCodeScannerController.java +++ b/packages/SystemUI/src/com/android/systemui/qrcodescanner/controller/QRCodeScannerController.java @@ -158,14 +158,18 @@ public class QRCodeScannerController implements * Returns true if lock screen entry point for QR Code Scanner is to be enabled. */ public boolean isEnabledForLockScreenButton() { - return mQRCodeScannerEnabled && mIntent != null && mConfigEnableLockScreenButton - && isActivityCallable(mIntent); + return mQRCodeScannerEnabled && isAbleToOpenCameraApp() && isAvailableOnDevice(); + } + + /** Returns whether the feature is available on the device. */ + public boolean isAvailableOnDevice() { + return mConfigEnableLockScreenButton; } /** - * Returns true if quick settings entry point for QR Code Scanner is to be enabled. + * Returns true if the feature can open a camera app on the device. */ - public boolean isEnabledForQuickSettings() { + public boolean isAbleToOpenCameraApp() { return mIntent != null && isActivityCallable(mIntent); } diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/QRCodeScannerTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/QRCodeScannerTile.java index 376d3d8da8e7..1a24af10ab08 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/QRCodeScannerTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/QRCodeScannerTile.java @@ -115,7 +115,7 @@ public class QRCodeScannerTile extends QSTileImpl<QSTile.State> { state.label = mContext.getString(R.string.qr_code_scanner_title); state.contentDescription = state.label; state.icon = ResourceIcon.get(R.drawable.ic_qr_code_scanner); - state.state = mQRCodeScannerController.isEnabledForQuickSettings() ? Tile.STATE_INACTIVE + state.state = mQRCodeScannerController.isAbleToOpenCameraApp() ? Tile.STATE_INACTIVE : Tile.STATE_UNAVAILABLE; } diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/HomeControlsKeyguardQuickAffordanceConfigParameterizedStateTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/HomeControlsKeyguardQuickAffordanceConfigParameterizedStateTest.kt index c94cec6e313a..322014a61a73 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/HomeControlsKeyguardQuickAffordanceConfigParameterizedStateTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/HomeControlsKeyguardQuickAffordanceConfigParameterizedStateTest.kt @@ -24,8 +24,9 @@ import com.android.systemui.controls.controller.ControlsController import com.android.systemui.controls.dagger.ControlsComponent import com.android.systemui.controls.management.ControlsListingController import com.android.systemui.util.mockito.mock +import com.android.systemui.util.mockito.whenever import com.google.common.truth.Truth.assertThat -import java.util.Optional +import java.util.* import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach @@ -40,7 +41,6 @@ import org.mockito.ArgumentCaptor import org.mockito.Captor import org.mockito.Mock import org.mockito.Mockito.verify -import org.mockito.Mockito.`when` as whenever import org.mockito.MockitoAnnotations @SmallTest @@ -93,6 +93,14 @@ class HomeControlsKeyguardQuickAffordanceConfigParameterizedStateTest : SysuiTes whenever(component.getControlsController()).thenReturn(Optional.of(controlsController)) whenever(component.getControlsListingController()) .thenReturn(Optional.of(controlsListingController)) + whenever(controlsListingController.getCurrentServices()) + .thenReturn( + if (hasServiceInfos) { + listOf(mock(), mock()) + } else { + emptyList() + } + ) whenever(component.canShowWhileLockedSetting) .thenReturn(MutableStateFlow(canShowWhileLocked)) whenever(component.getVisibility()) @@ -144,6 +152,17 @@ class HomeControlsKeyguardQuickAffordanceConfigParameterizedStateTest : SysuiTes KeyguardQuickAffordanceConfig.LockScreenState.Hidden::class.java } ) + assertThat(underTest.getPickerScreenState()) + .isInstanceOf( + when { + !isFeatureEnabled -> + KeyguardQuickAffordanceConfig.PickerScreenState.UnavailableOnDevice::class + .java + hasServiceInfos && hasFavorites -> + KeyguardQuickAffordanceConfig.PickerScreenState.Default::class.java + else -> KeyguardQuickAffordanceConfig.PickerScreenState.Disabled::class.java + } + ) job.cancel() } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/QrCodeScannerKeyguardQuickAffordanceConfigTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/QrCodeScannerKeyguardQuickAffordanceConfigTest.kt index 2bd8e9aabab3..6255980601ac 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/QrCodeScannerKeyguardQuickAffordanceConfigTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/QrCodeScannerKeyguardQuickAffordanceConfigTest.kt @@ -24,17 +24,18 @@ import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanc import com.android.systemui.qrcodescanner.controller.QRCodeScannerController import com.android.systemui.util.mockito.argumentCaptor import com.android.systemui.util.mockito.mock +import com.android.systemui.util.mockito.whenever import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.test.runBlockingTest +import kotlinx.coroutines.test.runTest import org.junit.Before import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.JUnit4 import org.mockito.Mock import org.mockito.Mockito.verify -import org.mockito.Mockito.`when` as whenever import org.mockito.MockitoAnnotations @SmallTest @@ -134,6 +135,33 @@ class QrCodeScannerKeyguardQuickAffordanceConfigTest : SysuiTestCase() { ) } + @Test + fun `getPickerScreenState - enabled if configured on device - can open camera`() = runTest { + whenever(controller.isAvailableOnDevice).thenReturn(true) + whenever(controller.isAbleToOpenCameraApp).thenReturn(true) + + assertThat(underTest.getPickerScreenState()) + .isEqualTo(KeyguardQuickAffordanceConfig.PickerScreenState.Default) + } + + @Test + fun `getPickerScreenState - disabled if configured on device - cannot open camera`() = runTest { + whenever(controller.isAvailableOnDevice).thenReturn(true) + whenever(controller.isAbleToOpenCameraApp).thenReturn(false) + + assertThat(underTest.getPickerScreenState()) + .isInstanceOf(KeyguardQuickAffordanceConfig.PickerScreenState.Disabled::class.java) + } + + @Test + fun `getPickerScreenState - unavailable if not configured on device`() = runTest { + whenever(controller.isAvailableOnDevice).thenReturn(false) + whenever(controller.isAbleToOpenCameraApp).thenReturn(true) + + assertThat(underTest.getPickerScreenState()) + .isEqualTo(KeyguardQuickAffordanceConfig.PickerScreenState.UnavailableOnDevice) + } + private fun assertVisibleState(latest: KeyguardQuickAffordanceConfig.LockScreenState?) { assertThat(latest) .isInstanceOf(KeyguardQuickAffordanceConfig.LockScreenState.Visible::class.java) diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfigTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfigTest.kt index 5178154bdeee..d875dd94da3e 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfigTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfigTest.kt @@ -33,9 +33,11 @@ import com.android.systemui.util.mockito.mock import com.android.systemui.util.mockito.whenever import com.android.systemui.wallet.controller.QuickAccessWalletController import com.google.common.truth.Truth.assertThat +import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.test.runBlockingTest +import kotlinx.coroutines.test.runTest import org.junit.Before import org.junit.Test import org.junit.runner.RunWith @@ -44,6 +46,7 @@ import org.mockito.Mock import org.mockito.Mockito.verify import org.mockito.MockitoAnnotations +@OptIn(ExperimentalCoroutinesApi::class) @SmallTest @RunWith(JUnit4::class) class QuickAccessWalletKeyguardQuickAffordanceConfigTest : SysuiTestCase() { @@ -59,7 +62,7 @@ class QuickAccessWalletKeyguardQuickAffordanceConfigTest : SysuiTestCase() { underTest = QuickAccessWalletKeyguardQuickAffordanceConfig( - mock(), + context, walletController, activityStarter, ) @@ -151,6 +154,44 @@ class QuickAccessWalletKeyguardQuickAffordanceConfigTest : SysuiTestCase() { ) } + @Test + fun `getPickerScreenState - default`() = runTest { + setUpState() + + assertThat(underTest.getPickerScreenState()) + .isEqualTo(KeyguardQuickAffordanceConfig.PickerScreenState.Default) + } + + @Test + fun `getPickerScreenState - unavailable`() = runTest { + setUpState( + isWalletEnabled = false, + ) + + assertThat(underTest.getPickerScreenState()) + .isEqualTo(KeyguardQuickAffordanceConfig.PickerScreenState.UnavailableOnDevice) + } + + @Test + fun `getPickerScreenState - disabled when there is no icon`() = runTest { + setUpState( + hasWalletIcon = false, + ) + + assertThat(underTest.getPickerScreenState()) + .isInstanceOf(KeyguardQuickAffordanceConfig.PickerScreenState.Disabled::class.java) + } + + @Test + fun `getPickerScreenState - disabled when there is no card`() = runTest { + setUpState( + hasSelectedCard = false, + ) + + assertThat(underTest.getPickerScreenState()) + .isInstanceOf(KeyguardQuickAffordanceConfig.PickerScreenState.Disabled::class.java) + } + private fun setUpState( isWalletEnabled: Boolean = true, isWalletQuerySuccessful: Boolean = true, diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardQuickAffordanceRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardQuickAffordanceRepositoryTest.kt index d8a360567a07..bfd5190e2fa1 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardQuickAffordanceRepositoryTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardQuickAffordanceRepositoryTest.kt @@ -133,23 +133,24 @@ class KeyguardQuickAffordanceRepositoryTest : SysuiTestCase() { } @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, - ), + fun getAffordancePickerRepresentations() = + runBlocking(IMMEDIATE) { + 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() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/qrcodescanner/controller/QRCodeScannerControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/qrcodescanner/controller/QRCodeScannerControllerTest.java index 346d1e60fcf9..65210d63e5c5 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qrcodescanner/controller/QRCodeScannerControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/qrcodescanner/controller/QRCodeScannerControllerTest.java @@ -31,7 +31,6 @@ import static org.mockito.Mockito.when; import android.content.Intent; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; -import android.content.res.Resources; import android.os.UserHandle; import android.provider.DeviceConfig; import android.provider.Settings; @@ -133,7 +132,7 @@ public class QRCodeScannerControllerTest extends SysuiTestCase { /* enableOnLockScreen */ true); verifyActivityDetails(null); assertThat(mController.isEnabledForLockScreenButton()).isFalse(); - assertThat(mController.isEnabledForQuickSettings()).isFalse(); + assertThat(mController.isAbleToOpenCameraApp()).isFalse(); } @Test @@ -152,7 +151,7 @@ public class QRCodeScannerControllerTest extends SysuiTestCase { /* enableOnLockScreen */ true); verifyActivityDetails("abc/.def"); assertThat(mController.isEnabledForLockScreenButton()).isTrue(); - assertThat(mController.isEnabledForQuickSettings()).isTrue(); + assertThat(mController.isAbleToOpenCameraApp()).isTrue(); } @Test @@ -162,7 +161,7 @@ public class QRCodeScannerControllerTest extends SysuiTestCase { /* enableOnLockScreen */ true); verifyActivityDetails("abc/.def"); assertThat(mController.isEnabledForLockScreenButton()).isTrue(); - assertThat(mController.isEnabledForQuickSettings()).isTrue(); + assertThat(mController.isAbleToOpenCameraApp()).isTrue(); } @Test @@ -172,7 +171,7 @@ public class QRCodeScannerControllerTest extends SysuiTestCase { /* enableOnLockScreen */ true); verifyActivityDetails("abc/.def"); assertThat(mController.isEnabledForLockScreenButton()).isTrue(); - assertThat(mController.isEnabledForQuickSettings()).isTrue(); + assertThat(mController.isAbleToOpenCameraApp()).isTrue(); } @Test @@ -182,7 +181,7 @@ public class QRCodeScannerControllerTest extends SysuiTestCase { /* enableOnLockScreen */ true); verifyActivityDetails("abc/abc.def"); assertThat(mController.isEnabledForLockScreenButton()).isTrue(); - assertThat(mController.isEnabledForQuickSettings()).isTrue(); + assertThat(mController.isAbleToOpenCameraApp()).isTrue(); } @Test @@ -192,7 +191,7 @@ public class QRCodeScannerControllerTest extends SysuiTestCase { /* enableOnLockScreen */ true); verifyActivityDetails(null); assertThat(mController.isEnabledForLockScreenButton()).isFalse(); - assertThat(mController.isEnabledForQuickSettings()).isFalse(); + assertThat(mController.isAbleToOpenCameraApp()).isFalse(); } @Test @@ -202,21 +201,21 @@ public class QRCodeScannerControllerTest extends SysuiTestCase { /* enableOnLockScreen */ true); verifyActivityDetails("abc/.def"); assertThat(mController.isEnabledForLockScreenButton()).isTrue(); - assertThat(mController.isEnabledForQuickSettings()).isTrue(); + assertThat(mController.isAbleToOpenCameraApp()).isTrue(); mProxyFake.setProperty(DeviceConfig.NAMESPACE_SYSTEMUI, SystemUiDeviceConfigFlags.DEFAULT_QR_CODE_SCANNER, "def/.ijk", false); verifyActivityDetails("def/.ijk"); assertThat(mController.isEnabledForLockScreenButton()).isTrue(); - assertThat(mController.isEnabledForQuickSettings()).isTrue(); + assertThat(mController.isAbleToOpenCameraApp()).isTrue(); mProxyFake.setProperty(DeviceConfig.NAMESPACE_SYSTEMUI, SystemUiDeviceConfigFlags.DEFAULT_QR_CODE_SCANNER, null, false); verifyActivityDetails("abc/.def"); assertThat(mController.isEnabledForLockScreenButton()).isTrue(); - assertThat(mController.isEnabledForQuickSettings()).isTrue(); + assertThat(mController.isAbleToOpenCameraApp()).isTrue(); // Once from setup + twice from this function verify(mCallback, times(3)).onQRCodeScannerActivityChanged(); @@ -229,7 +228,7 @@ public class QRCodeScannerControllerTest extends SysuiTestCase { /* enableOnLockScreen */ true); verifyActivityDetails(null); assertThat(mController.isEnabledForLockScreenButton()).isFalse(); - assertThat(mController.isEnabledForQuickSettings()).isFalse(); + assertThat(mController.isAbleToOpenCameraApp()).isFalse(); mProxyFake.setProperty(DeviceConfig.NAMESPACE_SYSTEMUI, SystemUiDeviceConfigFlags.DEFAULT_QR_CODE_SCANNER, @@ -237,14 +236,14 @@ public class QRCodeScannerControllerTest extends SysuiTestCase { verifyActivityDetails("def/.ijk"); assertThat(mController.isEnabledForLockScreenButton()).isTrue(); - assertThat(mController.isEnabledForQuickSettings()).isTrue(); + assertThat(mController.isAbleToOpenCameraApp()).isTrue(); mProxyFake.setProperty(DeviceConfig.NAMESPACE_SYSTEMUI, SystemUiDeviceConfigFlags.DEFAULT_QR_CODE_SCANNER, null, false); verifyActivityDetails(null); assertThat(mController.isEnabledForLockScreenButton()).isFalse(); - assertThat(mController.isEnabledForQuickSettings()).isFalse(); + assertThat(mController.isAbleToOpenCameraApp()).isFalse(); verify(mCallback, times(2)).onQRCodeScannerActivityChanged(); } @@ -296,19 +295,19 @@ public class QRCodeScannerControllerTest extends SysuiTestCase { /* enableOnLockScreen */ true); verifyActivityDetails("abc/.def"); assertThat(mController.isEnabledForLockScreenButton()).isTrue(); - assertThat(mController.isEnabledForQuickSettings()).isTrue(); + assertThat(mController.isAbleToOpenCameraApp()).isTrue(); mSecureSettings.putStringForUser(LOCK_SCREEN_SHOW_QR_CODE_SCANNER, "0", UserHandle.USER_CURRENT); verifyActivityDetails("abc/.def"); assertThat(mController.isEnabledForLockScreenButton()).isFalse(); - assertThat(mController.isEnabledForQuickSettings()).isTrue(); + assertThat(mController.isAbleToOpenCameraApp()).isTrue(); mSecureSettings.putStringForUser(LOCK_SCREEN_SHOW_QR_CODE_SCANNER, "1", UserHandle.USER_CURRENT); verifyActivityDetails("abc/.def"); assertThat(mController.isEnabledForLockScreenButton()).isTrue(); - assertThat(mController.isEnabledForQuickSettings()).isTrue(); + assertThat(mController.isAbleToOpenCameraApp()).isTrue(); // Once from setup + twice from this function verify(mCallback, times(3)).onQRCodeScannerPreferenceChanged(); } @@ -320,13 +319,13 @@ public class QRCodeScannerControllerTest extends SysuiTestCase { /* enableOnLockScreen */ true); verifyActivityDetails("abc/.def"); assertThat(mController.isEnabledForLockScreenButton()).isTrue(); - assertThat(mController.isEnabledForQuickSettings()).isTrue(); + assertThat(mController.isAbleToOpenCameraApp()).isTrue(); mController.unregisterQRCodeScannerChangeObservers(DEFAULT_QR_CODE_SCANNER_CHANGE, QR_CODE_SCANNER_PREFERENCE_CHANGE); verifyActivityDetails(null); assertThat(mController.isEnabledForLockScreenButton()).isFalse(); - assertThat(mController.isEnabledForQuickSettings()).isFalse(); + assertThat(mController.isAbleToOpenCameraApp()).isFalse(); // Unregister once again and make sure it affects the next register event mController.unregisterQRCodeScannerChangeObservers(DEFAULT_QR_CODE_SCANNER_CHANGE, @@ -335,7 +334,7 @@ public class QRCodeScannerControllerTest extends SysuiTestCase { QR_CODE_SCANNER_PREFERENCE_CHANGE); verifyActivityDetails("abc/.def"); assertThat(mController.isEnabledForLockScreenButton()).isTrue(); - assertThat(mController.isEnabledForQuickSettings()).isTrue(); + assertThat(mController.isAbleToOpenCameraApp()).isTrue(); } @Test @@ -345,7 +344,7 @@ public class QRCodeScannerControllerTest extends SysuiTestCase { /* enableOnLockScreen */ false); assertThat(mController.getIntent()).isNotNull(); assertThat(mController.isEnabledForLockScreenButton()).isFalse(); - assertThat(mController.isEnabledForQuickSettings()).isTrue(); + assertThat(mController.isAbleToOpenCameraApp()).isTrue(); assertThat(getSettingsQRCodeDefaultComponent()).isNull(); } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/QRCodeScannerTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/QRCodeScannerTileTest.java index cac90a14096e..6a3f785e1d52 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/QRCodeScannerTileTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/QRCodeScannerTileTest.java @@ -108,7 +108,7 @@ public class QRCodeScannerTileTest extends SysuiTestCase { @Test public void testQRCodeTileUnavailable() { - when(mController.isEnabledForQuickSettings()).thenReturn(false); + when(mController.isAbleToOpenCameraApp()).thenReturn(false); QSTile.State state = new QSTile.State(); mTile.handleUpdateState(state, null); assertEquals(state.state, Tile.STATE_UNAVAILABLE); @@ -116,7 +116,7 @@ public class QRCodeScannerTileTest extends SysuiTestCase { @Test public void testQRCodeTileAvailable() { - when(mController.isEnabledForQuickSettings()).thenReturn(true); + when(mController.isAbleToOpenCameraApp()).thenReturn(true); QSTile.State state = new QSTile.State(); mTile.handleUpdateState(state, null); assertEquals(state.state, Tile.STATE_INACTIVE); |