diff --git a/res/xml/modes_calls_settings.xml b/res/xml/modes_calls_settings.xml
new file mode 100644
index 00000000000..f2ba7f13867
--- /dev/null
+++ b/res/xml/modes_calls_settings.xml
@@ -0,0 +1,37 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/res/xml/modes_messages_settings.xml b/res/xml/modes_messages_settings.xml
new file mode 100644
index 00000000000..d4aee3d1947
--- /dev/null
+++ b/res/xml/modes_messages_settings.xml
@@ -0,0 +1,29 @@
+
+
+
+
+
+
+
+
diff --git a/res/xml/modes_people_settings.xml b/res/xml/modes_people_settings.xml
new file mode 100644
index 00000000000..136a357dddc
--- /dev/null
+++ b/res/xml/modes_people_settings.xml
@@ -0,0 +1,35 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/res/xml/modes_rule_settings.xml b/res/xml/modes_rule_settings.xml
index 1b79153cf7b..12a683e6bec 100644
--- a/res/xml/modes_rule_settings.xml
+++ b/res/xml/modes_rule_settings.xml
@@ -22,4 +22,8 @@
android:key="header"
android:layout="@layout/settings_entity_header" />
+
+
\ No newline at end of file
diff --git a/src/com/android/settings/notification/modes/ZenModeCallsFragment.java b/src/com/android/settings/notification/modes/ZenModeCallsFragment.java
new file mode 100644
index 00000000000..4c85bf56727
--- /dev/null
+++ b/src/com/android/settings/notification/modes/ZenModeCallsFragment.java
@@ -0,0 +1,61 @@
+/*
+ * 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;
+
+/**
+ * DND Calls Settings page to determine which priority senders can bypass DND when this mode is
+ * activated.
+ */
+public class ZenModeCallsFragment extends ZenModeFragmentBase {
+
+ @Override
+ protected List createPreferenceControllers(Context context) {
+ List controllers = new ArrayList<>();
+ controllers.add(new ZenModePrioritySendersPreferenceController(context,
+ "zen_mode_settings_category_calls", false, mBackend));
+ controllers.add(new ZenModeRepeatCallersPreferenceController(context,
+ "zen_mode_repeat_callers", mBackend,
+ context.getResources().getInteger(com.android.internal.R.integer
+ .config_zen_repeat_callers_threshold)));
+ return controllers;
+ }
+
+ @Override
+ protected int getPreferenceScreenResId() {
+ return R.xml.modes_calls_settings;
+ }
+
+ @Override
+ public int getMetricsCategory() {
+ // TODO: b/332937635 - make this the correct metrics category
+ return SettingsEnums.DND_CALLS;
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ use(ZenModePrioritySendersPreferenceController.class).onResume();
+ }
+}
diff --git a/src/com/android/settings/notification/modes/ZenModeCallsLinkPreferenceController.java b/src/com/android/settings/notification/modes/ZenModeCallsLinkPreferenceController.java
new file mode 100644
index 00000000000..1d1d7505944
--- /dev/null
+++ b/src/com/android/settings/notification/modes/ZenModeCallsLinkPreferenceController.java
@@ -0,0 +1,49 @@
+/*
+ * 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 ZenModeCallsLinkPreferenceController extends AbstractZenModePreferenceController {
+
+ private ZenModeSummaryHelper mSummaryHelper;
+
+ public ZenModeCallsLinkPreferenceController(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(ZenModeCallsFragment.class.getName())
+ .setSourceMetricsCategory(0)
+ .setArguments(bundle)
+ .toIntent());
+ preference.setSummary(mSummaryHelper.getCallsSettingSummary(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 d2126810358..1626d91bd85 100644
--- a/src/com/android/settings/notification/modes/ZenModeFragment.java
+++ b/src/com/android/settings/notification/modes/ZenModeFragment.java
@@ -20,9 +20,6 @@ import android.app.AutomaticZenRule;
import android.app.settings.SettingsEnums;
import android.content.Context;
-import androidx.preference.Preference;
-import androidx.preference.PreferenceScreen;
-
import com.android.settings.R;
import com.android.settingslib.core.AbstractPreferenceController;
@@ -42,6 +39,8 @@ public class ZenModeFragment extends ZenModeFragmentBase {
// {@link AbstractZenModePreferenceController}.
List prefControllers = new ArrayList<>();
prefControllers.add(new ZenModeHeaderController(context, "header", this, mBackend));
+ prefControllers.add(new ZenModePeopleLinkPreferenceController(
+ context, "zen_mode_people", mBackend));
return prefControllers;
}
diff --git a/src/com/android/settings/notification/modes/ZenModeMessagesFragment.java b/src/com/android/settings/notification/modes/ZenModeMessagesFragment.java
new file mode 100644
index 00000000000..c86f8dd4750
--- /dev/null
+++ b/src/com/android/settings/notification/modes/ZenModeMessagesFragment.java
@@ -0,0 +1,57 @@
+/*
+ * 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;
+
+/**
+ * DND Messages Settings page to determine which priority senders can bypass DND.
+ * "Messages" include SMS, MMS, and messaging apps.
+ */
+public class ZenModeMessagesFragment extends ZenModeFragmentBase {
+
+ @Override
+ protected List createPreferenceControllers(Context context) {
+ List controllers = new ArrayList<>();
+ controllers.add(new ZenModePrioritySendersPreferenceController(context,
+ "zen_mode_settings_category_messages", true, mBackend));
+ return controllers;
+ }
+
+ @Override
+ protected int getPreferenceScreenResId() {
+ return R.xml.modes_messages_settings;
+ }
+
+ @Override
+ public int getMetricsCategory() {
+ // TODO: b/332937635 - make this the correct metrics category
+ return SettingsEnums.DND_MESSAGES;
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ use(ZenModePrioritySendersPreferenceController.class).onResume();
+ }
+}
diff --git a/src/com/android/settings/notification/modes/ZenModeMessagesLinkPreferenceController.java b/src/com/android/settings/notification/modes/ZenModeMessagesLinkPreferenceController.java
new file mode 100644
index 00000000000..8261008aaf4
--- /dev/null
+++ b/src/com/android/settings/notification/modes/ZenModeMessagesLinkPreferenceController.java
@@ -0,0 +1,51 @@
+/*
+ * 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 ZenModeMessagesLinkPreferenceController extends AbstractZenModePreferenceController {
+ private final ZenModeSummaryHelper mSummaryHelper;
+
+ public ZenModeMessagesLinkPreferenceController(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(ZenModeMessagesFragment.class.getName())
+ .setSourceMetricsCategory(0)
+ .setArguments(bundle)
+ .toIntent());
+
+ preference.setEnabled(true);
+ preference.setSummary(mSummaryHelper.getMessagesSettingSummary(getMode().getPolicy()));
+ }
+}
diff --git a/src/com/android/settings/notification/modes/ZenModePeopleFragment.java b/src/com/android/settings/notification/modes/ZenModePeopleFragment.java
new file mode 100644
index 00000000000..e1f753cef05
--- /dev/null
+++ b/src/com/android/settings/notification/modes/ZenModePeopleFragment.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 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 calls and messages will break through the mode and links to the
+ * configuration pages for both.
+ */
+public class ZenModePeopleFragment extends ZenModeFragmentBase {
+
+ @Override
+ protected List createPreferenceControllers(Context context) {
+ List prefControllers = new ArrayList<>();
+ prefControllers.add(new ZenModeCallsLinkPreferenceController(
+ context, "zen_mode_people_calls", mBackend));
+ prefControllers.add(new ZenModeMessagesLinkPreferenceController(
+ context, "zen_mode_people_messages", mBackend));
+ return prefControllers;
+ }
+
+ @Override
+ protected int getPreferenceScreenResId() {
+ return R.xml.modes_people_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/ZenModePeopleLinkPreferenceController.java b/src/com/android/settings/notification/modes/ZenModePeopleLinkPreferenceController.java
new file mode 100644
index 00000000000..f12200627ef
--- /dev/null
+++ b/src/com/android/settings/notification/modes/ZenModePeopleLinkPreferenceController.java
@@ -0,0 +1,54 @@
+/*
+ * 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.R;
+import com.android.settings.core.SubSettingLauncher;
+
+/**
+ * Preference with a link and summary about what calls and messages can break through the mode
+ */
+public class ZenModePeopleLinkPreferenceController extends AbstractZenModePreferenceController {
+
+ ZenModeSummaryHelper mSummaryHelper;
+
+ public ZenModePeopleLinkPreferenceController(Context context, String key,
+ ZenModesBackend backend) {
+ super(context, key, backend);
+ mSummaryHelper = new ZenModeSummaryHelper(mContext, mBackend);
+ }
+
+ @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(ZenModePeopleFragment.class.getName())
+ .setSourceMetricsCategory(0)
+ .setArguments(bundle)
+ .toIntent());
+ preference.setSummary(mSummaryHelper.getPeopleSummary(getMode()));
+ }
+}
diff --git a/src/com/android/settings/notification/modes/ZenModePrioritySendersPreferenceController.java b/src/com/android/settings/notification/modes/ZenModePrioritySendersPreferenceController.java
new file mode 100644
index 00000000000..a71bbe844b4
--- /dev/null
+++ b/src/com/android/settings/notification/modes/ZenModePrioritySendersPreferenceController.java
@@ -0,0 +1,447 @@
+/*
+ * 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.CONVERSATION_SENDERS_ANYONE;
+import static android.service.notification.ZenPolicy.CONVERSATION_SENDERS_IMPORTANT;
+import static android.service.notification.ZenPolicy.CONVERSATION_SENDERS_NONE;
+import static android.service.notification.ZenPolicy.CONVERSATION_SENDERS_UNSET;
+import static android.service.notification.ZenPolicy.PEOPLE_TYPE_ANYONE;
+import static android.service.notification.ZenPolicy.PEOPLE_TYPE_CONTACTS;
+import static android.service.notification.ZenPolicy.PEOPLE_TYPE_NONE;
+import static android.service.notification.ZenPolicy.PEOPLE_TYPE_STARRED;
+import static android.service.notification.ZenPolicy.PEOPLE_TYPE_UNSET;
+
+import android.app.settings.SettingsEnums;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ParceledListSlice;
+import android.icu.text.MessageFormat;
+import android.provider.Contacts;
+import android.service.notification.ConversationChannelWrapper;
+import android.service.notification.ZenPolicy;
+import android.view.View;
+import androidx.annotation.VisibleForTesting;
+import androidx.preference.Preference;
+import androidx.preference.PreferenceCategory;
+import androidx.preference.PreferenceScreen;
+import com.android.settings.R;
+import com.android.settings.core.SubSettingLauncher;
+import com.android.settings.notification.app.ConversationListSettings;
+import com.android.settingslib.widget.SelectorWithWidgetPreference;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+
+/**
+ * Common preference controller functionality for zen mode priority senders preferences for both
+ * messages and calls.
+ *
+ * These controllers handle the settings regarding which priority senders that are allowed to
+ * bypass DND for calls or messages, which may be one of the following values: starred contacts, all
+ * contacts, priority conversations (for messages only), anyone, or no one.
+ */
+public class ZenModePrioritySendersPreferenceController
+ extends AbstractZenModePreferenceController {
+ private final boolean mIsMessages; // if this is false, then this preference is for calls
+
+ static final String KEY_ANY = "senders_anyone";
+ static final String KEY_CONTACTS = "senders_contacts";
+ static final String KEY_STARRED = "senders_starred_contacts";
+ static final String KEY_IMPORTANT = "conversations_important";
+ static final String KEY_NONE = "senders_none";
+
+ private int mNumImportantConversations = CONVERSATION_SENDERS_UNSET;
+
+ private static final Intent ALL_CONTACTS_INTENT =
+ new Intent(Contacts.Intents.UI.LIST_DEFAULT)
+ .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
+ private static final Intent STARRED_CONTACTS_INTENT =
+ new Intent(Contacts.Intents.UI.LIST_STARRED_ACTION)
+ .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
+ private static final Intent FALLBACK_INTENT = new Intent(Intent.ACTION_MAIN)
+ .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
+
+ private final PackageManager mPackageManager;
+ private PreferenceCategory mPreferenceCategory;
+ private List mSelectorPreferences = new ArrayList<>();
+
+ private final ZenModeSummaryHelper mZenModeSummaryHelper;
+
+ public ZenModePrioritySendersPreferenceController(Context context, String key,
+ boolean isMessages, ZenModesBackend backend) {
+ super(context, key, backend);
+ mIsMessages = isMessages;
+
+ String contactsPackage = context.getString(R.string.config_contacts_package_name);
+ ALL_CONTACTS_INTENT.setPackage(contactsPackage);
+ STARRED_CONTACTS_INTENT.setPackage(contactsPackage);
+ FALLBACK_INTENT.setPackage(contactsPackage);
+
+ mPackageManager = mContext.getPackageManager();
+ if (!FALLBACK_INTENT.hasCategory(Intent.CATEGORY_APP_CONTACTS)) {
+ FALLBACK_INTENT.addCategory(Intent.CATEGORY_APP_CONTACTS);
+ }
+ mZenModeSummaryHelper = new ZenModeSummaryHelper(mContext, mBackend);
+ }
+
+ @Override
+ public void displayPreference(PreferenceScreen screen) {
+ mPreferenceCategory = screen.findPreference(getPreferenceKey());
+ if (mPreferenceCategory.getPreferenceCount() == 0) {
+ makeSelectorPreference(KEY_STARRED,
+ com.android.settings.R.string.zen_mode_from_starred, mIsMessages);
+ makeSelectorPreference(KEY_CONTACTS,
+ com.android.settings.R.string.zen_mode_from_contacts, mIsMessages);
+ if (mIsMessages) {
+ makeSelectorPreference(KEY_IMPORTANT,
+ com.android.settings.R.string.zen_mode_from_important_conversations, true);
+ }
+ makeSelectorPreference(KEY_ANY,
+ com.android.settings.R.string.zen_mode_from_anyone, mIsMessages);
+ makeSelectorPreference(KEY_NONE,
+ com.android.settings.R.string.zen_mode_none_messages, mIsMessages);
+ }
+ super.displayPreference(screen);
+ }
+
+ @Override
+ public void updateState(Preference preference) {
+ if (mIsMessages) {
+ updateChannelCounts();
+ }
+ final int currContactsSetting = getPrioritySenders();
+ final int currConversationsSetting = getPriorityConversationSenders();
+ for (SelectorWithWidgetPreference pref : mSelectorPreferences) {
+ // for each preference, check whether the current state matches what this state
+ // would look like if the button were checked.
+ final int[] checkedState = keyToSettingEndState(pref.getKey(), true);
+ final int checkedContactsSetting = checkedState[0];
+ final int checkedConversationsSetting = checkedState[1];
+
+ boolean match = checkedContactsSetting == currContactsSetting;
+ if (mIsMessages && checkedConversationsSetting != CONVERSATION_SENDERS_UNSET) {
+ // "CONVERSATION_SENDERS_UNSET" in checkedContactsSetting means this preference
+ // doesn't govern the priority senders setting, so the full match happens when
+ // either the priority senders setting matches or if it's CONVERSATION_SENDERS_UNSET
+ // so only the conversation setting needs to match.
+ match = (match || checkedContactsSetting == PEOPLE_TYPE_UNSET)
+ && (checkedConversationsSetting == currConversationsSetting);
+ }
+
+ pref.setChecked(match);
+ }
+ updateSummaries();
+ }
+
+ public void onResume() {
+ if (mIsMessages) {
+ updateChannelCounts();
+ }
+ updateSummaries();
+ }
+
+ private void updateChannelCounts() {
+ ParceledListSlice impConversations =
+ mBackend.getConversations(true);
+ int numImportantConversations = 0;
+ if (impConversations != null) {
+ for (ConversationChannelWrapper conversation : impConversations.getList()) {
+ if (!conversation.getNotificationChannel().isDemoted()) {
+ numImportantConversations++;
+ }
+ }
+ }
+ mNumImportantConversations = numImportantConversations;
+ }
+
+ private int getPrioritySenders() {
+ if (mIsMessages) {
+ return getMode().getPolicy().getPriorityMessageSenders();
+ } else {
+ return getMode().getPolicy().getPriorityCallSenders();
+ }
+ }
+
+ private int getPriorityConversationSenders() {
+ if (mIsMessages) {
+ return getMode().getPolicy().getPriorityConversationSenders();
+ }
+ return CONVERSATION_SENDERS_UNSET;
+ }
+
+ private SelectorWithWidgetPreference makeSelectorPreference(String key, int titleId,
+ boolean isCheckbox) {
+ final SelectorWithWidgetPreference pref =
+ new SelectorWithWidgetPreference(mPreferenceCategory.getContext(), isCheckbox);
+ pref.setKey(key);
+ pref.setTitle(titleId);
+ pref.setOnClickListener(mSelectorClickListener);
+
+ View.OnClickListener widgetClickListener = getWidgetClickListener(key);
+ if (widgetClickListener != null) {
+ pref.setExtraWidgetOnClickListener(widgetClickListener);
+ }
+
+ mPreferenceCategory.addPreference(pref);
+ mSelectorPreferences.add(pref);
+ return pref;
+ }
+
+ private View.OnClickListener getWidgetClickListener(String key) {
+ if (!KEY_CONTACTS.equals(key) && !KEY_STARRED.equals(key) && !KEY_IMPORTANT.equals(key)) {
+ return null;
+ }
+
+ if (KEY_STARRED.equals(key) && !isStarredIntentValid()) {
+ return null;
+ }
+
+ if (KEY_CONTACTS.equals(key) && !isContactsIntentValid()) {
+ return null;
+ }
+
+ return v -> {
+ if (KEY_STARRED.equals(key)
+ && STARRED_CONTACTS_INTENT.resolveActivity(mPackageManager) != null) {
+ mContext.startActivity(STARRED_CONTACTS_INTENT);
+ } else if (KEY_CONTACTS.equals(key)
+ && ALL_CONTACTS_INTENT.resolveActivity(mPackageManager) != null) {
+ mContext.startActivity(ALL_CONTACTS_INTENT);
+ } else if (KEY_IMPORTANT.equals(key)) {
+ new SubSettingLauncher(mContext)
+ .setDestination(ConversationListSettings.class.getName())
+ .setSourceMetricsCategory(SettingsEnums.DND_CONVERSATIONS)
+ .launch();
+ } else {
+ mContext.startActivity(FALLBACK_INTENT);
+ }
+ };
+ }
+
+ private boolean isStarredIntentValid() {
+ return STARRED_CONTACTS_INTENT.resolveActivity(mPackageManager) != null
+ || FALLBACK_INTENT.resolveActivity(mPackageManager) != null;
+ }
+
+ private boolean isContactsIntentValid() {
+ return ALL_CONTACTS_INTENT.resolveActivity(mPackageManager) != null
+ || FALLBACK_INTENT.resolveActivity(mPackageManager) != null;
+ }
+
+ void updateSummaries() {
+ for (SelectorWithWidgetPreference pref : mSelectorPreferences) {
+ pref.setSummary(getSummary(pref.getKey()));
+ }
+ }
+
+ // Gets the desired end state of the priority senders and conversations for the given key
+ // and whether it is being checked or unchecked. [type]_UNSET indicates no change in state.
+ //
+ // Returns an integer array with 2 entries. The first entry is the setting for priority senders
+ // and the second entry is for priority conversation senders; if isMessages is false, then
+ // no changes will ever be prescribed for conversation senders.
+ int[] keyToSettingEndState(String key, boolean checked) {
+ int[] endState = new int[]{ PEOPLE_TYPE_UNSET, CONVERSATION_SENDERS_UNSET };
+ if (!checked) {
+ // Unchecking any priority-senders-based state should reset the state to NONE.
+ // "Unchecking" the NONE state doesn't do anything, in practice.
+ switch (key) {
+ case KEY_STARRED:
+ case KEY_CONTACTS:
+ case KEY_ANY:
+ case KEY_NONE:
+ endState[0] = PEOPLE_TYPE_NONE;
+ }
+
+ // For messages, unchecking "priority conversations" and "any" should reset conversation
+ // state to "NONE" as well.
+ if (mIsMessages) {
+ switch (key) {
+ case KEY_IMPORTANT:
+ case KEY_ANY:
+ case KEY_NONE:
+ endState[1] = CONVERSATION_SENDERS_NONE;
+ }
+ }
+ } else {
+ // All below is for the enabling (checked) state.
+ switch (key) {
+ case KEY_STARRED:
+ endState[0] = PEOPLE_TYPE_STARRED;
+ break;
+ case KEY_CONTACTS:
+ endState[0] = PEOPLE_TYPE_CONTACTS;
+ break;
+ case KEY_ANY:
+ endState[0] = PEOPLE_TYPE_ANYONE;
+ break;
+ case KEY_NONE:
+ endState[0] = PEOPLE_TYPE_NONE;
+ }
+
+ // In the messages case *only*, also handle changing of conversation settings.
+ if (mIsMessages) {
+ switch (key) {
+ case KEY_IMPORTANT:
+ endState[1] = CONVERSATION_SENDERS_IMPORTANT;
+ break;
+ case KEY_ANY:
+ endState[1] = CONVERSATION_SENDERS_ANYONE;
+ break;
+ case KEY_NONE:
+ endState[1] = CONVERSATION_SENDERS_NONE;
+ }
+ }
+ }
+
+ // Error case check: if somehow, after all of that, endState is still
+ // {PEOPLE_TYPE_UNSET, CONVERSATION_SENDERS_UNSET}, something has gone wrong.
+ if (endState[0] == PEOPLE_TYPE_UNSET && endState[1] == CONVERSATION_SENDERS_UNSET) {
+ throw new IllegalArgumentException("invalid key " + key);
+ }
+
+ return endState;
+ }
+
+ // Returns the preferences, if any, that should be newly saved for the specified setting and
+ // checked state in an array where index 0 is the new senders setting and 1 the new
+ // conversations setting. A return value of [type]_UNSET indicates that nothing should
+ // change.
+ //
+ // The returned conversations setting will always be CONVERSATION_SENDERS_UNSET (not to change)
+ // in the calls case.
+ //
+ // Checking and unchecking is mostly an operation of setting or unsetting the relevant
+ // preference, except for some special handling where the conversation setting overlaps:
+ // - setting or unsetting "priority contacts" or "contacts" has no effect on the
+ // priority conversation setting, and vice versa
+ // - if "priority conversations" is selected, and the user checks "anyone", the conversation
+ // setting is also set to any conversations
+ // - if "anyone" is previously selected, and the user clicks "priority conversations", then
+ // the contacts setting is additionally reset to "none".
+ // - if "anyone" is previously selected, and the user clicks one of the contacts values,
+ // then the conversations setting is additionally reset to "none".
+ int[] settingsToSaveOnClick(SelectorWithWidgetPreference preference,
+ int currSendersSetting, int currConvosSetting) {
+ int[] savedSettings = new int[]{ PEOPLE_TYPE_UNSET, CONVERSATION_SENDERS_UNSET };
+
+ // If the preference isn't a checkbox, always consider this to be "checking" the setting.
+ // Otherwise, toggle.
+ final int[] endState = keyToSettingEndState(preference.getKey(),
+ preference.isCheckBox() ? preference.isChecked() : true);
+ final int prioritySendersSetting = endState[0];
+ final int priorityConvosSetting = endState[1];
+
+ if (prioritySendersSetting != PEOPLE_TYPE_UNSET
+ && prioritySendersSetting != currSendersSetting) {
+ savedSettings[0] = prioritySendersSetting;
+ }
+
+ // Only handle conversation settings for the messages case. If not messages, there should
+ // never be any change to the conversation senders setting.
+ if (mIsMessages) {
+ if (priorityConvosSetting != CONVERSATION_SENDERS_UNSET
+ && priorityConvosSetting != currConvosSetting) {
+ savedSettings[1] = priorityConvosSetting;
+ }
+
+ // Special-case handling for the "priority conversations" checkbox:
+ // If a specific selection exists for priority senders (starred, contacts), we leave
+ // it untouched. Otherwise (when the senders is set to "any"), set it to NONE.
+ if (preference.getKey() == KEY_IMPORTANT
+ && currSendersSetting == PEOPLE_TYPE_ANYONE) {
+ savedSettings[0] = PEOPLE_TYPE_NONE;
+ }
+
+ // Flip-side special case for clicking either "contacts" option: if a specific selection
+ // exists for priority conversations, leave it untouched; otherwise, set to none.
+ if ((preference.getKey() == KEY_STARRED || preference.getKey() == KEY_CONTACTS)
+ && currConvosSetting == CONVERSATION_SENDERS_ANYONE) {
+ savedSettings[1] = CONVERSATION_SENDERS_NONE;
+ }
+ }
+
+ return savedSettings;
+ }
+
+ private String getSummary(String key) {
+ switch (key) {
+ case KEY_STARRED:
+ return mZenModeSummaryHelper.getStarredContactsSummary();
+ case KEY_CONTACTS:
+ return mZenModeSummaryHelper.getContactsNumberSummary();
+ case KEY_IMPORTANT:
+ return getConversationSummary();
+ case KEY_ANY:
+ return mContext.getResources().getString(mIsMessages
+ ? R.string.zen_mode_all_messages_summary
+ : R.string.zen_mode_all_calls_summary);
+ case KEY_NONE:
+ default:
+ return null;
+ }
+ }
+
+ private String getConversationSummary() {
+ final int numConversations = mNumImportantConversations;
+
+ if (numConversations == CONVERSATION_SENDERS_UNSET) {
+ return null;
+ } else {
+ MessageFormat msgFormat = new MessageFormat(
+ mContext.getString(R.string.zen_mode_conversations_count),
+ Locale.getDefault());
+ Map args = new HashMap<>();
+ args.put("count", numConversations);
+ return msgFormat.format(args);
+ }
+ }
+
+ @VisibleForTesting
+ SelectorWithWidgetPreference.OnClickListener mSelectorClickListener =
+ new SelectorWithWidgetPreference.OnClickListener() {
+ @Override
+ public void onRadioButtonClicked(SelectorWithWidgetPreference preference) {
+ // The settingsToSaveOnClick function takes whether the preference is a
+ // checkbox into account to determine whether this selection is checked or unchecked.
+ final int[] settingsToSave = settingsToSaveOnClick(preference,
+ getPrioritySenders(), getPriorityConversationSenders());
+ final int prioritySendersSetting = settingsToSave[0];
+ final int priorityConvosSetting = settingsToSave[1];
+
+ ZenPolicy.Builder diffPolicy = new ZenPolicy.Builder();
+ if (prioritySendersSetting != PEOPLE_TYPE_UNSET) {
+ if (mIsMessages) {
+ diffPolicy.allowMessages(prioritySendersSetting);
+
+ } else {
+ diffPolicy.allowCalls(prioritySendersSetting);
+ }
+ }
+ if (mIsMessages && priorityConvosSetting != CONVERSATION_SENDERS_UNSET) {
+ diffPolicy.allowConversations(priorityConvosSetting);
+ }
+ getMode().setPolicy(diffPolicy.build());
+ mBackend.updateMode(getMode());
+ }
+ };
+}
diff --git a/src/com/android/settings/notification/modes/ZenModeRepeatCallersPreferenceController.java b/src/com/android/settings/notification/modes/ZenModeRepeatCallersPreferenceController.java
new file mode 100644
index 00000000000..d6de9c22d3c
--- /dev/null
+++ b/src/com/android/settings/notification/modes/ZenModeRepeatCallersPreferenceController.java
@@ -0,0 +1,82 @@
+/*
+ * 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.PEOPLE_TYPE_ANYONE;
+import static android.service.notification.ZenPolicy.PEOPLE_TYPE_NONE;
+import static android.service.notification.ZenPolicy.STATE_ALLOW;
+
+import android.app.settings.SettingsEnums;
+import android.content.Context;
+import android.provider.Settings;
+import android.service.notification.ZenPolicy;
+import android.util.Log;
+import androidx.preference.Preference;
+import androidx.preference.PreferenceScreen;
+import androidx.preference.TwoStatePreference;
+import com.android.settings.R;
+
+public class ZenModeRepeatCallersPreferenceController extends AbstractZenModePreferenceController
+ implements Preference.OnPreferenceChangeListener {
+
+ private final int mRepeatCallersThreshold;
+
+ public ZenModeRepeatCallersPreferenceController(Context context,
+ String key, ZenModesBackend backend, int repeatCallersThreshold) {
+ super(context, key, backend);
+
+ mRepeatCallersThreshold = repeatCallersThreshold;
+ }
+
+ @Override
+ public void updateState(Preference preference) {
+ super.updateState(preference);
+
+ TwoStatePreference pref = (TwoStatePreference) preference;
+
+ boolean anyCallersCanBypassDnd =
+ getMode().getPolicy().getPriorityCategoryCalls() == STATE_ALLOW
+ && getMode().getPolicy().getPriorityCallSenders() == PEOPLE_TYPE_ANYONE;
+ // if any caller can bypass dnd then repeat callers preference is disabled
+ if (anyCallersCanBypassDnd) {
+ pref.setEnabled(false);
+ pref.setChecked(true);
+ } else {
+ pref.setEnabled(true);
+ pref.setChecked(
+ getMode().getPolicy().getPriorityCategoryRepeatCallers() == STATE_ALLOW);
+ }
+
+ setRepeatCallerSummary(preference);
+ }
+
+ @Override
+ public boolean onPreferenceChange(Preference preference, Object newValue) {
+ final boolean allowRepeatCallers = (Boolean) newValue;
+ ZenPolicy diffPolicy = new ZenPolicy.Builder()
+ .allowRepeatCallers(allowRepeatCallers)
+ .build();
+ getMode().setPolicy(diffPolicy);
+ mBackend.updateMode(getMode());
+ return true;
+ }
+
+ private void setRepeatCallerSummary(Preference preference) {
+ preference.setSummary(mContext.getString(R.string.zen_mode_repeat_callers_summary,
+ mRepeatCallersThreshold));
+ }
+}
diff --git a/src/com/android/settings/notification/modes/ZenModeSummaryHelper.java b/src/com/android/settings/notification/modes/ZenModeSummaryHelper.java
new file mode 100644
index 00000000000..cf0c3db7499
--- /dev/null
+++ b/src/com/android/settings/notification/modes/ZenModeSummaryHelper.java
@@ -0,0 +1,295 @@
+/*
+ * 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.CONVERSATION_SENDERS_ANYONE;
+import static android.service.notification.ZenPolicy.CONVERSATION_SENDERS_IMPORTANT;
+import static android.service.notification.ZenPolicy.CONVERSATION_SENDERS_NONE;
+import static android.service.notification.ZenPolicy.PEOPLE_TYPE_ANYONE;
+import static android.service.notification.ZenPolicy.PEOPLE_TYPE_CONTACTS;
+import static android.service.notification.ZenPolicy.PEOPLE_TYPE_NONE;
+import static android.service.notification.ZenPolicy.PRIORITY_CATEGORY_ALARMS;
+import static android.service.notification.ZenPolicy.PRIORITY_CATEGORY_CALLS;
+import static android.service.notification.ZenPolicy.PRIORITY_CATEGORY_CONVERSATIONS;
+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_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 android.content.Context;
+import android.icu.text.MessageFormat;
+import android.service.notification.ZenPolicy;
+
+import com.android.settings.R;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.function.Predicate;
+
+public class ZenModeSummaryHelper {
+
+ private Context mContext;
+ private ZenModesBackend mBackend;
+
+ public ZenModeSummaryHelper(Context context, ZenModesBackend backend) {
+ mContext = context;
+ mBackend = backend;
+ }
+
+ private static final int[] ALL_PRIORITY_CATEGORIES = {
+ PRIORITY_CATEGORY_ALARMS,
+ PRIORITY_CATEGORY_MEDIA,
+ PRIORITY_CATEGORY_SYSTEM,
+ PRIORITY_CATEGORY_MESSAGES,
+ PRIORITY_CATEGORY_CONVERSATIONS,
+ PRIORITY_CATEGORY_EVENTS,
+ PRIORITY_CATEGORY_REMINDERS,
+ PRIORITY_CATEGORY_CALLS,
+ PRIORITY_CATEGORY_REPEAT_CALLERS,
+ };
+
+ String getOtherSoundCategoriesSummary(ZenMode zenMode) {
+ List enabledCategories = getEnabledCategories(
+ zenMode.getPolicy(),
+ category -> PRIORITY_CATEGORY_ALARMS == category
+ || PRIORITY_CATEGORY_MEDIA == category
+ || PRIORITY_CATEGORY_SYSTEM == category
+ || PRIORITY_CATEGORY_REMINDERS == category
+ || PRIORITY_CATEGORY_EVENTS == category,
+ true);
+ int numCategories = enabledCategories.size();
+ MessageFormat msgFormat = new MessageFormat(
+ mContext.getString(R.string.zen_mode_other_sounds_summary),
+ Locale.getDefault());
+ Map args = new HashMap<>();
+ args.put("count", numCategories);
+ if (numCategories >= 1) {
+ args.put("sound_category_1", enabledCategories.get(0));
+ if (numCategories >= 2) {
+ args.put("sound_category_2", enabledCategories.get(1));
+ if (numCategories == 3) {
+ args.put("sound_category_3", enabledCategories.get(2));
+ }
+ }
+ }
+ return msgFormat.format(args);
+ }
+
+ String getCallsSettingSummary(ZenMode zenMode) {
+ List enabledCategories = getEnabledCategories(zenMode.getPolicy(),
+ category -> PRIORITY_CATEGORY_CALLS == category
+ || PRIORITY_CATEGORY_REPEAT_CALLERS == category, true);
+ int numCategories = enabledCategories.size();
+ if (numCategories == 0) {
+ return mContext.getString(R.string.zen_mode_none_calls);
+ } else if (numCategories == 1) {
+ return mContext.getString(R.string.zen_mode_calls_summary_one,
+ enabledCategories.get(0));
+ } else {
+ return mContext.getString(R.string.zen_mode_calls_summary_two,
+ enabledCategories.get(0),
+ enabledCategories.get(1));
+ }
+ }
+
+ String getMessagesSettingSummary(ZenPolicy policy) {
+ List enabledCategories = getEnabledCategories(policy,
+ category -> PRIORITY_CATEGORY_MESSAGES == category
+ || PRIORITY_CATEGORY_CONVERSATIONS == category, true);
+ int numCategories = enabledCategories.size();
+ if (numCategories == 0) {
+ return mContext.getString(R.string.zen_mode_none_messages);
+ } else if (numCategories == 1) {
+ return enabledCategories.get(0);
+ } else {
+ // While this string name seems like a slight misnomer: it's borrowing the analogous
+ // calls-summary functionality to combine two permissions.
+ return mContext.getString(R.string.zen_mode_calls_summary_two,
+ enabledCategories.get(0),
+ enabledCategories.get(1));
+ }
+ }
+
+ String getBlockedEffectsSummary(ZenMode zenMode) {
+ if (zenMode.getPolicy().shouldShowAllVisualEffects()) {
+ return mContext.getResources().getString(
+ R.string.zen_mode_restrict_notifications_summary_muted);
+ } else if (zenMode.getPolicy().shouldHideAllVisualEffects()) {
+ return mContext.getResources().getString(
+ R.string.zen_mode_restrict_notifications_summary_hidden);
+ } else {
+ return mContext.getResources().getString(
+ R.string.zen_mode_restrict_notifications_summary_custom);
+ }
+ }
+
+ private List getEnabledCategories(ZenPolicy policy,
+ Predicate filteredCategories, boolean capitalizeFirstInList) {
+ List enabledCategories = new ArrayList<>();
+ for (int category : ALL_PRIORITY_CATEGORIES) {
+ boolean isFirst = capitalizeFirstInList && enabledCategories.isEmpty();
+ if (filteredCategories.test(category) && policy.isCategoryAllowed(category, false)) {
+ if (category == PRIORITY_CATEGORY_REPEAT_CALLERS
+ && policy.isCategoryAllowed(PRIORITY_CATEGORY_CALLS, false)
+ && policy.getPriorityCallSenders() == PEOPLE_TYPE_ANYONE) {
+ continue;
+ }
+
+ // For conversations, only the "priority conversations" setting is relevant; any
+ // other setting is subsumed by the messages-specific messaging.
+ if (category == PRIORITY_CATEGORY_CONVERSATIONS
+ && policy.isCategoryAllowed(PRIORITY_CATEGORY_CONVERSATIONS, false)
+ && policy.getPriorityConversationSenders()
+ != CONVERSATION_SENDERS_IMPORTANT) {
+ continue;
+ }
+
+ enabledCategories.add(getCategory(category, policy, isFirst));
+ }
+ }
+ return enabledCategories;
+ }
+
+ private String getCategory(int category, ZenPolicy policy, boolean isFirst) {
+ if (category == PRIORITY_CATEGORY_ALARMS) {
+ if (isFirst) {
+ return mContext.getString(R.string.zen_mode_alarms_list_first);
+ } else {
+ return mContext.getString(R.string.zen_mode_alarms_list);
+ }
+ } else if (category == PRIORITY_CATEGORY_MEDIA) {
+ if (isFirst) {
+ return mContext.getString(R.string.zen_mode_media_list_first);
+ } else {
+ return mContext.getString(R.string.zen_mode_media_list);
+ }
+ } else if (category == PRIORITY_CATEGORY_SYSTEM) {
+ if (isFirst) {
+ return mContext.getString(R.string.zen_mode_system_list_first);
+ } else {
+ return mContext.getString(R.string.zen_mode_system_list);
+ }
+ } else if (category == PRIORITY_CATEGORY_MESSAGES) {
+ if (policy.getPriorityMessageSenders() == PEOPLE_TYPE_ANYONE) {
+ return mContext.getString(R.string.zen_mode_from_anyone);
+ } else if (policy.getPriorityMessageSenders() == PEOPLE_TYPE_CONTACTS) {
+ return mContext.getString(R.string.zen_mode_from_contacts);
+ } else {
+ return mContext.getString(R.string.zen_mode_from_starred);
+ }
+ } else if (category == PRIORITY_CATEGORY_CONVERSATIONS
+ && policy.getPriorityConversationSenders() == CONVERSATION_SENDERS_IMPORTANT) {
+ if (isFirst) {
+ return mContext.getString(R.string.zen_mode_from_important_conversations);
+ } else {
+ return mContext.getString(
+ R.string.zen_mode_from_important_conversations_second);
+ }
+ } else if (category == PRIORITY_CATEGORY_EVENTS) {
+ if (isFirst) {
+ return mContext.getString(R.string.zen_mode_events_list_first);
+ } else {
+ return mContext.getString(R.string.zen_mode_events_list);
+ }
+ } else if (category == PRIORITY_CATEGORY_REMINDERS) {
+ if (isFirst) {
+ return mContext.getString(R.string.zen_mode_reminders_list_first);
+ } else {
+ return mContext.getString(R.string.zen_mode_reminders_list);
+ }
+ } else if (category == PRIORITY_CATEGORY_CALLS) {
+ if (policy.getPriorityCallSenders() == PEOPLE_TYPE_ANYONE) {
+ if (isFirst) {
+ return mContext.getString(R.string.zen_mode_from_anyone);
+ }
+ return mContext.getString(R.string.zen_mode_all_callers);
+ } else if (policy.getPriorityCallSenders() == PEOPLE_TYPE_CONTACTS) {
+ if (isFirst) {
+ return mContext.getString(R.string.zen_mode_from_contacts);
+ }
+ return mContext.getString(R.string.zen_mode_contacts_callers);
+ } else {
+ if (isFirst) {
+ return mContext.getString(R.string.zen_mode_from_starred);
+ }
+ return mContext.getString(R.string.zen_mode_starred_callers);
+ }
+ } else if (category == PRIORITY_CATEGORY_REPEAT_CALLERS) {
+ if (isFirst) {
+ return mContext.getString(R.string.zen_mode_repeat_callers);
+ } else {
+ return mContext.getString(R.string.zen_mode_repeat_callers_list);
+ }
+ }
+
+ return "";
+ }
+
+ public String getStarredContactsSummary() {
+ List starredContacts = mBackend.getStarredContacts();
+ int numStarredContacts = starredContacts.size();
+ MessageFormat msgFormat = new MessageFormat(
+ mContext.getString(R.string.zen_mode_starred_contacts_summary_contacts),
+ Locale.getDefault());
+ Map args = new HashMap<>();
+ args.put("count", numStarredContacts);
+ if (numStarredContacts >= 1) {
+ args.put("contact_1", starredContacts.get(0));
+ if (numStarredContacts >= 2) {
+ args.put("contact_2", starredContacts.get(1));
+ if (numStarredContacts == 3) {
+ args.put("contact_3", starredContacts.get(2));
+ }
+ }
+ }
+ return msgFormat.format(args);
+ }
+
+ public String getContactsNumberSummary() {
+ MessageFormat msgFormat = new MessageFormat(
+ mContext.getString(R.string.zen_mode_contacts_count),
+ Locale.getDefault());
+ Map args = new HashMap<>();
+ args.put("count", mBackend.queryAllContactsData().getCount());
+ return msgFormat.format(args);
+ }
+
+ public String getPeopleSummary(ZenMode zenMode) {
+ final int callersAllowed = zenMode.getPolicy().getPriorityCallSenders();
+ final int messagesAllowed = zenMode.getPolicy().getPriorityMessageSenders();
+ final int conversationsAllowed = zenMode.getPolicy().getPriorityConversationSenders();
+ final boolean areRepeatCallersAllowed =
+ zenMode.getPolicy().isCategoryAllowed(PRIORITY_CATEGORY_REPEAT_CALLERS, false);
+
+ if (callersAllowed == PEOPLE_TYPE_ANYONE
+ && messagesAllowed == PEOPLE_TYPE_ANYONE
+ && conversationsAllowed == CONVERSATION_SENDERS_ANYONE) {
+ return mContext.getResources().getString(R.string.zen_mode_people_all);
+ } else if (callersAllowed == PEOPLE_TYPE_NONE
+ && messagesAllowed == PEOPLE_TYPE_NONE
+ && conversationsAllowed == CONVERSATION_SENDERS_NONE
+ && !areRepeatCallersAllowed) {
+ return mContext.getResources().getString(R.string.zen_mode_people_none);
+ } else {
+ return mContext.getResources().getString(R.string.zen_mode_people_some);
+ }
+ }
+}
diff --git a/src/com/android/settings/notification/modes/ZenModesBackend.java b/src/com/android/settings/notification/modes/ZenModesBackend.java
index 355adb46198..ac170c6d3d9 100644
--- a/src/com/android/settings/notification/modes/ZenModesBackend.java
+++ b/src/com/android/settings/notification/modes/ZenModesBackend.java
@@ -21,14 +21,22 @@ import static java.util.Objects.requireNonNull;
import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.AutomaticZenRule;
+import android.app.INotificationManager;
import android.app.NotificationManager;
import android.content.Context;
+import android.content.pm.ParceledListSlice;
+import android.database.Cursor;
import android.net.Uri;
+import android.os.ServiceManager;
+import android.provider.ContactsContract;
import android.provider.Settings;
import android.service.notification.Condition;
+import android.service.notification.ConversationChannelWrapper;
import android.service.notification.ZenAdapters;
import android.service.notification.ZenModeConfig;
+import android.util.Log;
+import androidx.annotation.VisibleForTesting;
import com.android.settings.R;
import java.time.Duration;
@@ -51,6 +59,8 @@ class ZenModesBackend {
private static ZenModesBackend sInstance;
private final NotificationManager mNotificationManager;
+ static INotificationManager sINM = INotificationManager.Stub.asInterface(
+ ServiceManager.getService(Context.NOTIFICATION_SERVICE));
private final Context mContext;
@@ -105,6 +115,54 @@ class ZenModesBackend {
}
}
+ public ParceledListSlice getConversations(boolean onlyImportant) {
+ try {
+ return sINM.getConversations(onlyImportant);
+ } catch (Exception e) {
+ Log.w(TAG, "Error calling NoMan", e);
+ return ParceledListSlice.emptyList();
+ }
+ }
+
+ public List getStarredContacts() {
+ Cursor cursor = null;
+ try {
+ cursor = queryStarredContactsData();
+ return getStarredContacts(cursor);
+ } finally {
+ if (cursor != null) {
+ cursor.close();
+ }
+ }
+ }
+
+ @VisibleForTesting
+ List getStarredContacts(Cursor cursor) {
+ List starredContacts = new ArrayList<>();
+ if (cursor != null && cursor.moveToFirst()) {
+ do {
+ String contact = cursor.getString(0);
+ starredContacts.add(contact != null ? contact :
+ mContext.getString(R.string.zen_mode_starred_contacts_empty_name));
+
+ } while (cursor.moveToNext());
+ }
+ return starredContacts;
+ }
+
+ private Cursor queryStarredContactsData() {
+ return mContext.getContentResolver().query(ContactsContract.Contacts.CONTENT_URI,
+ new String[]{ContactsContract.Contacts.DISPLAY_NAME_PRIMARY},
+ ContactsContract.Data.STARRED + "=1", null,
+ ContactsContract.Data.TIMES_CONTACTED);
+ }
+
+ Cursor queryAllContactsData() {
+ return mContext.getContentResolver().query(ContactsContract.Contacts.CONTENT_URI,
+ new String[]{ContactsContract.Contacts.DISPLAY_NAME_PRIMARY},
+ null, null, null);
+ }
+
private ZenMode getManualDndMode(ZenModeConfig config) {
// TODO: b/333530553 - Read ZenDeviceEffects of manual DND.
// TODO: b/333682392 - Replace with final strings for name & trigger description
diff --git a/src/com/android/settings/notification/modes/ZenModesFragmentBase.java b/src/com/android/settings/notification/modes/ZenModesFragmentBase.java
index 3b6d17f4225..595f1d08f29 100644
--- a/src/com/android/settings/notification/modes/ZenModesFragmentBase.java
+++ b/src/com/android/settings/notification/modes/ZenModesFragmentBase.java
@@ -57,9 +57,9 @@ abstract class ZenModesFragmentBase extends RestrictedDashboardFragment {
@Override
public void onAttach(@NonNull Context context) {
- super.onAttach(context);
mContext = context;
mBackend = ZenModesBackend.getInstance(context);
+ super.onAttach(context);
}
@Override
@@ -77,6 +77,12 @@ abstract class ZenModesFragmentBase extends RestrictedDashboardFragment {
}
}
+ @Override
+ public void onResume() {
+ super.onResume();
+ updateZenModeState();
+ }
+
@Override
public void onStop() {
super.onStop();
diff --git a/tests/robotests/src/com/android/settings/notification/modes/ZenModeCallsLinkPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/modes/ZenModeCallsLinkPreferenceControllerTest.java
new file mode 100644
index 00000000000..04d625a2848
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/notification/modes/ZenModeCallsLinkPreferenceControllerTest.java
@@ -0,0 +1,80 @@
+/*
+ * 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.junit.Assert.assertTrue;
+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;
+import org.robolectric.shadows.ShadowApplication;
+import org.robolectric.util.ReflectionHelpers;
+
+@RunWith(RobolectricTestRunner.class)
+public final class ZenModeCallsLinkPreferenceControllerTest {
+
+ private ZenModeCallsLinkPreferenceController 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 ZenModeCallsLinkPreferenceController(
+ 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/ZenModeMessagesLinkPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/modes/ZenModeMessagesLinkPreferenceControllerTest.java
new file mode 100644
index 00000000000..cfeefb40a43
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/notification/modes/ZenModeMessagesLinkPreferenceControllerTest.java
@@ -0,0 +1,77 @@
+/*
+ * 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.junit.Assert.assertTrue;
+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 ZenModeMessagesLinkPreferenceControllerTest {
+
+ private ZenModeMessagesLinkPreferenceController 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 ZenModeMessagesLinkPreferenceController(
+ 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/ZenModePeopleLinkPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/modes/ZenModePeopleLinkPreferenceControllerTest.java
new file mode 100644
index 00000000000..81e64648a33
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/notification/modes/ZenModePeopleLinkPreferenceControllerTest.java
@@ -0,0 +1,77 @@
+/*
+ * 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.junit.Assert.assertTrue;
+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 ZenModePeopleLinkPreferenceControllerTest {
+
+ private ZenModePeopleLinkPreferenceController 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 ZenModePeopleLinkPreferenceController(
+ 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/ZenModePrioritySendersPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/modes/ZenModePrioritySendersPreferenceControllerTest.java
new file mode 100644
index 00000000000..91eb59a77dc
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/notification/modes/ZenModePrioritySendersPreferenceControllerTest.java
@@ -0,0 +1,509 @@
+/*
+ * 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.CONVERSATION_SENDERS_ANYONE;
+import static android.service.notification.ZenPolicy.CONVERSATION_SENDERS_IMPORTANT;
+import static android.service.notification.ZenPolicy.CONVERSATION_SENDERS_NONE;
+import static android.service.notification.ZenPolicy.CONVERSATION_SENDERS_UNSET;
+import static android.service.notification.ZenPolicy.PEOPLE_TYPE_ANYONE;
+import static android.service.notification.ZenPolicy.PEOPLE_TYPE_CONTACTS;
+import static android.service.notification.ZenPolicy.PEOPLE_TYPE_NONE;
+import static android.service.notification.ZenPolicy.PEOPLE_TYPE_STARRED;
+import static android.service.notification.ZenPolicy.PEOPLE_TYPE_UNSET;
+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 com.android.settings.notification.modes.ZenModePrioritySendersPreferenceController.KEY_ANY;
+import static com.android.settings.notification.modes.ZenModePrioritySendersPreferenceController.KEY_CONTACTS;
+import static com.android.settings.notification.modes.ZenModePrioritySendersPreferenceController.KEY_IMPORTANT;
+import static com.android.settings.notification.modes.ZenModePrioritySendersPreferenceController.KEY_NONE;
+import static com.android.settings.notification.modes.ZenModePrioritySendersPreferenceController.KEY_STARRED;
+import static com.google.common.truth.Truth.assertThat;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.argThat;
+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.net.Uri;
+import android.platform.test.annotations.EnableFlags;
+import android.platform.test.flag.junit.SetFlagsRule;
+import android.service.notification.ZenPolicy;
+import androidx.preference.PreferenceCategory;
+import androidx.preference.PreferenceScreen;
+import com.android.settingslib.widget.SelectorWithWidgetPreference;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.ArgumentMatcher;
+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 ZenModePrioritySendersPreferenceControllerTest {
+
+ private ZenModePrioritySendersPreferenceController mCallsController;
+ private ZenModePrioritySendersPreferenceController mMessagesController;
+
+ @Rule
+ public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+
+ private Context mContext;
+ @Mock
+ private ZenModesBackend mBackend;
+
+ @Mock
+ private PreferenceCategory mMockMessagesPrefCategory, mMockCallsPrefCategory;
+ @Mock
+ private PreferenceScreen mPreferenceScreen;
+
+ @Before
+ public void setup() {
+ MockitoAnnotations.initMocks(this);
+
+ mContext = RuntimeEnvironment.application;
+
+ mMessagesController = new ZenModePrioritySendersPreferenceController(
+ mContext, "messages", true, mBackend);
+ mCallsController = new ZenModePrioritySendersPreferenceController(
+ mContext, "calls", false, mBackend);
+ when(mMockMessagesPrefCategory.getContext()).thenReturn(mContext);
+ when(mMockCallsPrefCategory.getContext()).thenReturn(mContext);
+ when(mPreferenceScreen.findPreference(mMessagesController.getPreferenceKey()))
+ .thenReturn(mMockMessagesPrefCategory);
+ when(mPreferenceScreen.findPreference(mCallsController.getPreferenceKey()))
+ .thenReturn(mMockCallsPrefCategory);
+ }
+
+ // Makes a preference with the provided key and whether it's a checkbox with
+ // mSelectorClickListener as the onClickListener set.
+ private SelectorWithWidgetPreference makePreference(
+ String key, boolean isCheckbox, boolean isMessages) {
+ final SelectorWithWidgetPreference pref =
+ new SelectorWithWidgetPreference(mContext, isCheckbox);
+ pref.setKey(key);
+ pref.setOnClickListener(
+ isMessages ? mMessagesController.mSelectorClickListener
+ : mCallsController.mSelectorClickListener);
+ return pref;
+ }
+
+ // Extension of ArgumentMatcher to check that a preference argument has the correct preference
+ // key, but doesn't check any other properties.
+ private class PrefKeyMatcher implements ArgumentMatcher {
+ private String mKey;
+ PrefKeyMatcher(String key) {
+ mKey = key;
+ }
+
+ public boolean matches(SelectorWithWidgetPreference pref) {
+ return pref.getKey() != null && pref.getKey().equals(mKey);
+ }
+
+ public String toString() {
+ return "SelectorWithWidgetPreference matcher for key " + mKey;
+ }
+ }
+
+ @Test
+ public void testDisplayPreferences_makeMessagesPrefs() {
+ ArgumentCaptor prefCaptor =
+ ArgumentCaptor.forClass(SelectorWithWidgetPreference.class);
+ when(mMockMessagesPrefCategory.getPreferenceCount()).thenReturn(0); // not yet created
+ mMessagesController.displayPreference(mPreferenceScreen);
+
+ // Starred contacts, Contacts, Priority Conversations, Any, None
+ verify(mMockMessagesPrefCategory, times(5)).addPreference(prefCaptor.capture());
+ }
+
+ @Test
+ public void testDisplayPreferences_makeCallsPrefs() {
+ ArgumentCaptor prefCaptor =
+ ArgumentCaptor.forClass(SelectorWithWidgetPreference.class);
+ when(mMockCallsPrefCategory.getPreferenceCount()).thenReturn(0); // not yet created
+ mCallsController.displayPreference(mPreferenceScreen);
+
+ // Starred contacts, Contacts, Any, None
+ verify(mMockCallsPrefCategory, times(4)).addPreference(prefCaptor.capture());
+
+ // Make sure we never have the conversation one
+ verify(mMockCallsPrefCategory, never())
+ .addPreference(argThat(new PrefKeyMatcher(KEY_IMPORTANT)));
+ }
+
+ @Test
+ public void testDisplayPreferences_createdOnlyOnce() {
+ // Return a nonzero number of child preference when asked.
+ // Then when displayPreference is called, it should never make any new preferences.
+ when(mMockCallsPrefCategory.getPreferenceCount()).thenReturn(4); // already created
+ mCallsController.displayPreference(mPreferenceScreen);
+ mCallsController.displayPreference(mPreferenceScreen);
+ mCallsController.displayPreference(mPreferenceScreen);
+
+ // Even though we called display 3 times we shouldn't add more preferences here.
+ verify(mMockCallsPrefCategory, never())
+ .addPreference(any(SelectorWithWidgetPreference.class));
+ }
+
+ @Test
+ public void testKeyToSettingEndState_messagesCheck() {
+ int[] endState;
+
+ // For KEY_NONE everything should be none.
+ endState = mMessagesController.keyToSettingEndState(KEY_NONE, true);
+ assertThat(endState[0]).isEqualTo(PEOPLE_TYPE_NONE);
+ assertThat(endState[1]).isEqualTo(CONVERSATION_SENDERS_NONE);
+
+ // For KEY_ANY everything should be allowed.
+ endState = mMessagesController.keyToSettingEndState(KEY_ANY, true);
+ assertThat(endState[0]).isEqualTo(PEOPLE_TYPE_ANYONE);
+ assertThat(endState[1]).isEqualTo(CONVERSATION_SENDERS_ANYONE);
+
+ // For [starred] contacts, we should set the priority senders, but not the conversations
+ endState = mMessagesController.keyToSettingEndState(KEY_STARRED, true);
+ assertThat(endState[0]).isEqualTo(PEOPLE_TYPE_STARRED);
+ assertThat(endState[1]).isEqualTo(CONVERSATION_SENDERS_UNSET);
+
+ endState = mMessagesController.keyToSettingEndState(KEY_CONTACTS, true);
+ assertThat(endState[0]).isEqualTo(PEOPLE_TYPE_CONTACTS);
+ assertThat(endState[1]).isEqualTo(CONVERSATION_SENDERS_UNSET);
+
+ // For priority conversations, we should set the conversations but not priority senders
+ endState = mMessagesController.keyToSettingEndState(KEY_IMPORTANT, true);
+ assertThat(endState[0]).isEqualTo(PEOPLE_TYPE_UNSET);
+ assertThat(endState[1]).isEqualTo(CONVERSATION_SENDERS_IMPORTANT);
+ }
+
+ @Test
+ public void testKeyToSettingEndState_messagesUncheck() {
+ int[] endState;
+
+ // For KEY_NONE, "unchecking" still means "none".
+ endState = mMessagesController.keyToSettingEndState(KEY_NONE, false);
+ assertThat(endState[0]).isEqualTo(PEOPLE_TYPE_NONE);
+ assertThat(endState[1]).isEqualTo(CONVERSATION_SENDERS_NONE);
+
+ // For KEY_ANY unchecking resets the state to "none".
+ endState = mMessagesController.keyToSettingEndState(KEY_ANY, false);
+ assertThat(endState[0]).isEqualTo(PEOPLE_TYPE_NONE);
+ assertThat(endState[1]).isEqualTo(CONVERSATION_SENDERS_NONE);
+
+ // For [starred] contacts, we should unset the priority senders, but not the conversations
+ endState = mMessagesController.keyToSettingEndState(KEY_STARRED, false);
+ assertThat(endState[0]).isEqualTo(PEOPLE_TYPE_NONE);
+ assertThat(endState[1]).isEqualTo(CONVERSATION_SENDERS_UNSET);
+
+ endState = mMessagesController.keyToSettingEndState(KEY_CONTACTS, false);
+ assertThat(endState[0]).isEqualTo(PEOPLE_TYPE_NONE);
+ assertThat(endState[1]).isEqualTo(CONVERSATION_SENDERS_UNSET);
+
+ // For priority conversations, we should set the conversations but not priority senders
+ endState = mMessagesController.keyToSettingEndState(KEY_IMPORTANT, false);
+ assertThat(endState[0]).isEqualTo(PEOPLE_TYPE_UNSET);
+ assertThat(endState[1]).isEqualTo(CONVERSATION_SENDERS_NONE);
+ }
+
+ @Test
+ public void testKeyToSettingEndState_callsCheck() {
+ int[] endState;
+
+ // For calls: we should never set conversations, as this is unrelated to calls.
+ // For KEY_NONE senders should be none.
+ endState = mCallsController.keyToSettingEndState(KEY_NONE, true);
+ assertThat(endState[0]).isEqualTo(PEOPLE_TYPE_NONE);
+ assertThat(endState[1]).isEqualTo(CONVERSATION_SENDERS_UNSET);
+
+ // For KEY_ANY senders should be ANY.
+ endState = mCallsController.keyToSettingEndState(KEY_ANY, true);
+ assertThat(endState[0]).isEqualTo(PEOPLE_TYPE_ANYONE);
+ assertThat(endState[1]).isEqualTo(CONVERSATION_SENDERS_UNSET);
+
+ // For [starred] contacts, we should set the priority senders accordingly
+ endState = mCallsController.keyToSettingEndState(KEY_STARRED, true);
+ assertThat(endState[0]).isEqualTo(PEOPLE_TYPE_STARRED);
+ assertThat(endState[1]).isEqualTo(CONVERSATION_SENDERS_UNSET);
+
+ endState = mCallsController.keyToSettingEndState(KEY_CONTACTS, true);
+ assertThat(endState[0]).isEqualTo(PEOPLE_TYPE_CONTACTS);
+ assertThat(endState[1]).isEqualTo(CONVERSATION_SENDERS_UNSET);
+ }
+
+ @Test
+ public void testKeyToSettingEndState_callsUncheck() {
+ int[] endState;
+
+ // A calls setup should never set conversations settings.
+ // For KEY_NONE, "unchecking" still means "none".
+ endState = mCallsController.keyToSettingEndState(KEY_NONE, false);
+ assertThat(endState[0]).isEqualTo(PEOPLE_TYPE_NONE);
+ assertThat(endState[1]).isEqualTo(CONVERSATION_SENDERS_UNSET);
+
+ // For KEY_ANY unchecking resets the state to "none".
+ endState = mCallsController.keyToSettingEndState(KEY_ANY, false);
+ assertThat(endState[0]).isEqualTo(PEOPLE_TYPE_NONE);
+ assertThat(endState[1]).isEqualTo(CONVERSATION_SENDERS_UNSET);
+
+ // For [starred] contacts, we should unset the priority senders, but not the conversations
+ endState = mCallsController.keyToSettingEndState(KEY_STARRED, false);
+ assertThat(endState[0]).isEqualTo(PEOPLE_TYPE_NONE);
+ assertThat(endState[1]).isEqualTo(CONVERSATION_SENDERS_UNSET);
+
+ endState = mCallsController.keyToSettingEndState(KEY_CONTACTS, false);
+ assertThat(endState[0]).isEqualTo(PEOPLE_TYPE_NONE);
+ assertThat(endState[1]).isEqualTo(CONVERSATION_SENDERS_UNSET);
+ }
+
+ @Test
+ public void testSettingsToSaveOnClick_messagesCheck() {
+ SelectorWithWidgetPreference anyPref = makePreference(KEY_ANY, true, true);
+ SelectorWithWidgetPreference nonePref = makePreference(KEY_NONE, true, true);
+ SelectorWithWidgetPreference contactsPref = makePreference(KEY_CONTACTS, true, true);
+ SelectorWithWidgetPreference starredPref = makePreference(KEY_STARRED, true, true);
+ SelectorWithWidgetPreference impPref = makePreference(KEY_IMPORTANT, true, true);
+ int[] endState;
+
+ // For KEY_NONE everything should be none.
+ nonePref.setChecked(true);
+ endState = mMessagesController.settingsToSaveOnClick(
+ nonePref, PEOPLE_TYPE_ANYONE, CONVERSATION_SENDERS_ANYONE);
+ assertThat(endState[0]).isEqualTo(PEOPLE_TYPE_NONE);
+ assertThat(endState[1]).isEqualTo(CONVERSATION_SENDERS_NONE);
+
+ // For KEY_ANY everything should be allowed.
+ anyPref.setChecked(true);
+ endState = mMessagesController.settingsToSaveOnClick(
+ anyPref, PEOPLE_TYPE_NONE, CONVERSATION_SENDERS_NONE);
+ assertThat(endState[0]).isEqualTo(PEOPLE_TYPE_ANYONE);
+ assertThat(endState[1]).isEqualTo(CONVERSATION_SENDERS_ANYONE);
+
+ // For [starred] contacts, we should set the priority senders, but not the conversations
+ starredPref.setChecked(true);
+ endState = mMessagesController.settingsToSaveOnClick(
+ starredPref, PEOPLE_TYPE_NONE, CONVERSATION_SENDERS_NONE);
+ assertThat(endState[0]).isEqualTo(PEOPLE_TYPE_STARRED);
+ assertThat(endState[1]).isEqualTo(CONVERSATION_SENDERS_UNSET);
+
+ contactsPref.setChecked(true);
+ endState = mMessagesController.settingsToSaveOnClick(
+ contactsPref, PEOPLE_TYPE_NONE, CONVERSATION_SENDERS_NONE);
+ assertThat(endState[0]).isEqualTo(PEOPLE_TYPE_CONTACTS);
+ assertThat(endState[1]).isEqualTo(CONVERSATION_SENDERS_UNSET);
+
+ // For priority conversations, we should set the conversations but not priority senders
+ impPref.setChecked(true);
+ endState = mMessagesController.settingsToSaveOnClick(
+ impPref, PEOPLE_TYPE_NONE, CONVERSATION_SENDERS_NONE);
+ assertThat(endState[0]).isEqualTo(PEOPLE_TYPE_UNSET);
+ assertThat(endState[1]).isEqualTo(CONVERSATION_SENDERS_IMPORTANT);
+ }
+
+ @Test
+ public void testSettingsToSaveOnClick_messagesUncheck() {
+ int[] endState;
+
+ SelectorWithWidgetPreference anyPref = makePreference(KEY_ANY, true, true);
+ SelectorWithWidgetPreference nonePref = makePreference(KEY_NONE, true, true);
+ SelectorWithWidgetPreference contactsPref = makePreference(KEY_CONTACTS, true, true);
+ SelectorWithWidgetPreference starredPref = makePreference(KEY_STARRED, true, true);
+ SelectorWithWidgetPreference impPref = makePreference(KEY_IMPORTANT, true, true);
+
+ // For KEY_NONE, "unchecking" still means "none".
+ nonePref.setChecked(false);
+ endState = mMessagesController.settingsToSaveOnClick(
+ nonePref, PEOPLE_TYPE_NONE, CONVERSATION_SENDERS_NONE);
+ assertThat(endState[0]).isEqualTo(PEOPLE_TYPE_UNSET);
+ assertThat(endState[1]).isEqualTo(CONVERSATION_SENDERS_UNSET);
+
+ // For KEY_ANY unchecking resets the state to "none".
+ anyPref.setChecked(false);
+ endState = mMessagesController.settingsToSaveOnClick(
+ anyPref, PEOPLE_TYPE_ANYONE, CONVERSATION_SENDERS_ANYONE);
+ assertThat(endState[0]).isEqualTo(PEOPLE_TYPE_NONE);
+ assertThat(endState[1]).isEqualTo(CONVERSATION_SENDERS_NONE);
+
+ // For [starred] contacts, we should unset the priority senders, but not the conversations
+ starredPref.setChecked(false);
+ endState = mMessagesController.settingsToSaveOnClick(
+ starredPref, PEOPLE_TYPE_STARRED, CONVERSATION_SENDERS_IMPORTANT);
+ assertThat(endState[0]).isEqualTo(PEOPLE_TYPE_NONE);
+ assertThat(endState[1]).isEqualTo(CONVERSATION_SENDERS_UNSET);
+
+ contactsPref.setChecked(false);
+ endState = mMessagesController.settingsToSaveOnClick(
+ contactsPref, PEOPLE_TYPE_CONTACTS, CONVERSATION_SENDERS_IMPORTANT);
+ assertThat(endState[0]).isEqualTo(PEOPLE_TYPE_NONE);
+ assertThat(endState[1]).isEqualTo(CONVERSATION_SENDERS_UNSET);
+
+ // For priority conversations, we should set the conversations but not priority senders
+ impPref.setChecked(false);
+ endState = mMessagesController.settingsToSaveOnClick(
+ impPref, PEOPLE_TYPE_CONTACTS, CONVERSATION_SENDERS_IMPORTANT);
+ assertThat(endState[0]).isEqualTo(PEOPLE_TYPE_UNSET);
+ assertThat(endState[1]).isEqualTo(CONVERSATION_SENDERS_NONE);
+ }
+
+ @Test
+ public void testSettingsToSaveOnClick_callsCheck() {
+ int[] endState;
+ SelectorWithWidgetPreference anyPref = makePreference(KEY_ANY, true, true);
+ SelectorWithWidgetPreference nonePref = makePreference(KEY_NONE, true, true);
+ SelectorWithWidgetPreference contactsPref = makePreference(KEY_CONTACTS, true, true);
+ SelectorWithWidgetPreference starredPref = makePreference(KEY_STARRED, true, true);
+
+ // For calls: we should never set conversations, as this is unrelated to calls.
+ // For KEY_NONE senders should be none.
+ nonePref.setChecked(true);
+ endState = mCallsController.settingsToSaveOnClick(
+ nonePref, PEOPLE_TYPE_CONTACTS, CONVERSATION_SENDERS_IMPORTANT);
+ assertThat(endState[0]).isEqualTo(PEOPLE_TYPE_NONE);
+ assertThat(endState[1]).isEqualTo(CONVERSATION_SENDERS_UNSET);
+
+ // For KEY_ANY senders should be ANY.
+ anyPref.setChecked(true);
+ endState = mCallsController.settingsToSaveOnClick(
+ anyPref, PEOPLE_TYPE_CONTACTS, CONVERSATION_SENDERS_IMPORTANT);
+ assertThat(endState[0]).isEqualTo(PEOPLE_TYPE_ANYONE);
+ assertThat(endState[1]).isEqualTo(CONVERSATION_SENDERS_UNSET);
+
+ // For [starred] contacts, we should set the priority senders accordingly
+ starredPref.setChecked(true);
+ endState = mCallsController.settingsToSaveOnClick(
+ starredPref, PEOPLE_TYPE_CONTACTS, CONVERSATION_SENDERS_IMPORTANT);
+ assertThat(endState[0]).isEqualTo(PEOPLE_TYPE_STARRED);
+ assertThat(endState[1]).isEqualTo(CONVERSATION_SENDERS_UNSET);
+
+ contactsPref.setChecked(true);
+ endState = mCallsController.settingsToSaveOnClick(
+ contactsPref, PEOPLE_TYPE_STARRED, CONVERSATION_SENDERS_IMPORTANT);
+ assertThat(endState[0]).isEqualTo(PEOPLE_TYPE_CONTACTS);
+ assertThat(endState[1]).isEqualTo(CONVERSATION_SENDERS_UNSET);
+ }
+
+ @Test
+ public void testSettingsToSaveOnClick_callsUncheck() {
+ int[] endState;
+ SelectorWithWidgetPreference anyPref = makePreference(KEY_ANY, true, true);
+ SelectorWithWidgetPreference nonePref = makePreference(KEY_NONE, true, true);
+ SelectorWithWidgetPreference contactsPref = makePreference(KEY_CONTACTS, true, true);
+ SelectorWithWidgetPreference starredPref = makePreference(KEY_STARRED, true, true);
+
+ // A calls setup should never set conversations settings.
+ // For KEY_NONE, "unchecking" still means "none".
+ nonePref.setChecked(false);
+ endState = mCallsController.settingsToSaveOnClick(
+ nonePref, PEOPLE_TYPE_NONE, CONVERSATION_SENDERS_NONE);
+ assertThat(endState[0]).isEqualTo(STATE_UNSET);
+ assertThat(endState[1]).isEqualTo(CONVERSATION_SENDERS_UNSET);
+
+ // For KEY_ANY unchecking resets the state to "none".
+ anyPref.setChecked(false);
+ endState = mCallsController.settingsToSaveOnClick(
+ anyPref, PEOPLE_TYPE_ANYONE, CONVERSATION_SENDERS_ANYONE);
+ assertThat(endState[0]).isEqualTo(PEOPLE_TYPE_NONE);
+ assertThat(endState[1]).isEqualTo(CONVERSATION_SENDERS_UNSET);
+
+ // For [starred] contacts, we should unset the priority senders, but not the conversations
+ starredPref.setChecked(false);
+ endState = mCallsController.settingsToSaveOnClick(
+ starredPref, PEOPLE_TYPE_STARRED, CONVERSATION_SENDERS_IMPORTANT);
+ assertThat(endState[0]).isEqualTo(PEOPLE_TYPE_NONE);
+ assertThat(endState[1]).isEqualTo(CONVERSATION_SENDERS_UNSET);
+
+ contactsPref.setChecked(false);
+ endState = mCallsController.settingsToSaveOnClick(
+ contactsPref, PEOPLE_TYPE_CONTACTS, CONVERSATION_SENDERS_IMPORTANT);
+ assertThat(endState[0]).isEqualTo(PEOPLE_TYPE_NONE);
+ assertThat(endState[1]).isEqualTo(CONVERSATION_SENDERS_UNSET);
+ }
+
+ @Test
+ public void testSettingsToSave_messages_noChange() {
+ int[] savedSettings;
+
+ SelectorWithWidgetPreference nonePref = makePreference(KEY_NONE, true, true);
+ nonePref.setChecked(true);
+ savedSettings = mMessagesController.settingsToSaveOnClick(
+ nonePref, PEOPLE_TYPE_NONE, CONVERSATION_SENDERS_NONE);
+ assertThat(savedSettings[0]).isEqualTo(STATE_UNSET);
+ assertThat(savedSettings[1]).isEqualTo(STATE_UNSET);
+
+ SelectorWithWidgetPreference anyPref = makePreference(KEY_ANY, true, true);
+ anyPref.setChecked(true);
+ savedSettings = mMessagesController.settingsToSaveOnClick(
+ anyPref, PEOPLE_TYPE_ANYONE, CONVERSATION_SENDERS_ANYONE);
+ assertThat(savedSettings[0]).isEqualTo(STATE_UNSET);
+ assertThat(savedSettings[1]).isEqualTo(STATE_UNSET);
+
+ SelectorWithWidgetPreference starredPref = makePreference(KEY_STARRED, true, true);
+ SelectorWithWidgetPreference contactsPref = makePreference(KEY_CONTACTS, true, true);
+ starredPref.setChecked(true);
+ savedSettings = mMessagesController.settingsToSaveOnClick(
+ starredPref, PEOPLE_TYPE_STARRED, CONVERSATION_SENDERS_ANYONE);
+ assertThat(savedSettings[0]).isEqualTo(STATE_UNSET);
+
+ contactsPref.setChecked(true);
+ savedSettings = mMessagesController.settingsToSaveOnClick(
+ contactsPref, PEOPLE_TYPE_CONTACTS, CONVERSATION_SENDERS_ANYONE);
+ assertThat(savedSettings[0]).isEqualTo(STATE_UNSET);
+
+ SelectorWithWidgetPreference impPref = makePreference(KEY_IMPORTANT, true, true);
+ impPref.setChecked(true);
+ savedSettings = mMessagesController.settingsToSaveOnClick(
+ impPref, PEOPLE_TYPE_CONTACTS, CONVERSATION_SENDERS_IMPORTANT);
+ assertThat(savedSettings[1]).isEqualTo(STATE_UNSET);
+ }
+
+ @Test
+ public void testSettingsToSave_calls_noChange() {
+ int[] savedSettings;
+ SelectorWithWidgetPreference nonePref = makePreference(KEY_NONE, false, false);
+
+ savedSettings = mMessagesController.settingsToSaveOnClick(
+ nonePref, PEOPLE_TYPE_NONE, CONVERSATION_SENDERS_NONE);
+ assertThat(savedSettings[0]).isEqualTo(STATE_UNSET);
+ assertThat(savedSettings[1]).isEqualTo(STATE_UNSET);
+
+ SelectorWithWidgetPreference anyPref = makePreference(KEY_ANY, false, false);
+
+ savedSettings = mMessagesController.settingsToSaveOnClick(
+ anyPref, PEOPLE_TYPE_ANYONE, CONVERSATION_SENDERS_ANYONE);
+ assertThat(savedSettings[0]).isEqualTo(STATE_UNSET);
+ assertThat(savedSettings[1]).isEqualTo(STATE_UNSET);
+
+ SelectorWithWidgetPreference starredPref = makePreference(KEY_STARRED, false, false);
+ SelectorWithWidgetPreference contactsPref = makePreference(KEY_CONTACTS, false, false);
+ savedSettings = mMessagesController.settingsToSaveOnClick(
+ starredPref, PEOPLE_TYPE_STARRED, CONVERSATION_SENDERS_ANYONE);
+ assertThat(savedSettings[0]).isEqualTo(STATE_UNSET);
+
+ savedSettings = mMessagesController.settingsToSaveOnClick(
+ contactsPref, PEOPLE_TYPE_CONTACTS, CONVERSATION_SENDERS_ANYONE);
+ assertThat(savedSettings[0]).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
new file mode 100644
index 00000000000..621c5b06f9d
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/notification/modes/ZenModesSummaryHelperTest.java
@@ -0,0 +1,88 @@
+/*
+ * 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.CONVERSATION_SENDERS_ANYONE;
+import static android.service.notification.ZenPolicy.PEOPLE_TYPE_ANYONE;
+import static android.service.notification.ZenPolicy.PEOPLE_TYPE_CONTACTS;
+import static com.google.common.truth.Truth.assertThat;
+
+import android.app.AutomaticZenRule;
+import android.content.Context;
+import android.net.Uri;
+import android.service.notification.ZenPolicy;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+
+@RunWith(RobolectricTestRunner.class)
+public class ZenModesSummaryHelperTest {
+ private Context mContext;
+ private ZenModesBackend mBackend;
+
+ private ZenModeSummaryHelper mSummaryHelper;
+
+ @Before
+ public void setup() {
+ MockitoAnnotations.initMocks(this);
+ mContext = RuntimeEnvironment.application;
+ mBackend = new ZenModesBackend(mContext);
+ mSummaryHelper = new ZenModeSummaryHelper(mContext, mBackend);
+ }
+
+ @Test
+ public void getPeopleSummary_noOne() {
+ AutomaticZenRule rule = new AutomaticZenRule.Builder("Bedtime", Uri.parse("bed"))
+ .setType(AutomaticZenRule.TYPE_BEDTIME)
+ .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
+ .setZenPolicy(new ZenPolicy.Builder().disallowAllSounds().build())
+ .build();
+ ZenMode zenMode = new ZenMode("id", rule, true);
+
+ assertThat(mSummaryHelper.getPeopleSummary(zenMode)).isEqualTo("No one can interrupt");
+ }
+
+ @Test
+ public void getPeopleSummary_some() {
+ AutomaticZenRule rule = new AutomaticZenRule.Builder("Bedtime", Uri.parse("bed"))
+ .setType(AutomaticZenRule.TYPE_BEDTIME)
+ .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
+ .setZenPolicy(new ZenPolicy.Builder().allowCalls(PEOPLE_TYPE_CONTACTS).build())
+ .build();
+ ZenMode zenMode = new ZenMode("id", rule, true);
+
+ assertThat(mSummaryHelper.getPeopleSummary(zenMode)).isEqualTo("Some people can interrupt");
+ }
+
+ @Test
+ public void getPeopleSummary_all() {
+ AutomaticZenRule rule = new AutomaticZenRule.Builder("Bedtime", Uri.parse("bed"))
+ .setType(AutomaticZenRule.TYPE_BEDTIME)
+ .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
+ .setZenPolicy(new ZenPolicy.Builder().allowCalls(PEOPLE_TYPE_ANYONE).
+ allowConversations(CONVERSATION_SENDERS_ANYONE)
+ .allowMessages(PEOPLE_TYPE_ANYONE).build())
+ .build();
+ ZenMode zenMode = new ZenMode("id", rule, true);
+
+ assertThat(mSummaryHelper.getPeopleSummary(zenMode)).isEqualTo("All people can interrupt");
+ }
+}