diff --git a/res/values/strings.xml b/res/values/strings.xml
index ff585c3173f..264a40fd394 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -7948,8 +7948,55 @@
Allow visual signals
+
+ Notifications that can reach you
+
+ Additional actions
+
+
+
+ Display settings
+
+ Display options
+
+ Grayscale
+
+ grayscale
+
+ Change the screen to black and white
+
+ Keep the screen dark
+
+ keep the screen dark
+
+ Disable always on display
+
+ Dim the wallpaper
+
+ dim the wallpaper
+
+ Filter the brightness of the wallpaper
+
+ Enable dark theme
+
+ enable dark theme
+
+ Switch the OS and apps to prefer light text on a dark
+ background, which may be easier on the eyes and confers significant battery savings on some devices
+
+
+ {count, plural, offset:2
+ =0 {No display changes}
+ =1 {{effect_1}}
+ =2 {{effect_1} and {effect_2}}
+ =3 {{effect_1}, {effect_2}, and {effect_3}}
+ other {{effect_1}, {effect_2}, and # more}
+ }
+
+
- Display options for hidden notifications
+ Display options for filtered
+ notifications
When Do Not Disturb is on
@@ -7971,11 +8018,12 @@
Remove custom setting
- No sound from notifications
+ Notifications shown
- Partially hidden
+ Notifications partially
+ hidden
- No visuals or sound from notifications
+ Notifications hidden
Custom restrictions
diff --git a/res/xml/modes_display_settings.xml b/res/xml/modes_display_settings.xml
new file mode 100644
index 00000000000..53268ecc95d
--- /dev/null
+++ b/res/xml/modes_display_settings.xml
@@ -0,0 +1,45 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/res/xml/modes_notif_vis_settings.xml b/res/xml/modes_notif_vis_settings.xml
new file mode 100644
index 00000000000..551c704a24a
--- /dev/null
+++ b/res/xml/modes_notif_vis_settings.xml
@@ -0,0 +1,59 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/res/xml/modes_rule_settings.xml b/res/xml/modes_rule_settings.xml
index 7a08a68d8c0..a380987d377 100644
--- a/res/xml/modes_rule_settings.xml
+++ b/res/xml/modes_rule_settings.xml
@@ -22,12 +22,23 @@
android:key="header"
android:layout="@layout/settings_entity_header" />
-
+
+
-
+
+
+
+
+
\ No newline at end of file
diff --git a/src/com/android/settings/notification/modes/ZenMode.java b/src/com/android/settings/notification/modes/ZenMode.java
index 51c92e6eae5..ea94f7d8c8f 100644
--- a/src/com/android/settings/notification/modes/ZenMode.java
+++ b/src/com/android/settings/notification/modes/ZenMode.java
@@ -90,7 +90,7 @@ class ZenMode {
.build();
private final String mId;
- private final AutomaticZenRule mRule;
+ private AutomaticZenRule mRule;
private final boolean mIsActive;
private final boolean mIsManualDnd;
@@ -190,6 +190,14 @@ class ZenMode {
}
}
+ /**
+ * Use sparingly. If you're updating a policy field, use
+ * {@link #setPolicy(android.service.notification.ZenPolicy)} instead.
+ */
+ public void setAzr(@NonNull AutomaticZenRule newRule) {
+ mRule = newRule;
+ }
+
/**
* Updates the {@link ZenPolicy} of the associated {@link AutomaticZenRule} based on the
* supplied policy. In some cases this involves conversions, so that the following call
diff --git a/src/com/android/settings/notification/modes/ZenModeDisplayEffectPreferenceController.java b/src/com/android/settings/notification/modes/ZenModeDisplayEffectPreferenceController.java
new file mode 100644
index 00000000000..f69d0d63fac
--- /dev/null
+++ b/src/com/android/settings/notification/modes/ZenModeDisplayEffectPreferenceController.java
@@ -0,0 +1,94 @@
+/*
+ * 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.service.notification.ZenPolicy.PRIORITY_CATEGORY_ALARMS;
+import static android.service.notification.ZenPolicy.PRIORITY_CATEGORY_EVENTS;
+import static android.service.notification.ZenPolicy.PRIORITY_CATEGORY_MEDIA;
+import static android.service.notification.ZenPolicy.PRIORITY_CATEGORY_REMINDERS;
+import static android.service.notification.ZenPolicy.PRIORITY_CATEGORY_SYSTEM;
+
+import android.app.AutomaticZenRule;
+import android.content.Context;
+import android.service.notification.ZenDeviceEffects;
+import android.service.notification.ZenPolicy;
+import androidx.preference.Preference;
+import androidx.preference.TwoStatePreference;
+
+public class ZenModeDisplayEffectPreferenceController extends AbstractZenModePreferenceController
+ implements Preference.OnPreferenceChangeListener {
+
+ public ZenModeDisplayEffectPreferenceController(Context context, String key,
+ ZenModesBackend backend) {
+ super(context, key, backend);
+ }
+
+ @Override
+ public void updateState(Preference preference) {
+ TwoStatePreference pref = (TwoStatePreference) preference;
+ ZenDeviceEffects effects = getMode().getRule().getDeviceEffects();
+ if (effects == null) {
+ pref.setChecked(false);
+ } else {
+ switch (getPreferenceKey()) {
+ case "effect_greyscale":
+ pref.setChecked(effects.shouldDisplayGrayscale());
+ break;
+ case "effect_aod":
+ pref.setChecked(effects.shouldSuppressAmbientDisplay());
+ break;
+ case "effect_wallpaper":
+ pref.setChecked(effects.shouldDimWallpaper());
+ break;
+ case "effect_dark_theme":
+ pref.setChecked(effects.shouldUseNightMode());
+ break;
+ }
+ }
+ }
+
+ @Override
+ public boolean onPreferenceChange(Preference preference, Object newValue) {
+ final boolean allow = (Boolean) newValue;
+
+ ZenDeviceEffects currEffects = getMode().getRule().getDeviceEffects();
+ ZenDeviceEffects.Builder updatedEffects = currEffects == null
+ ? new ZenDeviceEffects.Builder()
+ : new ZenDeviceEffects.Builder(getMode().getRule().getDeviceEffects());
+ switch (getPreferenceKey()) {
+ case "effect_greyscale":
+ updatedEffects.setShouldDisplayGrayscale(allow);
+ break;
+ case "effect_aod":
+ updatedEffects.setShouldSuppressAmbientDisplay(allow);
+ break;
+ case "effect_wallpaper":
+ updatedEffects.setShouldDimWallpaper(allow);
+ break;
+ case "effect_dark_theme":
+ updatedEffects.setShouldUseNightMode(allow);
+ break;
+ }
+ AutomaticZenRule updatedAzr = new AutomaticZenRule.Builder(getMode().getRule())
+ .setDeviceEffects(updatedEffects.build())
+ .build();
+ getMode().setAzr(updatedAzr);
+ mBackend.updateMode(getMode());
+
+ return true;
+ }
+}
diff --git a/src/com/android/settings/notification/modes/ZenModeDisplayFragment.java b/src/com/android/settings/notification/modes/ZenModeDisplayFragment.java
new file mode 100644
index 00000000000..09720495ed1
--- /dev/null
+++ b/src/com/android/settings/notification/modes/ZenModeDisplayFragment.java
@@ -0,0 +1,59 @@
+/*
+ * 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 android.app.settings.SettingsEnums;
+import android.content.Context;
+import com.android.settings.R;
+import com.android.settingslib.core.AbstractPreferenceController;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Settings page that shows what device effects/notification visuals will change when this mode
+ * is on.
+ */
+public class ZenModeDisplayFragment extends ZenModeFragmentBase {
+
+ @Override
+ protected List createPreferenceControllers(Context context) {
+ List prefControllers = new ArrayList<>();
+ prefControllers.add(new ZenModeNotifVisLinkPreferenceController(
+ context, "notification_visibility", mBackend));
+ prefControllers.add(new ZenModeDisplayEffectPreferenceController(
+ context, "effect_greyscale", mBackend));
+ prefControllers.add(new ZenModeDisplayEffectPreferenceController(
+ context, "effect_aod", mBackend));
+ prefControllers.add(new ZenModeDisplayEffectPreferenceController(
+ context, "effect_wallpaper", mBackend));
+ prefControllers.add(new ZenModeDisplayEffectPreferenceController(
+ context, "effect_dark_theme", mBackend));
+ return prefControllers;
+ }
+
+ @Override
+ protected int getPreferenceScreenResId() {
+ return R.xml.modes_display_settings;
+ }
+
+ @Override
+ public int getMetricsCategory() {
+ // TODO: b/332937635 - make this the correct metrics category
+ return SettingsEnums.DND_PEOPLE;
+ }
+}
diff --git a/src/com/android/settings/notification/modes/ZenModeDisplayLinkPreferenceController.java b/src/com/android/settings/notification/modes/ZenModeDisplayLinkPreferenceController.java
new file mode 100644
index 00000000000..943874a5d48
--- /dev/null
+++ b/src/com/android/settings/notification/modes/ZenModeDisplayLinkPreferenceController.java
@@ -0,0 +1,53 @@
+/*
+ * 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 com.android.settings.notification.modes.ZenModeFragmentBase.MODE_ID;
+
+import android.content.Context;
+import android.os.Bundle;
+import androidx.preference.Preference;
+import com.android.settings.core.SubSettingLauncher;
+
+public class ZenModeDisplayLinkPreferenceController extends AbstractZenModePreferenceController {
+
+ ZenModeSummaryHelper mSummaryHelper;
+
+ public ZenModeDisplayLinkPreferenceController(Context context, String key,
+ ZenModesBackend backend) {
+ super(context, key, backend);
+ mSummaryHelper = new ZenModeSummaryHelper(context, backend);
+ }
+
+ @Override
+ public void updateState(Preference preference) {
+ super.updateState(preference);
+ Bundle bundle = new Bundle();
+ bundle.putString(MODE_ID, getMode().getId());
+ // TODO(b/332937635): Update metrics category
+ preference.setIntent(new SubSettingLauncher(mContext)
+ .setDestination(ZenModeDisplayFragment.class.getName())
+ .setSourceMetricsCategory(0)
+ .setArguments(bundle)
+ .toIntent());
+ }
+
+ @Override
+ public CharSequence getSummary() {
+ return mSummaryHelper.getDisplayEffectsSummary(getMode());
+ }
+}
\ No newline at end of file
diff --git a/src/com/android/settings/notification/modes/ZenModeFragment.java b/src/com/android/settings/notification/modes/ZenModeFragment.java
index 51772f00a45..7f805eeb669 100644
--- a/src/com/android/settings/notification/modes/ZenModeFragment.java
+++ b/src/com/android/settings/notification/modes/ZenModeFragment.java
@@ -35,14 +35,14 @@ public class ZenModeFragment extends ZenModeFragmentBase {
@Override
protected List createPreferenceControllers(Context context) {
- // TODO: fill in with all the elements of this page. Each should be an instance of
- // {@link AbstractZenModePreferenceController}.
List prefControllers = new ArrayList<>();
prefControllers.add(new ZenModeHeaderController(context, "header", this, mBackend));
prefControllers.add(new ZenModePeopleLinkPreferenceController(
context, "zen_mode_people", mBackend));
prefControllers.add(new ZenModeOtherLinkPreferenceController(
context, "zen_other_settings", mBackend));
+ prefControllers.add(new ZenModeDisplayLinkPreferenceController(
+ context, "mode_display_settings", mBackend));
return prefControllers;
}
diff --git a/src/com/android/settings/notification/modes/ZenModeNotifVisFragment.java b/src/com/android/settings/notification/modes/ZenModeNotifVisFragment.java
new file mode 100644
index 00000000000..3fdfec6e106
--- /dev/null
+++ b/src/com/android/settings/notification/modes/ZenModeNotifVisFragment.java
@@ -0,0 +1,63 @@
+/*
+ * 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 android.app.settings.SettingsEnums;
+import android.content.Context;
+import android.service.notification.ZenPolicy;
+import com.android.settings.R;
+import com.android.settingslib.core.AbstractPreferenceController;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Settings page that shows what notification visuals will change when this mode is on.
+ */
+public class ZenModeNotifVisFragment extends ZenModeFragmentBase {
+ @Override
+ protected List createPreferenceControllers(Context context) {
+ List prefControllers = new ArrayList<>();
+ prefControllers.add(new ZenModeNotifVisPreferenceController(context,
+ "zen_effect_intent", ZenPolicy.VISUAL_EFFECT_FULL_SCREEN_INTENT, null, mBackend));
+ prefControllers.add(new ZenModeNotifVisPreferenceController(context,
+ "zen_effect_light", ZenPolicy.VISUAL_EFFECT_LIGHTS, null, mBackend));
+ prefControllers.add(new ZenModeNotifVisPreferenceController(context,
+ "zen_effect_peek", ZenPolicy.VISUAL_EFFECT_PEEK, null, mBackend));
+ prefControllers.add(new ZenModeNotifVisPreferenceController(context,
+ "zen_effect_status", ZenPolicy.VISUAL_EFFECT_STATUS_BAR,
+ new int[] {ZenPolicy.VISUAL_EFFECT_NOTIFICATION_LIST}, mBackend));
+ prefControllers.add(new ZenModeNotifVisPreferenceController(context,
+ "zen_effect_badge", ZenPolicy.VISUAL_EFFECT_BADGE, null, mBackend));
+ prefControllers.add(new ZenModeNotifVisPreferenceController(context,
+ "zen_effect_ambient", ZenPolicy.VISUAL_EFFECT_AMBIENT, null, mBackend));
+ prefControllers.add(new ZenModeNotifVisPreferenceController(context,
+ "zen_effect_list", ZenPolicy.VISUAL_EFFECT_NOTIFICATION_LIST, null, mBackend));
+ return prefControllers;
+ }
+
+ @Override
+ protected int getPreferenceScreenResId() {
+ return R.xml.modes_notif_vis_settings;
+ }
+
+ @Override
+ public int getMetricsCategory() {
+ // TODO: b/332937635 - make this the correct metrics category
+ return SettingsEnums.DND_PEOPLE;
+ }
+}
diff --git a/src/com/android/settings/notification/modes/ZenModeNotifVisLinkPreferenceController.java b/src/com/android/settings/notification/modes/ZenModeNotifVisLinkPreferenceController.java
new file mode 100644
index 00000000000..9807431fb17
--- /dev/null
+++ b/src/com/android/settings/notification/modes/ZenModeNotifVisLinkPreferenceController.java
@@ -0,0 +1,52 @@
+/*
+ * 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 com.android.settings.notification.modes.ZenModeFragmentBase.MODE_ID;
+
+import android.content.Context;
+import android.os.Bundle;
+import androidx.preference.Preference;
+import com.android.settings.core.SubSettingLauncher;
+
+public class ZenModeNotifVisLinkPreferenceController extends AbstractZenModePreferenceController {
+
+ ZenModeSummaryHelper mSummaryBuilder;
+ public ZenModeNotifVisLinkPreferenceController(Context context, String key,
+ ZenModesBackend backend) {
+ super(context, key, backend);
+ mSummaryBuilder = new ZenModeSummaryHelper(context, backend);
+ }
+
+ @Override
+ public void updateState(Preference preference) {
+ super.updateState(preference);
+ Bundle bundle = new Bundle();
+ bundle.putString(MODE_ID, getMode().getId());
+ // TODO(b/332937635): Update metrics category
+ preference.setIntent(new SubSettingLauncher(mContext)
+ .setDestination(ZenModeNotifVisFragment.class.getName())
+ .setSourceMetricsCategory(0)
+ .setArguments(bundle)
+ .toIntent());
+ }
+
+ @Override
+ public CharSequence getSummary() {
+ return mSummaryBuilder.getBlockedEffectsSummary(getMode());
+ }
+}
\ No newline at end of file
diff --git a/src/com/android/settings/notification/modes/ZenModeNotifVisPreferenceController.java b/src/com/android/settings/notification/modes/ZenModeNotifVisPreferenceController.java
new file mode 100644
index 00000000000..9b89a646811
--- /dev/null
+++ b/src/com/android/settings/notification/modes/ZenModeNotifVisPreferenceController.java
@@ -0,0 +1,91 @@
+/*
+ * 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 android.content.Context;
+import android.service.notification.ZenPolicy;
+import androidx.annotation.VisibleForTesting;
+import androidx.preference.CheckBoxPreference;
+import androidx.preference.Preference;
+import com.android.settings.widget.DisabledCheckBoxPreference;
+
+public class ZenModeNotifVisPreferenceController extends AbstractZenModePreferenceController
+ implements Preference.OnPreferenceChangeListener {
+
+ @VisibleForTesting protected @ZenPolicy.VisualEffect int mEffect;
+
+ // if any of these effects are suppressed, this effect must be too
+ @VisibleForTesting protected @ZenPolicy.VisualEffect int[] mParentSuppressedEffects;
+
+ public ZenModeNotifVisPreferenceController(Context context, String key,
+ @ZenPolicy.VisualEffect int visualEffect,
+ @ZenPolicy.VisualEffect int[] parentSuppressedEffects, ZenModesBackend backend) {
+ super(context, key, backend);
+ mEffect = visualEffect;
+ mParentSuppressedEffects = parentSuppressedEffects;
+ }
+
+ @Override
+ public boolean isAvailable() {
+ if (!super.isAvailable()) {
+ return false;
+ }
+
+ if (mEffect == ZenPolicy.VISUAL_EFFECT_LIGHTS) {
+ return mContext.getResources()
+ .getBoolean(com.android.internal.R.bool.config_intrusiveNotificationLed);
+ }
+ return true;
+ }
+
+ @Override
+ public void updateState(Preference preference) {
+
+ boolean suppressed = !getMode().getPolicy().isVisualEffectAllowed(mEffect, false);
+ boolean parentSuppressed = false;
+ if (mParentSuppressedEffects != null) {
+ for (@ZenPolicy.VisualEffect int parentEffect : mParentSuppressedEffects) {
+ if (!getMode().getPolicy().isVisualEffectAllowed(parentEffect, true)) {
+ parentSuppressed = true;
+ }
+ }
+ }
+ if (parentSuppressed) {
+ ((CheckBoxPreference) preference).setChecked(true);
+ onPreferenceChange(preference, true);
+ ((DisabledCheckBoxPreference) preference).enableCheckbox(false);
+ } else {
+ ((DisabledCheckBoxPreference) preference).enableCheckbox(true);
+ ((CheckBoxPreference) preference).setChecked(suppressed);
+ }
+ }
+
+ @Override
+ public boolean onPreferenceChange(Preference preference, Object newValue) {
+ final boolean allowEffect = !((Boolean) newValue);
+
+ if (getMode().getPolicy().isVisualEffectAllowed(mEffect, true) != allowEffect) {
+ ZenPolicy diffPolicy = new ZenPolicy.Builder()
+ .showVisualEffect(mEffect, allowEffect)
+ .build();
+ getMode().setPolicy(diffPolicy);
+ mBackend.updateMode(getMode());
+ }
+
+ return true;
+ }
+}
diff --git a/src/com/android/settings/notification/modes/ZenModeSummaryHelper.java b/src/com/android/settings/notification/modes/ZenModeSummaryHelper.java
index cf0c3db7499..e1a7cafa786 100644
--- a/src/com/android/settings/notification/modes/ZenModeSummaryHelper.java
+++ b/src/com/android/settings/notification/modes/ZenModeSummaryHelper.java
@@ -30,11 +30,21 @@ import static android.service.notification.ZenPolicy.PRIORITY_CATEGORY_MESSAGES;
import static android.service.notification.ZenPolicy.PRIORITY_CATEGORY_REMINDERS;
import static android.service.notification.ZenPolicy.PRIORITY_CATEGORY_REPEAT_CALLERS;
import static android.service.notification.ZenPolicy.PRIORITY_CATEGORY_SYSTEM;
+import static android.service.notification.ZenPolicy.STATE_DISALLOW;
+import static android.service.notification.ZenPolicy.VISUAL_EFFECT_AMBIENT;
+import static android.service.notification.ZenPolicy.VISUAL_EFFECT_BADGE;
+import static android.service.notification.ZenPolicy.VISUAL_EFFECT_FULL_SCREEN_INTENT;
+import static android.service.notification.ZenPolicy.VISUAL_EFFECT_LIGHTS;
+import static android.service.notification.ZenPolicy.VISUAL_EFFECT_NOTIFICATION_LIST;
+import static android.service.notification.ZenPolicy.VISUAL_EFFECT_PEEK;
+import static android.service.notification.ZenPolicy.VISUAL_EFFECT_STATUS_BAR;
import android.content.Context;
import android.icu.text.MessageFormat;
+import android.service.notification.ZenDeviceEffects;
import android.service.notification.ZenPolicy;
+import android.util.SparseArray;
import com.android.settings.R;
import java.util.ArrayList;
@@ -129,10 +139,22 @@ public class ZenModeSummaryHelper {
}
String getBlockedEffectsSummary(ZenMode zenMode) {
- if (zenMode.getPolicy().shouldShowAllVisualEffects()) {
+ List relevantVisualEffects = new ArrayList<>();
+ relevantVisualEffects.add(VISUAL_EFFECT_FULL_SCREEN_INTENT);
+ relevantVisualEffects.add(VISUAL_EFFECT_PEEK);
+ relevantVisualEffects.add(VISUAL_EFFECT_STATUS_BAR);
+ relevantVisualEffects.add(VISUAL_EFFECT_BADGE);
+ relevantVisualEffects.add(VISUAL_EFFECT_AMBIENT);
+ relevantVisualEffects.add(VISUAL_EFFECT_NOTIFICATION_LIST);
+ if (mContext.getResources()
+ .getBoolean(com.android.internal.R.bool.config_intrusiveNotificationLed)) {
+ relevantVisualEffects.add(VISUAL_EFFECT_LIGHTS);
+ }
+
+ if (shouldShowAllVisualEffects(zenMode.getPolicy(), relevantVisualEffects)) {
return mContext.getResources().getString(
R.string.zen_mode_restrict_notifications_summary_muted);
- } else if (zenMode.getPolicy().shouldHideAllVisualEffects()) {
+ } else if (shouldHideAllVisualEffects(zenMode.getPolicy(), relevantVisualEffects)) {
return mContext.getResources().getString(
R.string.zen_mode_restrict_notifications_summary_hidden);
} else {
@@ -141,6 +163,89 @@ public class ZenModeSummaryHelper {
}
}
+ private boolean shouldShowAllVisualEffects(ZenPolicy policy, List relevantEffects) {
+ for (int i = 0; i < relevantEffects.size(); i++) {
+ if (!policy.isVisualEffectAllowed(relevantEffects.get(i), false)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private boolean shouldHideAllVisualEffects(ZenPolicy policy, List relevantEffects) {
+ for (int i = 0; i < relevantEffects.size(); i++) {
+ if (policy.isVisualEffectAllowed(relevantEffects.get(i), false)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ String getDisplayEffectsSummary(ZenMode zenMode) {
+ boolean isFirst = true;
+ List enabledEffects = new ArrayList<>();
+ if (!zenMode.getPolicy().shouldShowAllVisualEffects()) {
+ enabledEffects.add(getBlockedEffectsSummary(zenMode));
+ isFirst = false;
+ }
+ ZenDeviceEffects currEffects = zenMode.getRule().getDeviceEffects();
+ if (currEffects != null) {
+ if (currEffects.shouldDisplayGrayscale()) {
+ if (isFirst) {
+ enabledEffects.add(mContext.getString(R.string.mode_grayscale_title));
+ } else {
+ enabledEffects.add(mContext.getString(
+ R.string.mode_grayscale_title_secondary_list));
+ }
+ isFirst = false;
+ }
+ if (currEffects.shouldSuppressAmbientDisplay()) {
+ if (isFirst) {
+ enabledEffects.add(mContext.getString(R.string.mode_aod_title));
+ } else {
+ enabledEffects.add(mContext.getString(
+ R.string.mode_aod_title_secondary_list));
+ }
+ isFirst = false;
+ }
+ if (currEffects.shouldDimWallpaper()) {
+ if (isFirst) {
+ enabledEffects.add(mContext.getString(R.string.mode_wallpaper_title));
+ } else {
+ enabledEffects.add(mContext.getString(
+ R.string.mode_wallpaper_title_secondary_list));
+ }
+ isFirst = false;
+ }
+ if (currEffects.shouldUseNightMode()) {
+ if (isFirst) {
+ enabledEffects.add(mContext.getString(R.string.mode_dark_theme_title));
+ } else {
+ enabledEffects.add(mContext.getString(
+ R.string.mode_dark_theme_title_secondary_list));
+ }
+ isFirst = false;
+ }
+ }
+
+ int numCategories = enabledEffects.size();
+ MessageFormat msgFormat = new MessageFormat(
+ mContext.getString(R.string.mode_display_settings_summary),
+ Locale.getDefault());
+ Map args = new HashMap<>();
+ args.put("count", numCategories);
+ if (numCategories >= 1) {
+ args.put("effect_1", enabledEffects.get(0));
+ if (numCategories >= 2) {
+ args.put("effect_2", enabledEffects.get(1));
+ if (numCategories == 3) {
+ args.put("effect_3", enabledEffects.get(2));
+ }
+ }
+ }
+ return msgFormat.format(args);
+ }
+
private List getEnabledCategories(ZenPolicy policy,
Predicate filteredCategories, boolean capitalizeFirstInList) {
List enabledCategories = new ArrayList<>();
diff --git a/tests/robotests/src/com/android/settings/notification/modes/ZenModeDisplayEffectPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/modes/ZenModeDisplayEffectPreferenceControllerTest.java
new file mode 100644
index 00000000000..1a62b75468a
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/notification/modes/ZenModeDisplayEffectPreferenceControllerTest.java
@@ -0,0 +1,252 @@
+/*
+ * 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.app.NotificationManager.INTERRUPTION_FILTER_PRIORITY;
+import static android.service.notification.ZenPolicy.STATE_ALLOW;
+import static android.service.notification.ZenPolicy.STATE_UNSET;
+import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+
+import android.app.AutomaticZenRule;
+import android.app.Flags;
+import android.content.Context;
+import android.net.Uri;
+import android.platform.test.annotations.EnableFlags;
+import android.platform.test.flag.junit.SetFlagsRule;
+import android.service.notification.ZenDeviceEffects;
+import android.service.notification.ZenPolicy;
+import androidx.preference.TwoStatePreference;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+
+@RunWith(RobolectricTestRunner.class)
+@EnableFlags(Flags.FLAG_MODES_UI)
+public final class ZenModeDisplayEffectPreferenceControllerTest {
+
+ private Context mContext;
+ @Mock
+ private ZenModesBackend mBackend;
+
+ @Rule
+ public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+
+ @Before
+ public void setup() {
+ MockitoAnnotations.initMocks(this);
+ mContext = RuntimeEnvironment.application;
+ }
+
+ @Test
+ public void testUpdateState_grayscale() {
+ TwoStatePreference preference = mock(TwoStatePreference.class);
+ ZenMode zenMode = new ZenMode("id",
+ new AutomaticZenRule.Builder("Driving", Uri.parse("drive"))
+ .setType(AutomaticZenRule.TYPE_DRIVING)
+ .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
+ .setZenPolicy(new ZenPolicy.Builder().allowAlarms(true).build())
+ .setDeviceEffects(new ZenDeviceEffects.Builder()
+ .setShouldDisplayGrayscale(true)
+ .build())
+ .build(), true);
+
+ ZenModeDisplayEffectPreferenceController controller =
+ new ZenModeDisplayEffectPreferenceController(
+ mContext, "effect_greyscale", mBackend);
+
+ controller.updateZenMode(preference, zenMode);
+
+ verify(preference).setChecked(true);
+ }
+
+ @Test
+ public void testOnPreferenceChange_grayscale() {
+ TwoStatePreference preference = mock(TwoStatePreference.class);
+ ZenMode zenMode = new ZenMode("id",
+ new AutomaticZenRule.Builder("Driving", Uri.parse("drive"))
+ .setType(AutomaticZenRule.TYPE_DRIVING)
+ .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
+ .setZenPolicy(new ZenPolicy.Builder().allowAlarms(false).build())
+ .setDeviceEffects(new ZenDeviceEffects.Builder()
+ .setShouldDisplayGrayscale(true)
+ .build())
+ .build(), true);
+
+ ZenModeDisplayEffectPreferenceController controller =
+ new ZenModeDisplayEffectPreferenceController(mContext, "effect_greyscale", mBackend);
+
+ controller.updateZenMode(preference, zenMode);
+
+ controller.onPreferenceChange(preference, false);
+
+ ArgumentCaptor captor = ArgumentCaptor.forClass(ZenMode.class);
+ verify(mBackend).updateMode(captor.capture());
+ assertThat(captor.getValue().getRule().getDeviceEffects().shouldDisplayGrayscale())
+ .isFalse();
+ }
+
+ @Test
+ public void testUpdateState_aod() {
+ TwoStatePreference preference = mock(TwoStatePreference.class);
+ ZenMode zenMode = new ZenMode("id",
+ new AutomaticZenRule.Builder("Driving", Uri.parse("drive"))
+ .setType(AutomaticZenRule.TYPE_DRIVING)
+ .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
+ .setZenPolicy(new ZenPolicy.Builder().allowMedia(true).build())
+ .setDeviceEffects(new ZenDeviceEffects.Builder()
+ .setShouldSuppressAmbientDisplay(true)
+ .build())
+ .build(), true);
+
+ ZenModeDisplayEffectPreferenceController controller =
+ new ZenModeDisplayEffectPreferenceController(mContext, "effect_aod", mBackend);
+
+ controller.updateZenMode(preference, zenMode);
+
+ verify(preference).setChecked(true);
+ }
+
+ @Test
+ public void testOnPreferenceChange_aod() {
+ TwoStatePreference preference = mock(TwoStatePreference.class);
+ ZenMode zenMode = new ZenMode("id",
+ new AutomaticZenRule.Builder("Driving", Uri.parse("drive"))
+ .setType(AutomaticZenRule.TYPE_DRIVING)
+ .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
+ .setZenPolicy(new ZenPolicy.Builder().allowMedia(false).build())
+ .setDeviceEffects(new ZenDeviceEffects.Builder()
+ .setShouldSuppressAmbientDisplay(true)
+ .build())
+ .build(), true);
+
+ ZenModeDisplayEffectPreferenceController controller =
+ new ZenModeDisplayEffectPreferenceController(mContext, "effect_aod", mBackend);
+
+ controller.updateZenMode(preference, zenMode);
+
+ controller.onPreferenceChange(preference, false);
+
+ ArgumentCaptor captor = ArgumentCaptor.forClass(ZenMode.class);
+ verify(mBackend).updateMode(captor.capture());
+ assertThat(captor.getValue().getRule().getDeviceEffects().shouldSuppressAmbientDisplay())
+ .isFalse();
+ }
+
+ @Test
+ public void testUpdateState_wallpaper() {
+ TwoStatePreference preference = mock(TwoStatePreference.class);
+ ZenMode zenMode = new ZenMode("id",
+ new AutomaticZenRule.Builder("Driving", Uri.parse("drive"))
+ .setType(AutomaticZenRule.TYPE_DRIVING)
+ .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
+ .setZenPolicy(new ZenPolicy.Builder().allowSystem(true).build())
+ .setDeviceEffects(new ZenDeviceEffects.Builder()
+ .setShouldDimWallpaper(true)
+ .build())
+ .build(), true);
+
+ ZenModeDisplayEffectPreferenceController controller =
+ new ZenModeDisplayEffectPreferenceController(
+ mContext, "effect_wallpaper", mBackend);
+
+ controller.updateZenMode(preference, zenMode);
+
+ verify(preference).setChecked(true);
+ }
+
+ @Test
+ public void testOnPreferenceChange_wallpaper() {
+ TwoStatePreference preference = mock(TwoStatePreference.class);
+ ZenMode zenMode = new ZenMode("id",
+ new AutomaticZenRule.Builder("Driving", Uri.parse("drive"))
+ .setType(AutomaticZenRule.TYPE_DRIVING)
+ .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
+ .setZenPolicy(new ZenPolicy.Builder().allowSystem(false).build())
+ .setDeviceEffects(new ZenDeviceEffects.Builder()
+ .setShouldDimWallpaper(true)
+ .build())
+ .build(), true);
+
+ ZenModeDisplayEffectPreferenceController controller =
+ new ZenModeDisplayEffectPreferenceController(
+ mContext, "effect_wallpaper", mBackend);
+
+ controller.updateZenMode(preference, zenMode);
+
+ controller.onPreferenceChange(preference, false);
+
+ ArgumentCaptor captor = ArgumentCaptor.forClass(ZenMode.class);
+ verify(mBackend).updateMode(captor.capture());
+ assertThat(captor.getValue().getRule().getDeviceEffects().shouldDimWallpaper()).isFalse();
+ }
+
+ @Test
+ public void testUpdateState_darkTheme() {
+ TwoStatePreference preference = mock(TwoStatePreference.class);
+ ZenMode zenMode = new ZenMode("id",
+ new AutomaticZenRule.Builder("Driving", Uri.parse("drive"))
+ .setType(AutomaticZenRule.TYPE_DRIVING)
+ .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
+ .setZenPolicy(new ZenPolicy.Builder().allowReminders(true).build())
+ .setDeviceEffects(new ZenDeviceEffects.Builder()
+ .setShouldUseNightMode(true)
+ .build())
+ .build(), true);
+
+ ZenModeDisplayEffectPreferenceController controller =
+ new ZenModeDisplayEffectPreferenceController(mContext, "effect_dark_theme",
+ mBackend);
+
+ controller.updateZenMode(preference, zenMode);
+
+ verify(preference).setChecked(true);
+ }
+
+ @Test
+ public void testOnPreferenceChange_darkTheme() {
+ TwoStatePreference preference = mock(TwoStatePreference.class);
+ ZenMode zenMode = new ZenMode("id",
+ new AutomaticZenRule.Builder("Driving", Uri.parse("drive"))
+ .setType(AutomaticZenRule.TYPE_DRIVING)
+ .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
+ .setZenPolicy(new ZenPolicy.Builder().allowReminders(false).build())
+ .setDeviceEffects(new ZenDeviceEffects.Builder()
+ .setShouldUseNightMode(true)
+ .build())
+ .build(), true);
+
+ ZenModeDisplayEffectPreferenceController controller =
+ new ZenModeDisplayEffectPreferenceController(mContext, "effect_dark_theme",
+ mBackend);
+
+ controller.updateZenMode(preference, zenMode);
+
+ controller.onPreferenceChange(preference, false);
+
+ ArgumentCaptor captor = ArgumentCaptor.forClass(ZenMode.class);
+ verify(mBackend).updateMode(captor.capture());
+ assertThat(captor.getValue().getRule().getDeviceEffects().shouldUseNightMode()).isFalse();
+ }
+}
\ No newline at end of file
diff --git a/tests/robotests/src/com/android/settings/notification/modes/ZenModeDisplayLinkPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/modes/ZenModeDisplayLinkPreferenceControllerTest.java
new file mode 100644
index 00000000000..9d33b0b7600
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/notification/modes/ZenModeDisplayLinkPreferenceControllerTest.java
@@ -0,0 +1,76 @@
+/*
+ * 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.app.NotificationManager.INTERRUPTION_FILTER_PRIORITY;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+
+import android.app.AutomaticZenRule;
+import android.app.Flags;
+import android.content.Context;
+import android.net.Uri;
+import android.platform.test.annotations.EnableFlags;
+import android.platform.test.flag.junit.SetFlagsRule;
+import android.service.notification.ZenPolicy;
+import androidx.preference.Preference;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+
+@RunWith(RobolectricTestRunner.class)
+public final class ZenModeDisplayLinkPreferenceControllerTest {
+
+ private ZenModeDisplayLinkPreferenceController mController;
+
+ @Rule
+ public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+
+ private Context mContext;
+ @Mock
+ private ZenModesBackend mBackend;
+
+ @Before
+ public void setup() {
+ MockitoAnnotations.initMocks(this);
+
+ mContext = RuntimeEnvironment.application;
+
+ mController = new ZenModeDisplayLinkPreferenceController(
+ mContext, "something", mBackend);
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_MODES_UI)
+ public void testHasSummary() {
+ Preference pref = mock(Preference.class);
+ ZenMode zenMode = new ZenMode("id",
+ new AutomaticZenRule.Builder("Driving", Uri.parse("drive"))
+ .setType(AutomaticZenRule.TYPE_DRIVING)
+ .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
+ .setZenPolicy(new ZenPolicy.Builder().allowAllSounds().build())
+ .build(), true);
+ mController.updateZenMode(pref, zenMode);
+ verify(pref).setSummary(any());
+ }
+}
\ No newline at end of file
diff --git a/tests/robotests/src/com/android/settings/notification/modes/ZenModeNotifVisLinkPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/modes/ZenModeNotifVisLinkPreferenceControllerTest.java
new file mode 100644
index 00000000000..646c7aae9c6
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/notification/modes/ZenModeNotifVisLinkPreferenceControllerTest.java
@@ -0,0 +1,76 @@
+/*
+ * 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.app.NotificationManager.INTERRUPTION_FILTER_PRIORITY;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+
+import android.app.AutomaticZenRule;
+import android.app.Flags;
+import android.content.Context;
+import android.net.Uri;
+import android.platform.test.annotations.EnableFlags;
+import android.platform.test.flag.junit.SetFlagsRule;
+import android.service.notification.ZenPolicy;
+import androidx.preference.Preference;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+
+@RunWith(RobolectricTestRunner.class)
+public final class ZenModeNotifVisLinkPreferenceControllerTest {
+
+ private ZenModeNotifVisLinkPreferenceController mController;
+
+ @Rule
+ public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+
+ private Context mContext;
+ @Mock
+ private ZenModesBackend mBackend;
+
+ @Before
+ public void setup() {
+ MockitoAnnotations.initMocks(this);
+
+ mContext = RuntimeEnvironment.application;
+
+ mController = new ZenModeNotifVisLinkPreferenceController(
+ mContext, "something", mBackend);
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_MODES_UI)
+ public void testHasSummary() {
+ Preference pref = mock(Preference.class);
+ ZenMode zenMode = new ZenMode("id",
+ new AutomaticZenRule.Builder("Driving", Uri.parse("drive"))
+ .setType(AutomaticZenRule.TYPE_DRIVING)
+ .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
+ .setZenPolicy(new ZenPolicy.Builder().allowAllSounds().build())
+ .build(), true);
+ mController.updateZenMode(pref, zenMode);
+ verify(pref).setSummary(any());
+ }
+}
\ No newline at end of file
diff --git a/tests/robotests/src/com/android/settings/notification/modes/ZenModeNotifVisPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/modes/ZenModeNotifVisPreferenceControllerTest.java
new file mode 100644
index 00000000000..7424ae6c1f9
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/notification/modes/ZenModeNotifVisPreferenceControllerTest.java
@@ -0,0 +1,241 @@
+/*
+ * 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.app.NotificationManager.INTERRUPTION_FILTER_PRIORITY;
+import static android.service.notification.ZenPolicy.STATE_ALLOW;
+import static android.service.notification.ZenPolicy.STATE_DISALLOW;
+import static android.service.notification.ZenPolicy.STATE_UNSET;
+import static android.service.notification.ZenPolicy.VISUAL_EFFECT_LIGHTS;
+import static android.service.notification.ZenPolicy.VISUAL_EFFECT_NOTIFICATION_LIST;
+import static android.service.notification.ZenPolicy.VISUAL_EFFECT_PEEK;
+import static android.service.notification.ZenPolicy.VISUAL_EFFECT_STATUS_BAR;
+import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.ArgumentMatchers.nullable;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.app.AutomaticZenRule;
+import android.app.Flags;
+import android.content.Context;
+import android.content.res.Resources;
+import android.net.Uri;
+import android.platform.test.annotations.EnableFlags;
+import android.platform.test.flag.junit.SetFlagsRule;
+import android.service.notification.ZenDeviceEffects;
+import android.service.notification.ZenPolicy;
+import androidx.preference.TwoStatePreference;
+import com.android.settings.notification.zen.ZenModeVisEffectPreferenceController;
+import com.android.settings.widget.DisabledCheckBoxPreference;
+import com.android.settingslib.core.lifecycle.Lifecycle;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+
+@RunWith(RobolectricTestRunner.class)
+@EnableFlags(Flags.FLAG_MODES_UI)
+public final class ZenModeNotifVisPreferenceControllerTest {
+
+ private Context mContext;
+ @Mock
+ private ZenModesBackend mBackend;
+
+ @Rule
+ public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+
+ private ZenModeNotifVisPreferenceController mController;
+ @Before
+ public void setup() {
+ MockitoAnnotations.initMocks(this);
+ mContext = RuntimeEnvironment.application;
+ mController = new ZenModeNotifVisPreferenceController(mContext,
+ "zen_effect_peek", VISUAL_EFFECT_PEEK, null, mBackend);
+ }
+ @Test
+ public void isAvailable() {
+ // SUPPRESSED_EFFECT_PEEK is always available:
+ assertThat(mController.isAvailable()).isTrue();
+
+ // SUPPRESSED_EFFECT_LIGHTS is only available if the device has an LED:
+ Context mockContext = mock(Context.class);
+ mController = new ZenModeNotifVisPreferenceController(mockContext,
+ "zen_effect_light", VISUAL_EFFECT_LIGHTS, null, mBackend);
+ Resources mockResources = mock(Resources.class);
+ when(mockContext.getResources()).thenReturn(mockResources);
+
+ when(mockResources.getBoolean(com.android.internal.R.bool.config_intrusiveNotificationLed))
+ .thenReturn(false); // no light
+ assertThat(mController.isAvailable()).isFalse();
+
+ when(mockResources.getBoolean(com.android.internal.R.bool.config_intrusiveNotificationLed))
+ .thenReturn(true); // has light
+ assertThat(mController.isAvailable()).isTrue();
+ }
+
+ @Test
+ public void updateState_notChecked() {
+ DisabledCheckBoxPreference preference = mock(DisabledCheckBoxPreference.class);
+ ZenMode zenMode = new ZenMode("id",
+ new AutomaticZenRule.Builder("Driving", Uri.parse("drive"))
+ .setType(AutomaticZenRule.TYPE_DRIVING)
+ .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
+ .setZenPolicy(new ZenPolicy.Builder()
+ .allowAlarms(true)
+ .showAllVisualEffects()
+ .build())
+ .build(), true);
+
+ mController.updateZenMode(preference, zenMode);
+
+ verify(preference).setChecked(false);
+ verify(preference).enableCheckbox(true);
+ }
+
+ @Test
+ public void updateState_checked() {
+ DisabledCheckBoxPreference preference = mock(DisabledCheckBoxPreference.class);
+ ZenMode zenMode = new ZenMode("id",
+ new AutomaticZenRule.Builder("Driving", Uri.parse("drive"))
+ .setType(AutomaticZenRule.TYPE_DRIVING)
+ .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
+ .setZenPolicy(new ZenPolicy.Builder()
+ .allowAlarms(true)
+ .showVisualEffect(VISUAL_EFFECT_PEEK, false)
+ .build())
+ .build(), true);
+
+ mController.updateZenMode(preference, zenMode);
+
+ verify(preference).setChecked(true);
+ verify(preference).enableCheckbox(true);
+ }
+
+ @Test
+ public void updateState_checkedFalse_parentChecked() {
+ DisabledCheckBoxPreference preference = mock(DisabledCheckBoxPreference.class);
+ mController = new ZenModeNotifVisPreferenceController(mContext,
+ "zen_effect_status", VISUAL_EFFECT_STATUS_BAR,
+ new int[]{VISUAL_EFFECT_NOTIFICATION_LIST}, mBackend);
+
+ ZenMode zenMode = new ZenMode("id",
+ new AutomaticZenRule.Builder("Driving", Uri.parse("drive"))
+ .setType(AutomaticZenRule.TYPE_DRIVING)
+ .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
+ .setZenPolicy(new ZenPolicy.Builder()
+ .allowAlarms(true)
+ .showVisualEffect(VISUAL_EFFECT_NOTIFICATION_LIST, false)
+ .showVisualEffect(VISUAL_EFFECT_STATUS_BAR, true)
+ .build())
+ .build(), true);
+
+ mController.updateZenMode(preference, zenMode);
+
+ verify(preference).setChecked(true);
+ verify(preference).enableCheckbox(false);
+ ArgumentCaptor captor = ArgumentCaptor.forClass(ZenMode.class);
+ verify(mBackend).updateMode(captor.capture());
+ assertThat(captor.getValue().getPolicy().getVisualEffectStatusBar())
+ .isEqualTo(STATE_DISALLOW);
+ assertThat(captor.getValue().getPolicy().getVisualEffectNotificationList())
+ .isEqualTo(STATE_UNSET);
+ }
+
+ @Test
+ public void updateState_checkedFalse_parentNotChecked() {
+ DisabledCheckBoxPreference preference = mock(DisabledCheckBoxPreference.class);
+ mController = new ZenModeNotifVisPreferenceController(mContext,
+ "zen_effect_status", VISUAL_EFFECT_STATUS_BAR,
+ new int[]{VISUAL_EFFECT_NOTIFICATION_LIST}, mBackend);
+
+ ZenMode zenMode = new ZenMode("id",
+ new AutomaticZenRule.Builder("Driving", Uri.parse("drive"))
+ .setType(AutomaticZenRule.TYPE_DRIVING)
+ .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
+ .setZenPolicy(new ZenPolicy.Builder()
+ .allowAlarms(true)
+ .showAllVisualEffects()
+ .build())
+ .build(), true);
+
+ mController.updateZenMode(preference, zenMode);
+
+ verify(preference).setChecked(false);
+ verify(preference).enableCheckbox(true);
+ verify(mBackend, never()).updateMode(any());
+ }
+
+ @Test
+ public void onPreferenceChanged_checkedFalse() {
+ DisabledCheckBoxPreference preference = mock(DisabledCheckBoxPreference.class);
+ ZenMode zenMode = new ZenMode("id",
+ new AutomaticZenRule.Builder("Driving", Uri.parse("drive"))
+ .setType(AutomaticZenRule.TYPE_DRIVING)
+ .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
+ .setZenPolicy(new ZenPolicy.Builder()
+ .allowAlarms(true)
+ .hideAllVisualEffects()
+ .build())
+ .build(), true);
+
+ mController.updateZenMode(preference, zenMode);
+
+ mController.onPreferenceChange(preference, false);
+
+ ArgumentCaptor captor = ArgumentCaptor.forClass(ZenMode.class);
+ verify(mBackend).updateMode(captor.capture());
+ assertThat(captor.getValue().getPolicy().getVisualEffectPeek())
+ .isEqualTo(STATE_ALLOW);
+ assertThat(captor.getValue().getPolicy().getVisualEffectNotificationList())
+ .isEqualTo(STATE_UNSET);
+ }
+
+ @Test
+ public void onPreferenceChanged_checkedTrue() {
+ DisabledCheckBoxPreference preference = mock(DisabledCheckBoxPreference.class);
+ ZenMode zenMode = new ZenMode("id",
+ new AutomaticZenRule.Builder("Driving", Uri.parse("drive"))
+ .setType(AutomaticZenRule.TYPE_DRIVING)
+ .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
+ .setZenPolicy(new ZenPolicy.Builder()
+ .allowAlarms(true)
+ .showAllVisualEffects()
+ .build())
+ .build(), true);
+
+ mController.updateZenMode(preference, zenMode);
+
+ mController.onPreferenceChange(preference, true);
+
+ ArgumentCaptor captor = ArgumentCaptor.forClass(ZenMode.class);
+ verify(mBackend).updateMode(captor.capture());
+ assertThat(captor.getValue().getPolicy().getVisualEffectPeek())
+ .isEqualTo(STATE_DISALLOW);
+ assertThat(captor.getValue().getPolicy().getVisualEffectNotificationList())
+ .isEqualTo(STATE_UNSET);
+ }
+}
\ No newline at end of file
diff --git a/tests/robotests/src/com/android/settings/notification/modes/ZenModesSummaryHelperTest.java b/tests/robotests/src/com/android/settings/notification/modes/ZenModesSummaryHelperTest.java
index 67be82f9032..3e41778cdc0 100644
--- a/tests/robotests/src/com/android/settings/notification/modes/ZenModesSummaryHelperTest.java
+++ b/tests/robotests/src/com/android/settings/notification/modes/ZenModesSummaryHelperTest.java
@@ -20,11 +20,14 @@ import static android.app.NotificationManager.INTERRUPTION_FILTER_PRIORITY;
import static android.service.notification.ZenPolicy.CONVERSATION_SENDERS_ANYONE;
import static android.service.notification.ZenPolicy.PEOPLE_TYPE_ANYONE;
import static android.service.notification.ZenPolicy.PEOPLE_TYPE_CONTACTS;
+import static android.service.notification.ZenPolicy.VISUAL_EFFECT_AMBIENT;
+import static android.service.notification.ZenPolicy.VISUAL_EFFECT_LIGHTS;
import static com.google.common.truth.Truth.assertThat;
import android.app.AutomaticZenRule;
import android.content.Context;
import android.net.Uri;
+import android.service.notification.ZenDeviceEffects;
import android.service.notification.ZenPolicy;
import org.junit.Before;
import org.junit.Test;
@@ -165,4 +168,161 @@ public class ZenModesSummaryHelperTest {
assertThat(mSummaryHelper.getOtherSoundCategoriesSummary(zenMode)).isEqualTo(
"Alarms, media, and 3 more can interrupt");
}
+
+ @Test
+ public void getBlockedEffectsSummary_none() {
+ AutomaticZenRule rule = new AutomaticZenRule.Builder("Bedtime", Uri.parse("bed"))
+ .setType(AutomaticZenRule.TYPE_BEDTIME)
+ .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
+ .setZenPolicy(new ZenPolicy.Builder()
+ .showAllVisualEffects()
+ .allowAlarms(true)
+ .build())
+ .build();
+ ZenMode zenMode = new ZenMode("id", rule, true);
+ assertThat(mSummaryHelper.getBlockedEffectsSummary(zenMode))
+ .isEqualTo("Notifications shown");
+ }
+
+ @Test
+ public void getBlockedEffectsSummary_some() {
+ AutomaticZenRule rule = new AutomaticZenRule.Builder("Bedtime", Uri.parse("bed"))
+ .setType(AutomaticZenRule.TYPE_BEDTIME)
+ .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
+ .setZenPolicy(new ZenPolicy.Builder()
+ .allowAlarms(true)
+ .showAllVisualEffects()
+ .showVisualEffect(VISUAL_EFFECT_AMBIENT, false)
+ .build())
+ .build();
+ ZenMode zenMode = new ZenMode("id", rule, true);
+ assertThat(mSummaryHelper.getBlockedEffectsSummary(zenMode))
+ .isEqualTo("Notifications partially hidden");
+ }
+
+ @Test
+ public void getBlockedEffectsSummary_all() {
+ AutomaticZenRule rule = new AutomaticZenRule.Builder("Bedtime", Uri.parse("bed"))
+ .setType(AutomaticZenRule.TYPE_BEDTIME)
+ .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
+ .setZenPolicy(new ZenPolicy.Builder()
+ .allowAlarms(true)
+ .hideAllVisualEffects()
+ .build())
+ .build();
+ ZenMode zenMode = new ZenMode("id", rule, true);
+ assertThat(mSummaryHelper.getBlockedEffectsSummary(zenMode))
+ .isEqualTo("Notifications hidden");
+ }
+
+ @Test
+ public void getDisplayEffectsSummary_single_notifVis() {
+ AutomaticZenRule rule = new AutomaticZenRule.Builder("Bedtime", Uri.parse("bed"))
+ .setType(AutomaticZenRule.TYPE_BEDTIME)
+ .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
+ .setZenPolicy(new ZenPolicy.Builder()
+ .showAllVisualEffects()
+ .showVisualEffect(VISUAL_EFFECT_AMBIENT, false)
+ .build())
+ .build();
+ ZenMode zenMode = new ZenMode("id", rule, true);
+
+ assertThat(mSummaryHelper.getDisplayEffectsSummary(zenMode)).isEqualTo(
+ "Notifications partially hidden");
+ }
+
+ @Test
+ public void getDisplayEffectsSummary_single_notifVis_unusedEffect() {
+ AutomaticZenRule rule = new AutomaticZenRule.Builder("Bedtime", Uri.parse("bed"))
+ .setType(AutomaticZenRule.TYPE_BEDTIME)
+ .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
+ .setZenPolicy(new ZenPolicy.Builder()
+ .showAllVisualEffects()
+ .showVisualEffect(VISUAL_EFFECT_LIGHTS, false)
+ .build())
+ .build();
+ ZenMode zenMode = new ZenMode("id", rule, true);
+
+ assertThat(mSummaryHelper.getDisplayEffectsSummary(zenMode)).isEqualTo(
+ "Notifications shown");
+ }
+
+ @Test
+ public void getDisplayEffectsSummary_single_displayEffect() {
+ AutomaticZenRule rule = new AutomaticZenRule.Builder("Bedtime", Uri.parse("bed"))
+ .setType(AutomaticZenRule.TYPE_BEDTIME)
+ .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
+ .setZenPolicy(new ZenPolicy.Builder().showAllVisualEffects().build())
+ .setDeviceEffects(new ZenDeviceEffects.Builder()
+ .setShouldDimWallpaper(true)
+ .build())
+ .build();
+ ZenMode zenMode = new ZenMode("id", rule, true);
+
+ assertThat(mSummaryHelper.getDisplayEffectsSummary(zenMode)).isEqualTo(
+ "Dim the wallpaper");
+ }
+
+ @Test
+ public void getDisplayEffectsSummary_duo() {
+ AutomaticZenRule rule = new AutomaticZenRule.Builder("Bedtime", Uri.parse("bed"))
+ .setType(AutomaticZenRule.TYPE_BEDTIME)
+ .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
+ .setZenPolicy(new ZenPolicy.Builder().showAllVisualEffects().build())
+ .setDeviceEffects(new ZenDeviceEffects.Builder()
+ .setShouldDimWallpaper(true)
+ .setShouldDisplayGrayscale(true)
+ .build())
+ .build();
+ ZenMode zenMode = new ZenMode("id", rule, true);
+
+ assertThat(mSummaryHelper.getDisplayEffectsSummary(zenMode)).isEqualTo(
+ "Grayscale and dim the wallpaper");
+ }
+
+ @Test
+ public void getDisplayEffectsSummary_trio() {
+ AutomaticZenRule rule = new AutomaticZenRule.Builder("Bedtime", Uri.parse("bed"))
+ .setType(AutomaticZenRule.TYPE_BEDTIME)
+ .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
+ .setZenPolicy(new ZenPolicy.Builder()
+ .hideAllVisualEffects()
+ .allowAlarms(true)
+ .allowMedia(true)
+ .allowSystem(true)
+ .build())
+ .setDeviceEffects(new ZenDeviceEffects.Builder()
+ .setShouldDisplayGrayscale(true)
+ .setShouldDimWallpaper(true)
+ .build())
+ .build();
+ ZenMode zenMode = new ZenMode("id", rule, true);
+
+ assertThat(mSummaryHelper.getDisplayEffectsSummary(zenMode)).isEqualTo(
+ "Notifications hidden, grayscale, and dim the wallpaper");
+ }
+
+ @Test
+ public void getDisplayEffectsSummary_quad() {
+ AutomaticZenRule rule = new AutomaticZenRule.Builder("Bedtime", Uri.parse("bed"))
+ .setType(AutomaticZenRule.TYPE_BEDTIME)
+ .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
+ .setZenPolicy(new ZenPolicy.Builder()
+ .showAllVisualEffects()
+ .showVisualEffect(VISUAL_EFFECT_AMBIENT, false)
+ .allowAlarms(true)
+ .allowMedia(true)
+ .allowSystem(true)
+ .build())
+ .setDeviceEffects(new ZenDeviceEffects.Builder()
+ .setShouldDimWallpaper(true)
+ .setShouldDisplayGrayscale(true)
+ .setShouldUseNightMode(true)
+ .build())
+ .build();
+ ZenMode zenMode = new ZenMode("id", rule, true);
+
+ assertThat(mSummaryHelper.getDisplayEffectsSummary(zenMode)).isEqualTo(
+ "Notifications partially hidden, grayscale, and 2 more");
+ }
}