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 package com.android.systemui.keyguard.data.quickaffordance 18 19 import android.content.Context 20 import android.content.Intent 21 import android.net.Uri 22 import android.provider.Settings 23 import android.provider.Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS 24 import android.provider.Settings.Global.ZEN_MODE_OFF 25 import android.provider.Settings.Secure.ZEN_DURATION_FOREVER 26 import android.provider.Settings.Secure.ZEN_DURATION_PROMPT 27 import android.service.notification.ZenModeConfig 28 import com.android.settingslib.notification.EnableZenModeDialog 29 import com.android.settingslib.notification.ZenModeDialogMetricsLogger 30 import com.android.systemui.res.R 31 import com.android.systemui.animation.Expandable 32 import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging 33 import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow 34 import com.android.systemui.common.shared.model.ContentDescription 35 import com.android.systemui.common.shared.model.Icon 36 import com.android.systemui.dagger.SysUISingleton 37 import com.android.systemui.dagger.qualifiers.Background 38 import com.android.systemui.keyguard.shared.quickaffordance.ActivationState 39 import com.android.systemui.settings.UserTracker 40 import com.android.systemui.statusbar.policy.ZenModeController 41 import com.android.systemui.util.settings.SecureSettings 42 import com.android.systemui.util.settings.SettingsProxyExt.observerFlow 43 import javax.inject.Inject 44 import kotlinx.coroutines.CoroutineDispatcher 45 import kotlinx.coroutines.channels.awaitClose 46 import kotlinx.coroutines.flow.Flow 47 import kotlinx.coroutines.flow.combine 48 import kotlinx.coroutines.flow.distinctUntilChanged 49 import kotlinx.coroutines.flow.flowOn 50 import kotlinx.coroutines.flow.map 51 import kotlinx.coroutines.flow.onEach 52 import kotlinx.coroutines.flow.onStart 53 54 @SysUISingleton 55 class DoNotDisturbQuickAffordanceConfig 56 constructor( 57 private val context: Context, 58 private val controller: ZenModeController, 59 private val secureSettings: SecureSettings, 60 private val userTracker: UserTracker, 61 @Background private val backgroundDispatcher: CoroutineDispatcher, 62 private val testConditionId: Uri?, 63 testDialog: EnableZenModeDialog?, 64 ) : KeyguardQuickAffordanceConfig { 65 66 @Inject 67 constructor( 68 context: Context, 69 controller: ZenModeController, 70 secureSettings: SecureSettings, 71 userTracker: UserTracker, 72 @Background backgroundDispatcher: CoroutineDispatcher, 73 ) : this(context, controller, secureSettings, userTracker, backgroundDispatcher, null, null) 74 75 private var dndMode: Int = 0 76 private var isAvailable = false 77 private var settingsValue: Int = 0 78 79 private val conditionUri: Uri 80 get() = 81 testConditionId 82 ?: ZenModeConfig.toTimeCondition( 83 context, 84 settingsValue, 85 userTracker.userId, 86 true, /* shortVersion */ 87 ) 88 .id 89 90 private val dialog: EnableZenModeDialog by lazy { 91 testDialog 92 ?: EnableZenModeDialog( 93 context, 94 R.style.Theme_SystemUI_Dialog, 95 true, /* cancelIsNeutral */ 96 ZenModeDialogMetricsLogger(context), 97 ) 98 } 99 100 override val key: String = BuiltInKeyguardQuickAffordanceKeys.DO_NOT_DISTURB 101 102 override fun pickerName(): String = context.getString(R.string.quick_settings_dnd_label) 103 104 override val pickerIconResourceId: Int = R.drawable.ic_do_not_disturb 105 106 override val lockScreenState: Flow<KeyguardQuickAffordanceConfig.LockScreenState> = 107 combine( 108 conflatedCallbackFlow { 109 val callback = 110 object : ZenModeController.Callback { 111 override fun onZenChanged(zen: Int) { 112 dndMode = zen 113 trySendWithFailureLogging(updateState(), TAG) 114 } 115 116 override fun onZenAvailableChanged(available: Boolean) { 117 isAvailable = available 118 trySendWithFailureLogging(updateState(), TAG) 119 } 120 } 121 122 dndMode = controller.zen 123 isAvailable = controller.isZenAvailable 124 trySendWithFailureLogging(updateState(), TAG) 125 126 controller.addCallback(callback) 127 128 awaitClose { controller.removeCallback(callback) } 129 }, 130 secureSettings 131 .observerFlow(userTracker.userId, Settings.Secure.ZEN_DURATION) 132 .onStart { emit(Unit) } 133 .map { secureSettings.getInt(Settings.Secure.ZEN_DURATION, ZEN_MODE_OFF) } 134 .flowOn(backgroundDispatcher) 135 .distinctUntilChanged() 136 .onEach { settingsValue = it } 137 ) { callbackFlowValue, _ -> 138 callbackFlowValue 139 } 140 141 override suspend fun getPickerScreenState(): KeyguardQuickAffordanceConfig.PickerScreenState { 142 return if (controller.isZenAvailable) { 143 KeyguardQuickAffordanceConfig.PickerScreenState.Default( 144 configureIntent = Intent(Settings.ACTION_ZEN_MODE_SETTINGS) 145 ) 146 } else { 147 KeyguardQuickAffordanceConfig.PickerScreenState.UnavailableOnDevice 148 } 149 } 150 151 override fun onTriggered( 152 expandable: Expandable? 153 ): KeyguardQuickAffordanceConfig.OnTriggeredResult { 154 return when { 155 !isAvailable -> KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled 156 dndMode != ZEN_MODE_OFF -> { 157 controller.setZen(ZEN_MODE_OFF, null, TAG) 158 KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled 159 } 160 settingsValue == ZEN_DURATION_PROMPT -> 161 KeyguardQuickAffordanceConfig.OnTriggeredResult.ShowDialog( 162 dialog.createDialog(), 163 expandable 164 ) 165 settingsValue == ZEN_DURATION_FOREVER -> { 166 controller.setZen(ZEN_MODE_IMPORTANT_INTERRUPTIONS, null, TAG) 167 KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled 168 } 169 else -> { 170 controller.setZen(ZEN_MODE_IMPORTANT_INTERRUPTIONS, conditionUri, TAG) 171 KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled 172 } 173 } 174 } 175 176 private fun updateState(): KeyguardQuickAffordanceConfig.LockScreenState { 177 return if (!isAvailable) { 178 KeyguardQuickAffordanceConfig.LockScreenState.Hidden 179 } else if (dndMode == ZEN_MODE_OFF) { 180 KeyguardQuickAffordanceConfig.LockScreenState.Visible( 181 Icon.Resource( 182 R.drawable.qs_dnd_icon_off, 183 ContentDescription.Resource(R.string.dnd_is_off), 184 ), 185 ActivationState.Inactive, 186 ) 187 } else { 188 KeyguardQuickAffordanceConfig.LockScreenState.Visible( 189 Icon.Resource( 190 R.drawable.qs_dnd_icon_on, 191 ContentDescription.Resource(R.string.dnd_is_on), 192 ), 193 ActivationState.Active, 194 ) 195 } 196 } 197 198 companion object { 199 const val TAG = "DoNotDisturbQuickAffordanceConfig" 200 } 201 } 202