Merge "Clean up EuiccPreferenceController" into main
diff --git a/res/layout-sw600dp/mode_interstitial_layout.xml b/res/layout-sw600dp/mode_interstitial_layout.xml
new file mode 100644
index 0000000..595f353
--- /dev/null
+++ b/res/layout-sw600dp/mode_interstitial_layout.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2024 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.
+  -->
+<FrameLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:paddingHorizontal="80dp" >
+
+    <include
+        layout="@layout/mode_interstitial_layout_base"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:layout_gravity="center_horizontal" />
+</FrameLayout>
\ No newline at end of file
diff --git a/res/layout/mode_interstitial_layout.xml b/res/layout/mode_interstitial_layout_base.xml
similarity index 100%
rename from res/layout/mode_interstitial_layout.xml
rename to res/layout/mode_interstitial_layout_base.xml
diff --git a/res/values/aliases.xml b/res/values/aliases.xml
index e17198b..3356977 100644
--- a/res/values/aliases.xml
+++ b/res/values/aliases.xml
@@ -23,5 +23,6 @@
      <item name="fingerprint_enroll_finish" type="layout">@layout/fingerprint_enroll_finish_base</item>
      <item name="sfps_enroll_finish" type="layout">@layout/sfps_enroll_finish_base</item>
      <item name="choose_lock_pattern" type="layout">@layout/choose_lock_pattern_common</item>
+     <item name="mode_interstitial_layout" type="layout">@layout/mode_interstitial_layout_base</item>
 </resources>
 
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 0fcb0d6..ac63cbf 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -3071,10 +3071,9 @@
     <string name="sim_change_data_ok">
         Use <xliff:g id="carrier" example="Verizon">%1$s</xliff:g>
     </string>
-    <!-- Title for the dialog asking to user to change the preferred SIM  [CHAR LIMIT=30] -->
-    <string name="sim_preferred_title">Update preferred SIM?</string>
+
     <!-- Message for the dialog asking to user to change the preferred SIM  [CHAR LIMIT=NONE] -->
-    <string name="sim_preferred_message"><xliff:g id="new_sim">%1$s</xliff:g> is the only SIM in your device. Do you want to use this SIM for mobile data, calls, and SMS messages?</string>
+    <string name="sim_preferred_message"><xliff:g id="new_sim">%1$s</xliff:g> is now your primary SIM</string>
 
     <!-- Title for the dialog asking user to enable auto data switch upon enabling multi-SIM  [CHAR LIMIT=30] -->
     <string name="enable_auto_data_switch_dialog_title">Improve mobile data coverage?</string>
@@ -7531,7 +7530,7 @@
     <string name="select_sim_for_sms">Select a SIM for SMS</string>
     <!-- Message for switching data SIM; switching takes a while -->
     <string name="data_switch_started">Switching data SIM, this may take up to a minute\u2026</string>
-    <!-- Title for selecting specific sim for data in settings.  [CHAR LIMIT=40] -->
+    <!-- Title for selecting specific sim/preferred sim for data in settings.  [CHAR LIMIT=40] -->
     <string name="select_specific_sim_for_data_title">Use <xliff:g id="new_sim" example="carrierA">%1$s</xliff:g> for mobile data?</string>
     <!-- Message for selecting specific sim for data in settings.  [CHAR LIMIT=NONE] -->
     <string name="select_specific_sim_for_data_msg">If you switch to <xliff:g id="new_sim" example="carrierA">%1$s</xliff:g>, <xliff:g id="old_sim" example="carrierB">%2$s</xliff:g> will no longer be used for mobile data.</string>
diff --git a/src/com/android/settings/ResetNetwork.java b/src/com/android/settings/ResetNetwork.java
index c1e3494..ad9f35e 100644
--- a/src/com/android/settings/ResetNetwork.java
+++ b/src/com/android/settings/ResetNetwork.java
@@ -132,7 +132,6 @@
         if (Flags.resetMobileNetworkSettings()) {
             resetOptions |= ResetNetworkRequest.RESET_IMS_STACK;
             resetOptions |= ResetNetworkRequest.RESET_PHONE_PROCESS;
-            resetOptions |= ResetNetworkRequest.RESET_RILD;
         }
         ResetNetworkRequest request = new ResetNetworkRequest(resetOptions);
         if (mSubscriptions != null && mSubscriptions.size() > 0) {
diff --git a/src/com/android/settings/Settings.java b/src/com/android/settings/Settings.java
index 24d9525..493a27b 100644
--- a/src/com/android/settings/Settings.java
+++ b/src/com/android/settings/Settings.java
@@ -27,6 +27,8 @@
 import android.text.TextUtils;
 import android.util.Log;
 
+import androidx.annotation.Nullable;
+
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.settings.biometrics.face.FaceSettings;
 import com.android.settings.communal.CommunalPreferenceController;
@@ -35,6 +37,7 @@
 import com.android.settings.overlay.FeatureFactory;
 import com.android.settings.safetycenter.SafetyCenterManagerWrapper;
 import com.android.settings.security.SecuritySettingsFeatureProvider;
+import com.android.settings.wifi.WifiUtils;
 
 import com.google.android.setupdesign.util.ThemeHelper;
 
@@ -73,7 +76,18 @@
     public static class NetworkProviderSettingsActivity extends SettingsActivity { /* empty */ }
     public static class NetworkSelectActivity extends SettingsActivity { /* empty */ }
     /** Activity for the Wi-Fi network details settings. */
-    public static class WifiDetailsSettingsActivity extends SettingsActivity { /* empty */ }
+    public static class WifiDetailsSettingsActivity extends SettingsActivity {
+        @Override
+        protected void createUiFromIntent(@Nullable Bundle savedState, Intent intent) {
+            Bundle bundle = intent.getBundleExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS);
+            if (TextUtils.isEmpty(bundle.getString(WifiUtils.KEY_CHOSEN_WIFIENTRY_KEY))) {
+                Log.e(getLocalClassName(), "The key of WifiEntry is empty!");
+                finishAndRemoveTask();
+                return;
+            }
+            super.createUiFromIntent(savedState, intent);
+        }
+    }
     public static class WifiP2pSettingsActivity extends SettingsActivity { /* empty */ }
     public static class AvailableVirtualKeyboardActivity extends SettingsActivity { /* empty */ }
     public static class KeyboardLayoutPickerActivity extends SettingsActivity { /* empty */ }
diff --git a/src/com/android/settings/inputmethod/TouchGesturesButtonPreferenceController.java b/src/com/android/settings/inputmethod/TouchGesturesButtonPreferenceController.java
index 12dc076..71fa28b 100644
--- a/src/com/android/settings/inputmethod/TouchGesturesButtonPreferenceController.java
+++ b/src/com/android/settings/inputmethod/TouchGesturesButtonPreferenceController.java
@@ -21,11 +21,13 @@
 import android.app.settings.SettingsEnums;
 import android.content.Context;
 import android.content.Intent;
+import android.os.UserHandle;
 import android.util.FeatureFlagUtils;
 
 import androidx.fragment.app.Fragment;
 import androidx.preference.PreferenceScreen;
 
+import com.android.settings.Utils;
 import com.android.settings.core.BasePreferenceController;
 import com.android.settings.overlay.FeatureFactory;
 import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
@@ -79,10 +81,12 @@
     private void showTouchpadGestureEducation() {
         mMetricsFeatureProvider.action(mContext, SettingsEnums.ACTION_LEARN_TOUCHPAD_GESTURE_CLICK);
         if (newTouchpadGesturesTutorial()) {
-            Intent intent = new Intent(TUTORIAL_ACTION);
-            intent.addCategory(Intent.CATEGORY_DEFAULT);
-            intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-            mContext.startActivity(intent);
+            Intent intent = new Intent(TUTORIAL_ACTION)
+                    .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
+                    .setPackage(Utils.SYSTEMUI_PACKAGE_NAME);
+            // touchpad tutorial must be started as system user as it needs to have access to state
+            // of user 0 sysui instance
+            mContext.startActivityAsUser(intent, UserHandle.SYSTEM);
         } else {
             TrackpadGestureDialogFragment fragment = new TrackpadGestureDialogFragment();
             fragment.setTargetFragment(mParent, 0);
diff --git a/src/com/android/settings/notification/modes/AbstractZenModePreferenceController.java b/src/com/android/settings/notification/modes/AbstractZenModePreferenceController.java
index c473456..b27292a 100644
--- a/src/com/android/settings/notification/modes/AbstractZenModePreferenceController.java
+++ b/src/com/android/settings/notification/modes/AbstractZenModePreferenceController.java
@@ -28,7 +28,9 @@
 import androidx.annotation.VisibleForTesting;
 import androidx.preference.Preference;
 
+import com.android.settings.overlay.FeatureFactory;
 import com.android.settingslib.core.AbstractPreferenceController;
+import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
 import com.android.settingslib.notification.modes.ZenMode;
 import com.android.settingslib.notification.modes.ZenModesBackend;
 
@@ -43,13 +45,14 @@
 
     @Nullable protected final ZenModesBackend mBackend;
 
-
     @Nullable  // only until setZenMode() is called
     private ZenMode mZenMode;
 
     @NonNull
     private final String mKey;
 
+    @NonNull private final MetricsFeatureProvider mMetricsFeatureProvider;
+
     /**
      * Constructor suitable for "read-only" controllers (e.g. link to a different sub-screen.
      * Controllers that call this constructor to initialize themselves <em>cannot</em> call
@@ -59,6 +62,7 @@
         super(context);
         mKey = key;
         mBackend = null;
+        mMetricsFeatureProvider = FeatureFactory.getFeatureFactory().getMetricsFeatureProvider();
     }
 
     /**
@@ -71,6 +75,7 @@
         super(context);
         mKey = key;
         mBackend = backend;
+        mMetricsFeatureProvider = FeatureFactory.getFeatureFactory().getMetricsFeatureProvider();
     }
 
     @Override
@@ -79,6 +84,11 @@
         return mKey;
     }
 
+    @NonNull
+    public MetricsFeatureProvider getMetricsFeatureProvider() {
+        return mMetricsFeatureProvider;
+    }
+
     @Override
     public boolean isAvailable() {
         if (mZenMode != null) {
diff --git a/src/com/android/settings/notification/modes/SetupInterstitialActivity.java b/src/com/android/settings/notification/modes/SetupInterstitialActivity.java
index 8408624..c5beb36 100644
--- a/src/com/android/settings/notification/modes/SetupInterstitialActivity.java
+++ b/src/com/android/settings/notification/modes/SetupInterstitialActivity.java
@@ -29,6 +29,7 @@
 
 import android.annotation.SuppressLint;
 import android.app.ActionBar;
+import android.app.AutomaticZenRule;
 import android.app.settings.SettingsEnums;
 import android.content.Context;
 import android.content.Intent;
@@ -184,22 +185,28 @@
 
     private void setupButton(Button button, @NonNull ZenMode mode) {
         button.setText(getString(R.string.zen_mode_setup_button_label, mode.getName()));
-        button.setOnClickListener(enableButtonListener(mode.getId()));
+        button.setOnClickListener(enableButtonListener(mode.getId(), mode.getType()));
     }
 
     @VisibleForTesting
-    View.OnClickListener enableButtonListener(String modeId) {
+    View.OnClickListener enableButtonListener(String modeId, @AutomaticZenRule.Type int modeType) {
         return unused -> {
             // When clicked, we first reload mode info in case it has changed in the interim,
             // then enable the mode and then send the user to the mode's configuration page.
             boolean updated = enableMode(modeId);
 
+            int metricsCategory = switch (modeType) {
+                case TYPE_BEDTIME -> SettingsEnums.ZEN_MODE_INTERSTITIAL_BEDTIME;
+                case TYPE_DRIVING -> SettingsEnums.ZEN_MODE_INTERSTITIAL_DRIVING;
+                default -> SettingsEnums.ZEN_MODE_INTERSTITIAL;
+            };
+
             // Don't come back to this activity after sending the user to the modes page, if
             // they happen to go back. Forward the activity result in case we got here (indirectly)
             // from some app that is waiting for the result.
             if (updated) {
                 ZenSubSettingLauncher.forModeFragment(this, ZenModeFragment.class, modeId,
-                                SettingsEnums.ZEN_MODE_INTERSTITIAL)
+                                metricsCategory)
                         .addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT).launch();
             }
             finish();
diff --git a/src/com/android/settings/notification/modes/ZenModeTriggerUpdatePreferenceController.java b/src/com/android/settings/notification/modes/ZenModeTriggerUpdatePreferenceController.java
index 1add488..f2302c0 100644
--- a/src/com/android/settings/notification/modes/ZenModeTriggerUpdatePreferenceController.java
+++ b/src/com/android/settings/notification/modes/ZenModeTriggerUpdatePreferenceController.java
@@ -218,6 +218,8 @@
             }
             return zenMode;
         });
+        getMetricsFeatureProvider().action(mContext, SettingsEnums.ACTION_ZEN_MODE_ENABLE_TOGGLE,
+                enabled);
     }
 
     private void undoToggleSwitch(Preference preference, boolean wasSwitchedTo) {
diff --git a/src/com/android/settings/notification/modes/ZenModesListFragment.java b/src/com/android/settings/notification/modes/ZenModesListFragment.java
index 2b58f8e..588b320 100644
--- a/src/com/android/settings/notification/modes/ZenModesListFragment.java
+++ b/src/com/android/settings/notification/modes/ZenModesListFragment.java
@@ -23,6 +23,7 @@
 
 import androidx.annotation.Nullable;
 import androidx.annotation.VisibleForTesting;
+import androidx.lifecycle.Lifecycle;
 
 import com.android.settings.R;
 import com.android.settings.core.SubSettingLauncher;
@@ -62,9 +63,12 @@
 
     @Override
     protected void onUpdatedZenModeState() {
-        // TODO: b/322373473 -- update any overall description of modes state here if necessary.
-        // Note the preferences linking to individual rules do not need to be updated, as
-        // updateState() is called on all preference controllers whenever the page is resumed.
+        // Preferences linking to individual rules do not need to be updated as part of onStart(),
+        // because DashboardFragment does that in onResume(). However, we force the update if we
+        // detect Modes changes in the background with the page open.
+        if (getLifecycle().getCurrentState().isAtLeast(Lifecycle.State.RESUMED)) {
+            forceUpdatePreferences();
+        }
     }
 
     @Override
diff --git a/src/com/android/settings/notification/modes/ZenSettingsObserver.java b/src/com/android/settings/notification/modes/ZenSettingsObserver.java
index a853646..0f22d7d 100644
--- a/src/com/android/settings/notification/modes/ZenSettingsObserver.java
+++ b/src/com/android/settings/notification/modes/ZenSettingsObserver.java
@@ -16,6 +16,7 @@
 
 package com.android.settings.notification.modes;
 
+import android.app.Flags;
 import android.content.Context;
 import android.database.ContentObserver;
 import android.net.Uri;
@@ -42,13 +43,17 @@
     }
 
     void register() {
-        mContext.getContentResolver().registerContentObserver(ZEN_MODE_URI, false, this);
-        mContext.getContentResolver().registerContentObserver(ZEN_MODE_CONFIG_ETAG_URI, false,
-                this);
+        if (Flags.modesApi() && Flags.modesUi()) {
+            mContext.getContentResolver().registerContentObserver(ZEN_MODE_URI, false, this);
+            mContext.getContentResolver().registerContentObserver(ZEN_MODE_CONFIG_ETAG_URI, false,
+                    this);
+        }
     }
 
     void unregister() {
-        mContext.getContentResolver().unregisterContentObserver(this);
+        if (Flags.modesApi() && Flags.modesUi()) {
+            mContext.getContentResolver().unregisterContentObserver(this);
+        }
     }
 
     void setOnChangeListener(@Nullable Runnable callback) {
diff --git a/src/com/android/settings/sim/PreferredSimDialogFragment.java b/src/com/android/settings/sim/PreferredSimDialogFragment.java
index bb69440..96c0eb6 100644
--- a/src/com/android/settings/sim/PreferredSimDialogFragment.java
+++ b/src/com/android/settings/sim/PreferredSimDialogFragment.java
@@ -37,8 +37,8 @@
 import com.android.settings.network.SubscriptionUtil;
 
 /**
- * Presents a dialog asking the user if they want to update all services to use a given "preferred"
- * SIM. Typically this would be used in a case where a device goes from having multiple SIMs down to
+ * After androidV, presents a dialog asking the user if they want to update the mobile data.
+ * Typically this would be used in a case where a device goes from having multiple SIMs down to
  * only one.
  */
 public class PreferredSimDialogFragment extends SimDialogFragment implements
@@ -48,7 +48,7 @@
     public static PreferredSimDialogFragment newInstance() {
         final PreferredSimDialogFragment fragment = new PreferredSimDialogFragment();
         final Bundle args = initArguments(SimDialogActivity.PREFERRED_PICK,
-                R.string.sim_preferred_title);
+                R.string.select_specific_sim_for_data_title);
         fragment.setArguments(args);
         return fragment;
     }
@@ -57,7 +57,6 @@
     @Override
     public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) {
         final AlertDialog dialog = new AlertDialog.Builder(getContext())
-                .setTitle(getTitleResId())
                 .setPositiveButton(R.string.yes, this)
                 .setNegativeButton(R.string.no, null)
                 .create();
@@ -104,10 +103,18 @@
             dismiss();
             return;
         }
+        Log.d(TAG, "SubscriptionInfo: " + info);
+        final CharSequence simName =
+                SubscriptionUtil.getUniqueSubscriptionDisplayName(info, getContext());
+        final String title =
+                getContext().getString(
+                        getTitleResId(),
+                        simName);
         final String message =
                 getContext().getString(
                         R.string.sim_preferred_message,
-                        SubscriptionUtil.getUniqueSubscriptionDisplayName(info, getContext()));
+                        simName);
+        dialog.setTitle(title);
         dialog.setMessage(message);
     }
 
diff --git a/src/com/android/settings/sim/SimDialogActivity.java b/src/com/android/settings/sim/SimDialogActivity.java
index 2e19641..5512117 100644
--- a/src/com/android/settings/sim/SimDialogActivity.java
+++ b/src/com/android/settings/sim/SimDialogActivity.java
@@ -122,6 +122,7 @@
 
     private void showOrUpdateDialog() {
         final int dialogType = getIntent().getIntExtra(DIALOG_TYPE_KEY, INVALID_PICK);
+        Log.d(TAG, "dialogType:" + dialogType);
 
         if (dialogType == PICK_DISMISS) {
             finishAndRemoveTask();
@@ -379,8 +380,6 @@
 
     private void setPreferredSim(final int subId) {
         setDefaultDataSubId(subId);
-        setDefaultSmsSubId(subId);
-        setDefaultCallsSubId(subId);
     }
 
     private PhoneAccountHandle subscriptionIdToPhoneAccountHandle(final int subId) {
diff --git a/src/com/android/settings/wifi/details2/WifiPrivacyPageProvider.kt b/src/com/android/settings/wifi/details2/WifiPrivacyPageProvider.kt
index e41863c..7744a73 100644
--- a/src/com/android/settings/wifi/details2/WifiPrivacyPageProvider.kt
+++ b/src/com/android/settings/wifi/details2/WifiPrivacyPageProvider.kt
@@ -17,7 +17,6 @@
 package com.android.settings.wifi.details2
 
 import android.content.Context
-import android.net.wifi.WifiConfiguration
 import android.net.wifi.WifiManager
 import android.os.Bundle
 import android.os.Handler
@@ -114,19 +113,19 @@
                 }
             })
             wifiEntry.wifiConfiguration?.let {
-                DeviceNameSwitchPreference(it)
+                DeviceNameSwitchPreference(wifiEntry)
             }
         }
     }
 }
 
 @Composable
-fun DeviceNameSwitchPreference(wifiConfiguration: WifiConfiguration){
+fun DeviceNameSwitchPreference(wifiEntry: WifiEntry) {
     Spacer(modifier = Modifier.width(SettingsDimension.itemDividerHeight))
     CategoryTitle(title = stringResource(R.string.wifi_privacy_device_name_settings))
     Spacer(modifier = Modifier.width(SettingsDimension.itemDividerHeight))
     var checked by remember {
-        mutableStateOf(wifiConfiguration.isSendDhcpHostnameEnabled)
+        mutableStateOf(wifiEntry.wifiConfiguration?.isSendDhcpHostnameEnabled)
     }
     val context = LocalContext.current
     val wifiManager = context.getSystemService(WifiManager::class.java)!!
@@ -143,9 +142,11 @@
             }
         override val checked = { checked }
         override val onCheckedChange: (Boolean) -> Unit = { newChecked ->
-            wifiConfiguration.isSendDhcpHostnameEnabled = newChecked
-            wifiManager.save(wifiConfiguration, null /* listener */)
-            checked = newChecked
+            wifiEntry.wifiConfiguration?.let {
+                it.isSendDhcpHostnameEnabled = newChecked
+                wifiManager.save(it, null /* listener */)
+                checked = newChecked
+            }
         }
     })
 }
diff --git a/tests/robotests/src/com/android/settings/notification/modes/SetupInterstitialActivityTest.java b/tests/robotests/src/com/android/settings/notification/modes/SetupInterstitialActivityTest.java
index 40bf1c7..d60f073 100644
--- a/tests/robotests/src/com/android/settings/notification/modes/SetupInterstitialActivityTest.java
+++ b/tests/robotests/src/com/android/settings/notification/modes/SetupInterstitialActivityTest.java
@@ -128,7 +128,7 @@
                                 SetupInterstitialActivity.class)
                         .putExtra(EXTRA_AUTOMATIC_ZEN_RULE_ID, MODE_ID));
         scenario.onActivity(activity -> {
-            View.OnClickListener listener = activity.enableButtonListener(MODE_ID);
+            View.OnClickListener listener = activity.enableButtonListener(MODE_ID, mode.getType());
 
             // simulate button press even though we don't actually have a button
             listener.onClick(null);
diff --git a/tests/robotests/src/com/android/settings/notification/modes/ZenSettingsObserverTest.java b/tests/robotests/src/com/android/settings/notification/modes/ZenSettingsObserverTest.java
new file mode 100644
index 0000000..7fd47d9
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/notification/modes/ZenSettingsObserverTest.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2024 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.settings.notification.modes;
+
+import static android.provider.Settings.Global.ZEN_MODE_CONFIG_ETAG;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.robolectric.Shadows.shadowOf;
+
+import android.app.Flags;
+import android.content.Context;
+import android.database.ContentObserver;
+import android.net.Uri;
+import android.platform.test.annotations.DisableFlags;
+import android.platform.test.annotations.EnableFlags;
+import android.platform.test.flag.junit.SetFlagsRule;
+import android.provider.Settings;
+
+import androidx.test.core.app.ApplicationProvider;
+
+import com.google.common.collect.ImmutableList;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.shadows.ShadowLooper;
+
+import java.util.concurrent.atomic.AtomicInteger;
+
+@RunWith(RobolectricTestRunner.class)
+public class ZenSettingsObserverTest {
+
+    @Rule
+    public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+
+    private static final Uri SETTINGS_URI = Settings.Global.getUriFor(
+            ZEN_MODE_CONFIG_ETAG);
+
+    private Context mContext;
+    private ZenSettingsObserver mObserver;
+
+    @Before
+    public void setUp() {
+        mContext = ApplicationProvider.getApplicationContext();
+        mObserver = new ZenSettingsObserver(mContext);
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_MODES_UI)
+    public void register_withFlagEnabled_registersAndCallsBack() {
+        AtomicInteger someValue = new AtomicInteger();
+        mObserver.setOnChangeListener(someValue::incrementAndGet);
+        assertThat(getSettingsContentObservers()).isEmpty();
+
+        mObserver.register();
+        assertThat(getSettingsContentObservers()).hasSize(1);
+
+        getSettingsContentObservers().forEach(o -> o.dispatchChange(false, SETTINGS_URI));
+        ShadowLooper.idleMainLooper();
+        assertThat(someValue.get()).isEqualTo(1);
+
+        mObserver.unregister();
+        assertThat(getSettingsContentObservers()).isEmpty();
+    }
+
+    @Test
+    @DisableFlags(Flags.FLAG_MODES_UI)
+    public void register_withFlagDisabled_doesNotRegister() {
+        mObserver.register();
+        assertThat(getSettingsContentObservers()).isEmpty();
+        mObserver.unregister();
+        assertThat(getSettingsContentObservers()).isEmpty();
+    }
+
+    private ImmutableList<ContentObserver> getSettingsContentObservers() {
+        return ImmutableList.copyOf(
+                shadowOf(mContext.getContentResolver())
+                        .getContentObservers(SETTINGS_URI));
+    }
+}