• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
<lambda>null2  * 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.content.Context
21 import android.content.Intent
22 import androidx.annotation.DrawableRes
23 import com.android.systemui.R
24 import com.android.systemui.animation.Expandable
25 import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
26 import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
27 import com.android.systemui.common.shared.model.ContentDescription
28 import com.android.systemui.common.shared.model.Icon
29 import com.android.systemui.controls.ControlsServiceInfo
30 import com.android.systemui.controls.controller.StructureInfo
31 import com.android.systemui.controls.dagger.ControlsComponent
32 import com.android.systemui.controls.management.ControlsListingController
33 import com.android.systemui.controls.ui.ControlsActivity
34 import com.android.systemui.controls.ui.ControlsUiController
35 import com.android.systemui.dagger.SysUISingleton
36 import com.android.systemui.dagger.qualifiers.Application
37 import com.android.systemui.util.kotlin.getOrNull
38 import javax.inject.Inject
39 import kotlinx.coroutines.channels.awaitClose
40 import kotlinx.coroutines.flow.Flow
41 import kotlinx.coroutines.flow.flatMapLatest
42 import kotlinx.coroutines.flow.flowOf
43 
44 /** Home controls quick affordance data source. */
45 @SysUISingleton
46 class HomeControlsKeyguardQuickAffordanceConfig
47 @Inject
48 constructor(
49     @Application private val context: Context,
50     private val component: ControlsComponent,
51 ) : KeyguardQuickAffordanceConfig {
52 
53     private val appContext = context.applicationContext
54 
55     override val key: String = BuiltInKeyguardQuickAffordanceKeys.HOME_CONTROLS
56 
57     override val pickerName: String by lazy { context.getString(component.getTileTitleId()) }
58 
59     override val pickerIconResourceId: Int by lazy { component.getTileImageId() }
60 
61     override val lockScreenState: Flow<KeyguardQuickAffordanceConfig.LockScreenState> =
62         component.canShowWhileLockedSetting.flatMapLatest { canShowWhileLocked ->
63             if (canShowWhileLocked) {
64                 stateInternal(component.getControlsListingController().getOrNull())
65             } else {
66                 flowOf(KeyguardQuickAffordanceConfig.LockScreenState.Hidden)
67             }
68         }
69 
70     override suspend fun getPickerScreenState(): KeyguardQuickAffordanceConfig.PickerScreenState {
71         if (!component.isEnabled()) {
72             return KeyguardQuickAffordanceConfig.PickerScreenState.UnavailableOnDevice
73         }
74 
75         val currentServices =
76             component.getControlsListingController().getOrNull()?.getCurrentServices()
77         val hasFavorites =
78             component.getControlsController().getOrNull()?.getFavorites()?.isNotEmpty() == true
79         if (currentServices.isNullOrEmpty() || !hasFavorites) {
80             return KeyguardQuickAffordanceConfig.PickerScreenState.Disabled(
81                 instructions =
82                     listOf(
83                         context.getString(
84                             R.string.keyguard_affordance_enablement_dialog_home_instruction_1
85                         ),
86                         context.getString(
87                             R.string.keyguard_affordance_enablement_dialog_home_instruction_2
88                         ),
89                     ),
90             )
91         }
92 
93         return KeyguardQuickAffordanceConfig.PickerScreenState.Default()
94     }
95 
96     override fun onTriggered(
97         expandable: Expandable?,
98     ): KeyguardQuickAffordanceConfig.OnTriggeredResult {
99         return KeyguardQuickAffordanceConfig.OnTriggeredResult.StartActivity(
100             intent =
101                 Intent(appContext, ControlsActivity::class.java)
102                     .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_NEW_TASK)
103                     .putExtra(
104                         ControlsUiController.EXTRA_ANIMATE,
105                         true,
106                     ),
107             canShowWhileLocked = component.canShowWhileLockedSetting.value,
108         )
109     }
110 
111     private fun stateInternal(
112         listingController: ControlsListingController?,
113     ): Flow<KeyguardQuickAffordanceConfig.LockScreenState> {
114         if (listingController == null) {
115             return flowOf(KeyguardQuickAffordanceConfig.LockScreenState.Hidden)
116         }
117 
118         return conflatedCallbackFlow {
119             val callback =
120                 object : ControlsListingController.ControlsListingCallback {
121                     override fun onServicesUpdated(serviceInfos: List<ControlsServiceInfo>) {
122                         val favorites: List<StructureInfo>? =
123                             component.getControlsController().getOrNull()?.getFavorites()
124 
125                         trySendWithFailureLogging(
126                             state(
127                                 isFeatureEnabled = component.isEnabled(),
128                                 hasFavorites = favorites?.isNotEmpty() == true,
129                                 hasPanels = serviceInfos.any { it.panelActivity != null },
130                                 hasServiceInfos = serviceInfos.isNotEmpty(),
131                                 iconResourceId = component.getTileImageId(),
132                                 visibility = component.getVisibility(),
133                             ),
134                             TAG,
135                         )
136                     }
137                 }
138 
139             listingController.addCallback(callback)
140 
141             awaitClose { listingController.removeCallback(callback) }
142         }
143     }
144 
145     private fun state(
146         isFeatureEnabled: Boolean,
147         hasFavorites: Boolean,
148         hasPanels: Boolean,
149         hasServiceInfos: Boolean,
150         visibility: ControlsComponent.Visibility,
151         @DrawableRes iconResourceId: Int?,
152     ): KeyguardQuickAffordanceConfig.LockScreenState {
153         return if (
154             isFeatureEnabled &&
155                 (hasFavorites || hasPanels) &&
156                 hasServiceInfos &&
157                 iconResourceId != null &&
158                 visibility == ControlsComponent.Visibility.AVAILABLE
159         ) {
160             KeyguardQuickAffordanceConfig.LockScreenState.Visible(
161                 icon =
162                     Icon.Resource(
163                         res = iconResourceId,
164                         contentDescription =
165                             ContentDescription.Resource(
166                                 res = component.getTileTitleId(),
167                             ),
168                     ),
169             )
170         } else {
171             KeyguardQuickAffordanceConfig.LockScreenState.Hidden
172         }
173     }
174 
175     companion object {
176         private const val TAG = "HomeControlsKeyguardQuickAffordanceConfig"
177     }
178 }
179