diff --git a/packages/SystemUI/res/drawable/ic_do_not_disturb.xml b/packages/SystemUI/res/drawable/ic_do_not_disturb.xml
new file mode 100644
index 0000000000000..95663c095c4be
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_do_not_disturb.xml
@@ -0,0 +1,25 @@
+
+
+
+
+
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index ca2cf1a56b410..80e969ead1696 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -2035,6 +2035,9 @@
Do Not Disturb is off
+
+ Do Not Disturb is on
+
Do Not Disturb was turned on by an automatic rule (%s).
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/BuiltInKeyguardQuickAffordanceKeys.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/BuiltInKeyguardQuickAffordanceKeys.kt
index 73dbeab3030b4..ea5b4f43cc75f 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/BuiltInKeyguardQuickAffordanceKeys.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/BuiltInKeyguardQuickAffordanceKeys.kt
@@ -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"
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/DoNotDisturbQuickAffordanceConfig.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/DoNotDisturbQuickAffordanceConfig.kt
new file mode 100644
index 0000000000000..8efb366248317
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/DoNotDisturbQuickAffordanceConfig.kt
@@ -0,0 +1,190 @@
+/*
+ * 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 = 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
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardDataQuickAffordanceModule.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardDataQuickAffordanceModule.kt
index 072cfb140e623..71d01ebc84967 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardDataQuickAffordanceModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardDataQuickAffordanceModule.kt
@@ -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 {
return setOf(
camera,
+ doNotDisturb,
flashlight,
home,
quickAccessWallet,
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceConfig.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceConfig.kt
index 98b1a731b82c6..02ebcd3200a4b 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceConfig.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceConfig.kt
@@ -17,6 +17,7 @@
package com.android.systemui.keyguard.data.quickaffordance
+import android.app.AlertDialog
import android.content.Intent
import com.android.systemui.animation.Expandable
import com.android.systemui.common.shared.model.Icon
@@ -141,6 +142,16 @@ interface KeyguardQuickAffordanceConfig {
val intent: Intent,
val canShowWhileLocked: Boolean,
) : OnTriggeredResult()
+
+ /**
+ * Returning this as a result from the [onTriggered] method means that the implementation
+ * has _not_ taken care of the action and the system should show a Dialog using the
+ * given [AlertDialog] and [Expandable].
+ */
+ data class ShowDialog(
+ val dialog: AlertDialog,
+ val expandable: Expandable?,
+ ) : OnTriggeredResult()
}
companion object {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt
index f4c335b04f5b8..8eace76632aa1 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt
@@ -17,9 +17,11 @@
package com.android.systemui.keyguard.domain.interactor
+import android.app.AlertDialog
import android.content.Intent
import android.util.Log
import com.android.internal.widget.LockPatternUtils
+import com.android.systemui.animation.DialogLaunchAnimator
import com.android.systemui.animation.Expandable
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.flags.FeatureFlags
@@ -35,15 +37,16 @@ import com.android.systemui.keyguard.shared.quickaffordance.KeyguardQuickAfforda
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.settings.UserTracker
import com.android.systemui.shared.quickaffordance.data.content.KeyguardQuickAffordanceProviderContract as Contract
+import com.android.systemui.statusbar.phone.SystemUIDialog
import com.android.systemui.statusbar.policy.KeyguardStateController
import dagger.Lazy
-import javax.inject.Inject
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onStart
+import javax.inject.Inject
@SysUISingleton
class KeyguardQuickAffordanceInteractor
@@ -57,6 +60,7 @@ constructor(
private val activityStarter: ActivityStarter,
private val featureFlags: FeatureFlags,
private val repository: Lazy,
+ private val launchAnimator: DialogLaunchAnimator,
) {
private val isUsingRepository: Boolean
get() = featureFlags.isEnabled(Flags.CUSTOMIZABLE_LOCK_SCREEN_QUICK_AFFORDANCES)
@@ -131,6 +135,11 @@ constructor(
expandable = expandable,
)
is KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled -> Unit
+ is KeyguardQuickAffordanceConfig.OnTriggeredResult.ShowDialog ->
+ showDialog(
+ result.dialog,
+ result.expandable,
+ )
}
}
@@ -279,6 +288,19 @@ constructor(
}
}
+ private fun showDialog(dialog: AlertDialog, expandable: Expandable?) {
+ expandable?.dialogLaunchController()?.let { controller ->
+ SystemUIDialog.applyFlags(dialog)
+ SystemUIDialog.setShowForAllUsers(dialog, true)
+ SystemUIDialog.registerDismissListener(dialog)
+ SystemUIDialog.setDialogSize(dialog)
+ launchAnimator.show(
+ dialog,
+ controller
+ )
+ }
+ }
+
private fun launchQuickAffordance(
intent: Intent,
canShowWhileLocked: Boolean,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardQuickAffordanceProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardQuickAffordanceProviderTest.kt
index 26a63e22eb8b8..cf9c91a1adb0b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardQuickAffordanceProviderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardQuickAffordanceProviderTest.kt
@@ -31,6 +31,7 @@ import androidx.test.filters.SmallTest
import com.android.internal.widget.LockPatternUtils
import com.android.systemui.SystemUIAppComponentFactoryBase
import com.android.systemui.SysuiTestCase
+import com.android.systemui.animation.DialogLaunchAnimator
import com.android.systemui.flags.FakeFeatureFlags
import com.android.systemui.flags.Flags
import com.android.systemui.keyguard.data.quickaffordance.FakeKeyguardQuickAffordanceConfig
@@ -84,6 +85,7 @@ class KeyguardQuickAffordanceProviderTest : SysuiTestCase() {
@Mock private lateinit var previewRenderer: KeyguardPreviewRenderer
@Mock private lateinit var backgroundHandler: Handler
@Mock private lateinit var previewSurfacePackage: SurfaceControlViewHost.SurfacePackage
+ @Mock private lateinit var launchAnimator: DialogLaunchAnimator
private lateinit var underTest: KeyguardQuickAffordanceProvider
@@ -170,6 +172,7 @@ class KeyguardQuickAffordanceProviderTest : SysuiTestCase() {
set(Flags.LOCKSCREEN_CUSTOM_CLOCKS, true)
},
repository = { quickAffordanceRepository },
+ launchAnimator = launchAnimator,
)
underTest.previewManager =
KeyguardRemotePreviewManager(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/DoNotDisturbQuickAffordanceConfigTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/DoNotDisturbQuickAffordanceConfigTest.kt
new file mode 100644
index 0000000000000..7c10108d5b45d
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/DoNotDisturbQuickAffordanceConfigTest.kt
@@ -0,0 +1,251 @@
+/*
+ * 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.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 androidx.test.filters.SmallTest
+import com.android.settingslib.notification.EnableZenModeDialog
+import com.android.systemui.R
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.animation.Expandable
+import com.android.systemui.common.shared.model.ContentDescription
+import com.android.systemui.common.shared.model.Icon
+import com.android.systemui.coroutines.collectLastValue
+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.mockito.argumentCaptor
+import com.android.systemui.util.mockito.eq
+import com.android.systemui.util.mockito.mock
+import com.android.systemui.util.mockito.whenever
+import com.android.systemui.util.settings.FakeSettings
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.StandardTestDispatcher
+import kotlinx.coroutines.test.TestDispatcher
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertNull
+import org.junit.Assert.assertTrue
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+import org.mockito.ArgumentCaptor
+import org.mockito.Captor
+import org.mockito.Mock
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@RunWith(JUnit4::class)
+class DoNotDisturbQuickAffordanceConfigTest : SysuiTestCase() {
+
+ @Mock private lateinit var zenModeController: ZenModeController
+ @Mock private lateinit var userTracker: UserTracker
+ @Mock private lateinit var conditionUri: Uri
+ @Mock private lateinit var enableZenModeDialog: EnableZenModeDialog
+ @Captor private lateinit var spyZenMode: ArgumentCaptor
+ @Captor private lateinit var spyConditionId: ArgumentCaptor
+ private lateinit var settings: FakeSettings
+
+ private lateinit var underTest: DoNotDisturbQuickAffordanceConfig
+ private lateinit var testDispatcher: TestDispatcher
+ private lateinit var testScope: TestScope
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+
+ testDispatcher = StandardTestDispatcher()
+ testScope = TestScope(testDispatcher)
+
+ settings = FakeSettings()
+
+ underTest = DoNotDisturbQuickAffordanceConfig(
+ context,
+ zenModeController,
+ settings,
+ userTracker,
+ testDispatcher,
+ conditionUri,
+ enableZenModeDialog,
+ )
+ }
+
+ @Test
+ fun `dnd not available - picker state hidden`() = testScope.runTest {
+ //given
+ whenever(zenModeController.isZenAvailable).thenReturn(false)
+
+ //when
+ val result = underTest.getPickerScreenState()
+
+ //then
+ assertEquals(KeyguardQuickAffordanceConfig.PickerScreenState.UnavailableOnDevice, result)
+ }
+
+ @Test
+ fun `dnd available - picker state visible`() = testScope.runTest {
+ //given
+ whenever(zenModeController.isZenAvailable).thenReturn(true)
+
+ //when
+ val result = underTest.getPickerScreenState()
+
+ //then
+ assertEquals(KeyguardQuickAffordanceConfig.PickerScreenState.Default, result)
+ }
+
+ @Test
+ fun `onTriggered - dnd mode is not ZEN_MODE_OFF - set to ZEN_MODE_OFF`() = testScope.runTest {
+ //given
+ whenever(zenModeController.isZenAvailable).thenReturn(true)
+ whenever(zenModeController.zen).thenReturn(-1)
+ settings.putInt(Settings.Secure.ZEN_DURATION, -2)
+ collectLastValue(underTest.lockScreenState)
+ runCurrent()
+
+ //when
+ val result = underTest.onTriggered(null)
+ verify(zenModeController).setZen(spyZenMode.capture(), spyConditionId.capture(), eq(DoNotDisturbQuickAffordanceConfig.TAG))
+
+ //then
+ assertEquals(KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled, result)
+ assertEquals(ZEN_MODE_OFF, spyZenMode.value)
+ assertNull(spyConditionId.value)
+ }
+
+ @Test
+ fun `onTriggered - dnd mode is ZEN_MODE_OFF - setting is FOREVER - set zen with no condition`() = testScope.runTest {
+ //given
+ whenever(zenModeController.isZenAvailable).thenReturn(true)
+ whenever(zenModeController.zen).thenReturn(ZEN_MODE_OFF)
+ settings.putInt(Settings.Secure.ZEN_DURATION, ZEN_DURATION_FOREVER)
+ collectLastValue(underTest.lockScreenState)
+ runCurrent()
+
+ //when
+ val result = underTest.onTriggered(null)
+ verify(zenModeController).setZen(spyZenMode.capture(), spyConditionId.capture(), eq(DoNotDisturbQuickAffordanceConfig.TAG))
+
+ //then
+ assertEquals(KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled, result)
+ assertEquals(ZEN_MODE_IMPORTANT_INTERRUPTIONS, spyZenMode.value)
+ assertNull(spyConditionId.value)
+ }
+
+ @Test
+ fun `onTriggered - dnd mode is ZEN_MODE_OFF - setting is not FOREVER or PROMPT - set zen with condition`() = testScope.runTest {
+ //given
+ whenever(zenModeController.isZenAvailable).thenReturn(true)
+ whenever(zenModeController.zen).thenReturn(ZEN_MODE_OFF)
+ settings.putInt(Settings.Secure.ZEN_DURATION, -900)
+ collectLastValue(underTest.lockScreenState)
+ runCurrent()
+
+ //when
+ val result = underTest.onTriggered(null)
+ verify(zenModeController).setZen(spyZenMode.capture(), spyConditionId.capture(), eq(DoNotDisturbQuickAffordanceConfig.TAG))
+
+ //then
+ assertEquals(KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled, result)
+ assertEquals(ZEN_MODE_IMPORTANT_INTERRUPTIONS, spyZenMode.value)
+ assertEquals(conditionUri, spyConditionId.value)
+ }
+
+ @Test
+ fun `onTriggered - dnd mode is ZEN_MODE_OFF - setting is PROMPT - show dialog`() = testScope.runTest {
+ //given
+ val expandable: Expandable = mock()
+ whenever(zenModeController.isZenAvailable).thenReturn(true)
+ whenever(zenModeController.zen).thenReturn(ZEN_MODE_OFF)
+ settings.putInt(Settings.Secure.ZEN_DURATION, ZEN_DURATION_PROMPT)
+ whenever(enableZenModeDialog.createDialog()).thenReturn(mock())
+ collectLastValue(underTest.lockScreenState)
+ runCurrent()
+
+ //when
+ val result = underTest.onTriggered(expandable)
+
+ //then
+ assertTrue(result is KeyguardQuickAffordanceConfig.OnTriggeredResult.ShowDialog)
+ assertEquals(expandable, (result as KeyguardQuickAffordanceConfig.OnTriggeredResult.ShowDialog).expandable)
+ }
+
+ @Test
+ fun `lockScreenState - dndAvailable starts as true - changes to false - State moves to Hidden`() = testScope.runTest {
+ //given
+ whenever(zenModeController.isZenAvailable).thenReturn(true)
+ val callbackCaptor: ArgumentCaptor = argumentCaptor()
+ val valueSnapshot = collectLastValue(underTest.lockScreenState)
+ val secondLastValue = valueSnapshot()
+ verify(zenModeController).addCallback(callbackCaptor.capture())
+
+ //when
+ callbackCaptor.value.onZenAvailableChanged(false)
+ val lastValue = valueSnapshot()
+
+ //then
+ assertTrue(secondLastValue is KeyguardQuickAffordanceConfig.LockScreenState.Visible)
+ assertTrue(lastValue is KeyguardQuickAffordanceConfig.LockScreenState.Hidden)
+ }
+
+ @Test
+ fun `lockScreenState - dndMode starts as ZEN_MODE_OFF - changes to not OFF - State moves to Visible`() = testScope.runTest {
+ //given
+ whenever(zenModeController.isZenAvailable).thenReturn(true)
+ whenever(zenModeController.zen).thenReturn(ZEN_MODE_OFF)
+ val valueSnapshot = collectLastValue(underTest.lockScreenState)
+ val secondLastValue = valueSnapshot()
+ val callbackCaptor: ArgumentCaptor = argumentCaptor()
+ verify(zenModeController).addCallback(callbackCaptor.capture())
+
+ //when
+ callbackCaptor.value.onZenChanged(ZEN_MODE_IMPORTANT_INTERRUPTIONS)
+ val lastValue = valueSnapshot()
+
+ //then
+ assertEquals(
+ KeyguardQuickAffordanceConfig.LockScreenState.Visible(
+ Icon.Resource(
+ R.drawable.qs_dnd_icon_off,
+ ContentDescription.Resource(R.string.dnd_is_off)
+ ),
+ ActivationState.Inactive
+ ),
+ secondLastValue,
+ )
+ assertEquals(
+ KeyguardQuickAffordanceConfig.LockScreenState.Visible(
+ Icon.Resource(
+ R.drawable.qs_dnd_icon_on,
+ ContentDescription.Resource(R.string.dnd_is_on)
+ ),
+ ActivationState.Active
+ ),
+ lastValue,
+ )
+ }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorParameterizedTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorParameterizedTest.kt
index 1c1f0399bb069..14b7c311d9afe 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorParameterizedTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorParameterizedTest.kt
@@ -23,6 +23,7 @@ import androidx.test.filters.SmallTest
import com.android.internal.widget.LockPatternUtils
import com.android.systemui.SysuiTestCase
import com.android.systemui.animation.ActivityLaunchAnimator
+import com.android.systemui.animation.DialogLaunchAnimator
import com.android.systemui.animation.Expandable
import com.android.systemui.common.shared.model.ContentDescription
import com.android.systemui.common.shared.model.Icon
@@ -214,6 +215,7 @@ class KeyguardQuickAffordanceInteractorParameterizedTest : SysuiTestCase() {
@Mock private lateinit var activityStarter: ActivityStarter
@Mock private lateinit var animationController: ActivityLaunchAnimator.Controller
@Mock private lateinit var expandable: Expandable
+ @Mock private lateinit var launchAnimator: DialogLaunchAnimator
private lateinit var underTest: KeyguardQuickAffordanceInteractor
@@ -308,6 +310,7 @@ class KeyguardQuickAffordanceInteractorParameterizedTest : SysuiTestCase() {
set(Flags.CUSTOMIZABLE_LOCK_SCREEN_QUICK_AFFORDANCES, false)
},
repository = { quickAffordanceRepository },
+ launchAnimator = launchAnimator,
)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt
index 58a85306eb690..972919a9d27a0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt
@@ -22,6 +22,7 @@ import androidx.test.filters.SmallTest
import com.android.internal.widget.LockPatternUtils
import com.android.systemui.R
import com.android.systemui.SysuiTestCase
+import com.android.systemui.animation.DialogLaunchAnimator
import com.android.systemui.common.shared.model.ContentDescription
import com.android.systemui.common.shared.model.Icon
import com.android.systemui.coroutines.collectLastValue
@@ -73,6 +74,7 @@ class KeyguardQuickAffordanceInteractorTest : SysuiTestCase() {
@Mock private lateinit var keyguardStateController: KeyguardStateController
@Mock private lateinit var userTracker: UserTracker
@Mock private lateinit var activityStarter: ActivityStarter
+ @Mock private lateinit var launchAnimator: DialogLaunchAnimator
private lateinit var underTest: KeyguardQuickAffordanceInteractor
@@ -171,6 +173,7 @@ class KeyguardQuickAffordanceInteractorTest : SysuiTestCase() {
activityStarter = activityStarter,
featureFlags = featureFlags,
repository = { quickAffordanceRepository },
+ launchAnimator = launchAnimator,
)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt
index 0abff88b5faf3..a2c2f711b1d4b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt
@@ -21,6 +21,7 @@ import android.os.UserHandle
import androidx.test.filters.SmallTest
import com.android.internal.widget.LockPatternUtils
import com.android.systemui.SysuiTestCase
+import com.android.systemui.animation.DialogLaunchAnimator
import com.android.systemui.animation.Expandable
import com.android.systemui.common.shared.model.Icon
import com.android.systemui.coroutines.collectLastValue
@@ -82,6 +83,7 @@ class KeyguardBottomAreaViewModelTest : SysuiTestCase() {
@Mock private lateinit var keyguardStateController: KeyguardStateController
@Mock private lateinit var userTracker: UserTracker
@Mock private lateinit var activityStarter: ActivityStarter
+ @Mock private lateinit var launchAnimator: DialogLaunchAnimator
private lateinit var underTest: KeyguardBottomAreaViewModel
@@ -191,6 +193,7 @@ class KeyguardBottomAreaViewModelTest : SysuiTestCase() {
set(Flags.CUSTOMIZABLE_LOCK_SCREEN_QUICK_AFFORDANCES, false)
},
repository = { quickAffordanceRepository },
+ launchAnimator = launchAnimator,
),
bottomAreaInteractor = KeyguardBottomAreaInteractor(repository = repository),
burnInHelperWrapper = burnInHelperWrapper,