Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 4459e7dc authored by Alejandro Nijamkin's avatar Alejandro Nijamkin Committed by Automerger Merge Worker
Browse files

Support for unavailable and disabled affordances. am: af8fd1b3

parents 275ecf9c af8fd1b3
Loading
Loading
Loading
Loading
+46 −0
Original line number Diff line number Diff line
@@ -2679,4 +2679,50 @@

    <!-- Time format for the Dream Time Complication for 24-hour time format [CHAR LIMIT=NONE] -->
    <string name="dream_time_complication_24_hr_time_format">kk:mm</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">&#8226; 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">&#8226; 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">&#8226; 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">&#8226; 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">&#8226; At least one device is available</string>
</resources>
+21 −0
Original line number Diff line number Diff line
@@ -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"
        }
    }

+15 −4
Original line number Diff line number Diff line
@@ -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,
                        )
                    )
                }
+32 −1
Original line number Diff line number Diff line
@@ -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 {
+73 −0
Original line number Diff line number Diff line
@@ -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. */
@@ -40,6 +41,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.
     *
@@ -48,6 +55,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
            }
        }
    }
}
Loading