Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit ed89d185 authored by Brad Hinegardner's avatar Brad Hinegardner
Browse files

Add Do Not disturb custom quick affordance

Bug: 256687019
Test: DoNotDisturbQuickAffordanceConfigTest.kt , manual verification
Change-Id: I3f234ef86b99b5737fe7657ede165c59aa42a324
parent 990b0bb5
Loading
Loading
Loading
Loading
+25 −0
Original line number Diff line number Diff line
<!--
  ~ 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.
  -->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:height="24dp"
    android:width="24dp"
    android:viewportHeight="24.0"
    android:viewportWidth="24.0">

    <path
        android:fillColor="#ffffff"
        android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM12,20c-4.41,0 -8,-3.59 -8,-8s3.59,-8 8,-8 8,3.59 8,8 -3.59,8 -8,8zM7,11h10v2L7,13z"/>
</vector>
+3 −0
Original line number Diff line number Diff line
@@ -2035,6 +2035,9 @@
    <!-- Label for when Do not disturb is off in QS detail panel [CHAR LIMIT=NONE] -->
    <string name="dnd_is_off">Do Not Disturb is off</string>

    <!-- Label for when Do not disturb is on in lockscreen quick affordance [CHAR LIMIT=NONE] -->
    <string name="dnd_is_on">Do Not Disturb is on</string>

    <!-- Prompt for when Do not disturb is on from automatic rule in QS [CHAR LIMIT=NONE] -->
    <string name="qs_dnd_prompt_auto_rule">Do Not Disturb was turned on by an automatic rule (<xliff:g name="rule">%s</xliff:g>).</string>

+1 −0
Original line number Diff line number Diff line
@@ -25,6 +25,7 @@ package com.android.systemui.keyguard.data.quickaffordance
object BuiltInKeyguardQuickAffordanceKeys {
    // Please keep alphabetical order of const names to simplify future maintenance.
    const val CAMERA = "camera"
    const val DO_NOT_DISTURB = "do_not_disturb"
    const val FLASHLIGHT = "flashlight"
    const val HOME_CONTROLS = "home"
    const val QR_CODE_SCANNER = "qr_code_scanner"
+190 −0
Original line number Diff line number Diff line
/*
 *  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.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 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
import javax.inject.Inject

@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(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
        } 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"
    }
}
 No newline at end of file
+2 −0
Original line number Diff line number Diff line
@@ -33,6 +33,7 @@ interface KeyguardDataQuickAffordanceModule {
        @Provides
        @ElementsIntoSet
        fun quickAffordanceConfigs(
            doNotDisturb: DoNotDisturbQuickAffordanceConfig,
            flashlight: FlashlightQuickAffordanceConfig,
            home: HomeControlsKeyguardQuickAffordanceConfig,
            quickAccessWallet: QuickAccessWalletKeyguardQuickAffordanceConfig,
@@ -41,6 +42,7 @@ interface KeyguardDataQuickAffordanceModule {
        ): Set<KeyguardQuickAffordanceConfig> {
            return setOf(
                camera,
                doNotDisturb,
                flashlight,
                home,
                quickAccessWallet,
Loading