• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2022 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  *
16  */
17 
18 package com.android.systemui.keyguard.data.quickaffordance
19 
20 import android.app.AlertDialog
21 import android.content.Intent
22 import com.android.systemui.animation.Expandable
23 import com.android.systemui.common.shared.model.Icon
24 import com.android.systemui.keyguard.shared.quickaffordance.ActivationState
25 import com.android.systemui.shared.customization.data.content.CustomizationProviderContract as Contract
26 import kotlinx.coroutines.flow.Flow
27 
28 /** Defines interface that can act as data source for a single quick affordance model. */
29 interface KeyguardQuickAffordanceConfig {
30 
31     /** Unique identifier for this quick affordance. It must be globally unique. */
32     val key: String
33 
34     val pickerName: String
35 
36     val pickerIconResourceId: Int
37 
38     /**
39      * The ever-changing state of the affordance.
40      *
41      * Used to populate the lock screen.
42      */
43     val lockScreenState: Flow<LockScreenState>
44 
45     /**
46      * Returns the [PickerScreenState] representing the affordance in the settings or selector
47      * experience.
48      */
getPickerScreenStatenull49     suspend fun getPickerScreenState(): PickerScreenState = PickerScreenState.Default()
50 
51     /**
52      * Notifies that the affordance was clicked by the user.
53      *
54      * @param expandable An [Expandable] to use when animating dialogs or activities
55      * @return An [OnTriggeredResult] telling the caller what to do next
56      */
57     fun onTriggered(expandable: Expandable?): OnTriggeredResult
58 
59     /**
60      * Encapsulates the state of a quick affordance within the context of the settings or selector
61      * experience.
62      */
63     sealed class PickerScreenState {
64 
65         /** The picker shows the item for selecting this affordance as it normally would. */
66         data class Default(
67             /** Optional [Intent] to use to start an activity to configure this affordance. */
68             val configureIntent: Intent? = null,
69         ) : PickerScreenState()
70 
71         /**
72          * The picker does not show an item for selecting this affordance as it is not supported on
73          * the device at all. For example, missing hardware requirements.
74          */
75         object UnavailableOnDevice : PickerScreenState()
76 
77         /**
78          * The picker shows the item for selecting this affordance as disabled. Clicking on it will
79          * show the given instructions to the user. If [actionText] and [actionComponentName] are
80          * provided (optional) a button will be shown to open an activity to help the user complete
81          * the steps described in the instructions.
82          */
83         data class Disabled(
84             /** List of human-readable instructions for setting up the quick affordance. */
85             val instructions: List<String>,
86             /**
87              * Optional text to display on a button that the user can click to start a flow to go
88              * and set up the quick affordance and make it enabled.
89              */
90             val actionText: String? = null,
91             /**
92              * Optional component name to be able to build an `Intent` that opens an `Activity` for
93              * the user to be able to set up the quick affordance and make it enabled.
94              *
95              * This is either just an action for the `Intent` or a package name and action,
96              * separated by
97              * [Contract.LockScreenQuickAffordances.AffordanceTable.COMPONENT_NAME_SEPARATOR] for
98              * convenience, you can use the [componentName] function.
99              */
100             val actionComponentName: String? = null,
101         ) : PickerScreenState() {
102             init {
103                 check(instructions.isNotEmpty()) { "Instructions must not be empty!" }
104                 check(
105                     (actionText.isNullOrEmpty() && actionComponentName.isNullOrEmpty()) ||
106                         (!actionText.isNullOrEmpty() && !actionComponentName.isNullOrEmpty())
107                 ) {
108                     "actionText and actionComponentName must either both be null/empty or both be" +
109                         " non-empty!"
110                 }
111             }
112         }
113     }
114 
115     /**
116      * Encapsulates the state of a "quick affordance" in the keyguard bottom area (for example, a
117      * button on the lock-screen).
118      */
119     sealed class LockScreenState {
120 
121         /** No affordance should show up. */
122         object Hidden : LockScreenState()
123 
124         /** An affordance is visible. */
125         data class Visible(
126             /** An icon for the affordance. */
127             val icon: Icon,
128             /** The activation state of the affordance. */
129             val activationState: ActivationState = ActivationState.NotSupported,
130         ) : LockScreenState()
131     }
132 
133     sealed class OnTriggeredResult {
134         /**
135          * Returning this as a result from the [onTriggered] method means that the implementation
136          * has taken care of the action, the system will do nothing.
137          */
138         object Handled : OnTriggeredResult()
139 
140         /**
141          * Returning this as a result from the [onTriggered] method means that the implementation
142          * has _not_ taken care of the action and the system should start an activity using the
143          * given [Intent].
144          */
145         data class StartActivity(
146             val intent: Intent,
147             val canShowWhileLocked: Boolean,
148         ) : OnTriggeredResult()
149 
150         /**
151          * Returning this as a result from the [onTriggered] method means that the implementation
152          * has _not_ taken care of the action and the system should show a Dialog using the given
153          * [AlertDialog] and [Expandable].
154          */
155         data class ShowDialog(
156             val dialog: AlertDialog,
157             val expandable: Expandable?,
158         ) : OnTriggeredResult()
159     }
160 
161     companion object {
componentNamenull162         fun componentName(
163             packageName: String? = null,
164             action: String?,
165         ): String? {
166             return when {
167                 action.isNullOrEmpty() -> null
168                 !packageName.isNullOrEmpty() ->
169                     "$packageName${Contract.LockScreenQuickAffordances.AffordanceTable
170                         .COMPONENT_NAME_SEPARATOR}$action"
171                 else -> action
172             }
173         }
174     }
175 }
176