blob: ef0c9a175141b307e870cfcfe8e67cd7819ba8eb [file] [log] [blame]
/*
* Copyright (C) 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package com.android.systemui.keyguard.data.quickaffordance
import android.content.Context
import android.content.Intent
import android.net.Uri
import android.provider.Settings
import android.provider.Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS
import android.provider.Settings.Global.ZEN_MODE_OFF
import android.provider.Settings.Secure.ZEN_DURATION_FOREVER
import android.provider.Settings.Secure.ZEN_DURATION_PROMPT
import android.service.notification.ZenModeConfig
import com.android.settingslib.notification.EnableZenModeDialog
import com.android.settingslib.notification.ZenModeDialogMetricsLogger
import com.android.systemui.R
import com.android.systemui.animation.Expandable
import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
import com.android.systemui.common.shared.model.ContentDescription
import com.android.systemui.common.shared.model.Icon
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.keyguard.shared.quickaffordance.ActivationState
import com.android.systemui.settings.UserTracker
import com.android.systemui.statusbar.policy.ZenModeController
import com.android.systemui.util.settings.SecureSettings
import com.android.systemui.util.settings.SettingsProxyExt.observerFlow
import javax.inject.Inject
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.onStart
@SysUISingleton
class DoNotDisturbQuickAffordanceConfig
constructor(
private val context: Context,
private val controller: ZenModeController,
private val secureSettings: SecureSettings,
private val userTracker: UserTracker,
@Background private val backgroundDispatcher: CoroutineDispatcher,
private val testConditionId: Uri?,
testDialog: EnableZenModeDialog?,
) : KeyguardQuickAffordanceConfig {
@Inject
constructor(
context: Context,
controller: ZenModeController,
secureSettings: SecureSettings,
userTracker: UserTracker,
@Background backgroundDispatcher: CoroutineDispatcher,
) : this(context, controller, secureSettings, userTracker, backgroundDispatcher, null, null)
private var dndMode: Int = 0
private var isAvailable = false
private var settingsValue: Int = 0
private val conditionUri: Uri
get() =
testConditionId
?: ZenModeConfig.toTimeCondition(
context,
settingsValue,
userTracker.userId,
true, /* shortVersion */
)
.id
private val dialog: EnableZenModeDialog by lazy {
testDialog
?: EnableZenModeDialog(
context,
R.style.Theme_SystemUI_Dialog,
true, /* cancelIsNeutral */
ZenModeDialogMetricsLogger(context),
)
}
override val key: String = BuiltInKeyguardQuickAffordanceKeys.DO_NOT_DISTURB
override val pickerName: String = context.getString(R.string.quick_settings_dnd_label)
override val pickerIconResourceId: Int = R.drawable.ic_do_not_disturb
override val lockScreenState: Flow<KeyguardQuickAffordanceConfig.LockScreenState> =
combine(
conflatedCallbackFlow {
val callback =
object : ZenModeController.Callback {
override fun onZenChanged(zen: Int) {
dndMode = zen
trySendWithFailureLogging(updateState(), TAG)
}
override fun onZenAvailableChanged(available: Boolean) {
isAvailable = available
trySendWithFailureLogging(updateState(), TAG)
}
}
dndMode = controller.zen
isAvailable = controller.isZenAvailable
trySendWithFailureLogging(updateState(), TAG)
controller.addCallback(callback)
awaitClose { controller.removeCallback(callback) }
},
secureSettings
.observerFlow(userTracker.userId, Settings.Secure.ZEN_DURATION)
.onStart { emit(Unit) }
.map { secureSettings.getInt(Settings.Secure.ZEN_DURATION, ZEN_MODE_OFF) }
.flowOn(backgroundDispatcher)
.distinctUntilChanged()
.onEach { settingsValue = it }
) { callbackFlowValue, _ ->
callbackFlowValue
}
override suspend fun getPickerScreenState(): KeyguardQuickAffordanceConfig.PickerScreenState {
return if (controller.isZenAvailable) {
KeyguardQuickAffordanceConfig.PickerScreenState.Default(
configureIntent = Intent(Settings.ACTION_ZEN_MODE_SETTINGS)
)
} else {
KeyguardQuickAffordanceConfig.PickerScreenState.UnavailableOnDevice
}
}
override fun onTriggered(
expandable: Expandable?
): KeyguardQuickAffordanceConfig.OnTriggeredResult {
return when {
!isAvailable -> KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled
dndMode != ZEN_MODE_OFF -> {
controller.setZen(ZEN_MODE_OFF, null, TAG)
KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled
}
settingsValue == ZEN_DURATION_PROMPT ->
KeyguardQuickAffordanceConfig.OnTriggeredResult.ShowDialog(
dialog.createDialog(),
expandable
)
settingsValue == ZEN_DURATION_FOREVER -> {
controller.setZen(ZEN_MODE_IMPORTANT_INTERRUPTIONS, null, TAG)
KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled
}
else -> {
controller.setZen(ZEN_MODE_IMPORTANT_INTERRUPTIONS, conditionUri, TAG)
KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled
}
}
}
private fun updateState(): KeyguardQuickAffordanceConfig.LockScreenState {
return if (!isAvailable) {
KeyguardQuickAffordanceConfig.LockScreenState.Hidden
} else if (dndMode == ZEN_MODE_OFF) {
KeyguardQuickAffordanceConfig.LockScreenState.Visible(
Icon.Resource(
R.drawable.qs_dnd_icon_off,
ContentDescription.Resource(R.string.dnd_is_off),
),
ActivationState.Inactive,
)
} else {
KeyguardQuickAffordanceConfig.LockScreenState.Visible(
Icon.Resource(
R.drawable.qs_dnd_icon_on,
ContentDescription.Resource(R.string.dnd_is_on),
),
ActivationState.Active,
)
}
}
companion object {
const val TAG = "DoNotDisturbQuickAffordanceConfig"
}
}