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