From 77191e9519fdda3bd1211be1352381ada2d8152b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mat=C3=ADas=20Hern=C3=A1ndez?= Date: Tue, 23 Jul 2024 19:34:47 +0200 Subject: [PATCH] Show confirmation dialogs when enabling or disabling a mode (Strings are not final, but structure is there). Bug: 349376785 Test: atest ZenModeSetTriggerLinkPreferenceControllerTest Flag: android.app.modes_ui Change-Id: Ia9e604483b90bc30ad1c12e5663a07e251084073 --- res/values/strings.xml | 13 +++ ...odeSetTriggerLinkPreferenceController.java | 39 +++++++-- ...etTriggerLinkPreferenceControllerTest.java | 82 +++++++++++++++++-- 3 files changed, 124 insertions(+), 10 deletions(-) diff --git a/res/values/strings.xml b/res/values/strings.xml index 08d29ea0678..3add15ed346 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -9496,6 +9496,19 @@ Managed by %1$s + + Disable Mode + + If you disable this feature, the mode will no longer work as intended and its settings will be hidden. + + Disable + + Enable Mode + + If you enable this feature, the mode will activate automatically according to its schedule. + + Enable + Warning diff --git a/src/com/android/settings/notification/modes/ZenModeSetTriggerLinkPreferenceController.java b/src/com/android/settings/notification/modes/ZenModeSetTriggerLinkPreferenceController.java index 1f979022670..24df931a43b 100644 --- a/src/com/android/settings/notification/modes/ZenModeSetTriggerLinkPreferenceController.java +++ b/src/com/android/settings/notification/modes/ZenModeSetTriggerLinkPreferenceController.java @@ -25,6 +25,7 @@ import static android.service.notification.ZenModeConfig.tryParseScheduleConditi import static com.google.common.base.Preconditions.checkNotNull; import android.annotation.SuppressLint; +import android.app.AlertDialog; import android.content.Context; import android.content.Intent; import android.content.pm.ApplicationInfo; @@ -231,12 +232,40 @@ class ZenModeSetTriggerLinkPreferenceController extends AbstractZenModePreferenc }); private final Preference.OnPreferenceChangeListener mSwitchChangeListener = (p, newValue) -> { - final boolean newEnabled = (Boolean) newValue; - return saveMode((zenMode) -> { - if (newEnabled != zenMode.getRule().isEnabled()) { - zenMode.getRule().setEnabled(newEnabled); + confirmChangeEnabled(p, (boolean) newValue); + return true; + }; + + private void confirmChangeEnabled(Preference preference, boolean enabled) { + @StringRes int title = enabled ? R.string.zen_mode_confirm_enable_title + : R.string.zen_mode_confirm_disable_title; + @StringRes int message = enabled ? R.string.zen_mode_confirm_enable_message + : R.string.zen_mode_confirm_disable_message; + @StringRes int confirmButton = enabled ? R.string.zen_mode_action_enable + : R.string.zen_mode_action_disable; + + new AlertDialog.Builder(mContext) + .setTitle(title) + .setMessage(message) + .setPositiveButton(confirmButton, + (dialog, which) -> setModeEnabled(enabled)) + .setNegativeButton(R.string.cancel, + (dialog, which) -> undoToggleSwitch(preference, enabled)) + .setOnCancelListener(dialog -> undoToggleSwitch(preference, enabled)) + .show(); + } + + private void setModeEnabled(boolean enabled) { + saveMode((zenMode) -> { + if (enabled != zenMode.getRule().isEnabled()) { + zenMode.getRule().setEnabled(enabled); } return zenMode; }); - }; + } + + private void undoToggleSwitch(Preference preference, boolean wasSwitchedTo) { + PrimarySwitchPreference switchPreference = (PrimarySwitchPreference) preference; + switchPreference.setChecked(!wasSwitchedTo); + } } diff --git a/tests/robotests/src/com/android/settings/notification/modes/ZenModeSetTriggerLinkPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/modes/ZenModeSetTriggerLinkPreferenceControllerTest.java index 61ca4d84662..93db4bea617 100644 --- a/tests/robotests/src/com/android/settings/notification/modes/ZenModeSetTriggerLinkPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/notification/modes/ZenModeSetTriggerLinkPreferenceControllerTest.java @@ -32,9 +32,12 @@ import static com.google.common.truth.Truth.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import static org.robolectric.Shadows.shadowOf; +import android.app.AlertDialog; import android.app.AutomaticZenRule; import android.app.Flags; import android.content.Context; @@ -42,6 +45,7 @@ import android.content.Intent; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.net.Uri; +import android.os.Looper; import android.platform.test.annotations.EnableFlags; import android.platform.test.flag.junit.SetFlagsRule; import android.service.notification.SystemZenRules; @@ -74,6 +78,7 @@ import org.mockito.Mock; import org.mockito.MockitoAnnotations; import org.mockito.stubbing.Answer; import org.robolectric.RobolectricTestRunner; +import org.robolectric.shadows.ShadowAlertDialog; import java.util.Calendar; @@ -161,19 +166,86 @@ public class ZenModeSetTriggerLinkPreferenceControllerTest { } @Test - public void onPreferenceChange_updatesMode() { + public void onPreferenceChange_toggleOn_enablesModeAfterConfirmation() { + // Start with a disabled mode ZenMode zenMode = new TestModeBuilder().setEnabled(false).build(); - - // start with disabled rule mController.updateZenMode(mPrefCategory, zenMode); - // then flip the switch + // Flip the switch mConfigPreference.callChangeListener(true); + verify(mBackend, never()).updateMode(any()); - // verify the backend got asked to update the mode to be enabled + // Oh wait, I forgot to confirm! Let's do that + assertThat(ShadowAlertDialog.getLatestAlertDialog()).isNotNull(); + assertThat(ShadowAlertDialog.getLatestAlertDialog().isShowing()).isTrue(); + ShadowAlertDialog.getLatestAlertDialog() + .getButton(AlertDialog.BUTTON_POSITIVE).performClick(); + shadowOf(Looper.getMainLooper()).idle(); + + // Verify the backend got asked to update the mode to be enabled ArgumentCaptor captor = ArgumentCaptor.forClass(ZenMode.class); verify(mBackend).updateMode(captor.capture()); assertThat(captor.getValue().getRule().isEnabled()).isTrue(); + assertThat(ShadowAlertDialog.getLatestAlertDialog().isShowing()).isFalse(); + } + + @Test + public void onPreferenceChange_toggleOff_disablesModeAfterConfirmation() { + // Start with an enabled mode + ZenMode zenMode = new TestModeBuilder().setEnabled(true).build(); + mController.updateZenMode(mPrefCategory, zenMode); + + // Flip the switch + mConfigPreference.callChangeListener(false); + verify(mBackend, never()).updateMode(any()); + + // Oh wait, I forgot to confirm! Let's do that + assertThat(ShadowAlertDialog.getLatestAlertDialog()).isNotNull(); + assertThat(ShadowAlertDialog.getLatestAlertDialog().isShowing()).isTrue(); + ShadowAlertDialog.getLatestAlertDialog() + .getButton(AlertDialog.BUTTON_POSITIVE).performClick(); + shadowOf(Looper.getMainLooper()).idle(); + + // Verify the backend got asked to update the mode to be disabled + ArgumentCaptor captor = ArgumentCaptor.forClass(ZenMode.class); + verify(mBackend).updateMode(captor.capture()); + assertThat(captor.getValue().getRule().isEnabled()).isFalse(); + assertThat(ShadowAlertDialog.getLatestAlertDialog().isShowing()).isFalse(); + } + + @Test + public void onPreferenceChange_ifPressCancelButton_doesNotUpdateMode() { + // Start with a disabled mode + ZenMode zenMode = new TestModeBuilder().setEnabled(false).build(); + mController.updateZenMode(mPrefCategory, zenMode); + + // Flip the switch, then have second thoughts about it + mConfigPreference.callChangeListener(true); + ShadowAlertDialog.getLatestAlertDialog() + .getButton(AlertDialog.BUTTON_NEGATIVE).performClick(); + shadowOf(Looper.getMainLooper()).idle(); + + // Verify nothing changed, and the switch shows the correct (pre-change) value. + verify(mBackend, never()).updateMode(any()); + assertThat(mConfigPreference.isChecked()).isFalse(); + assertThat(ShadowAlertDialog.getLatestAlertDialog().isShowing()).isFalse(); + } + + @Test + public void onPreferenceChange_ifExitingDialog_doesNotUpdateMode() { + // Start with a disabled mode + ZenMode zenMode = new TestModeBuilder().setEnabled(false).build(); + mController.updateZenMode(mPrefCategory, zenMode); + + // Flip the switch, but close the dialog without selecting either button. + mConfigPreference.callChangeListener(true); + ShadowAlertDialog.getLatestAlertDialog().dismiss(); + shadowOf(Looper.getMainLooper()).idle(); + + // Verify nothing changed, and the switch shows the correct (pre-change) value. + verify(mBackend, never()).updateMode(any()); + assertThat(mConfigPreference.isChecked()).isFalse(); + assertThat(ShadowAlertDialog.getLatestAlertDialog().isShowing()).isFalse(); } @Test