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
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 08d29ea..3add15e 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -9496,6 +9496,19 @@
     <!-- Priority Modes: Generic trigger summary for modes where the owner app did not provide neither a triggerDescription nor a configurationActivity to call [CHAR LIMIT=60] -->
     <string name="zen_mode_trigger_summary_managed_by_app">Managed by <xliff:g id="app_name" example="The Awesome App">%1$s</xliff:g></string>
 
+    <!-- Priority Modes: Title of the confirmation dialog for disabling an enabled mode [CHAR LIMIT=30] -->
+    <string name="zen_mode_confirm_disable_title">Disable Mode</string>
+    <!-- Priority Modes: Message body of the confirmation dialog for disabling an enabled mode [CHAR LIMIT=NONE] -->
+    <string name="zen_mode_confirm_disable_message">If you disable this feature, the mode will no longer work as intended and its settings will be hidden.</string>
+    <!-- Priority Modes: Button to disable a mode [CHAR LIMIT=20] -->
+    <string name="zen_mode_action_disable">Disable</string>
+    <!-- Priority Modes: Title of the confirmation dialog for enabling a disabled mode [CHAR LIMIT=30] -->
+    <string name="zen_mode_confirm_enable_title">Enable Mode</string>
+    <!-- Priority Modes: Message body of the confirmation dialog for enabling a disabled mode [CHAR LIMIT=NONE] -->
+    <string name="zen_mode_confirm_enable_message">If you enable this feature, the mode will activate automatically according to its schedule.</string>
+    <!-- Priority Modes: Button to disable a mode [CHAR LIMIT=20] -->
+    <string name="zen_mode_action_enable">Enable</string>
+
     <!-- Content description for help icon button [CHAR LIMIT=20] -->
     <string name="warning_button_text">Warning</string>
 
diff --git a/src/com/android/settings/notification/modes/ZenModeSetTriggerLinkPreferenceController.java b/src/com/android/settings/notification/modes/ZenModeSetTriggerLinkPreferenceController.java
index 1f97902..24df931 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 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 @@
             });
 
     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 61ca4d8..93db4be 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 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.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.MockitoAnnotations;
 import org.mockito.stubbing.Answer;
 import org.robolectric.RobolectricTestRunner;
+import org.robolectric.shadows.ShadowAlertDialog;
 
 import java.util.Calendar;
 
@@ -161,19 +166,86 @@
     }
 
     @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<ZenMode> 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<ZenMode> 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