From ed89d1858cce133862e592eac36f8d8ad2019957 Mon Sep 17 00:00:00 2001 From: Brad Hinegardner Date: Fri, 16 Dec 2022 15:44:38 -0500 Subject: [PATCH] Add Do Not disturb custom quick affordance Bug: 256687019 Test: DoNotDisturbQuickAffordanceConfigTest.kt , manual verification Change-Id: I3f234ef86b99b5737fe7657ede165c59aa42a324 --- .../res/drawable/ic_do_not_disturb.xml | 25 ++ packages/SystemUI/res/values/strings.xml | 3 + .../BuiltInKeyguardQuickAffordanceKeys.kt | 1 + .../DoNotDisturbQuickAffordanceConfig.kt | 190 +++++++++++++ .../KeyguardDataQuickAffordanceModule.kt | 2 + .../KeyguardQuickAffordanceConfig.kt | 11 + .../KeyguardQuickAffordanceInteractor.kt | 24 +- .../KeyguardQuickAffordanceProviderTest.kt | 3 + .../DoNotDisturbQuickAffordanceConfigTest.kt | 251 ++++++++++++++++++ ...ckAffordanceInteractorParameterizedTest.kt | 3 + .../KeyguardQuickAffordanceInteractorTest.kt | 3 + .../KeyguardBottomAreaViewModelTest.kt | 3 + 12 files changed, 518 insertions(+), 1 deletion(-) create mode 100644 packages/SystemUI/res/drawable/ic_do_not_disturb.xml create mode 100644 packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/DoNotDisturbQuickAffordanceConfig.kt create mode 100644 packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/DoNotDisturbQuickAffordanceConfigTest.kt 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,