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