Adds modes settings page for bypassing apps

Adds the settings page that a user will reach from a mode settings page
when they tap on "App," to allow  them to select whether all, none, or
priority modes can break through and send notifications.

Bug: 308819928
Bug: 331267485
Flag: android.app.modes_ui DEVELOPMENT
Test: build and test, controller unit tests
Change-Id: I5bcebfca0fc2882f839afdced8d2b817dad14e6c
This commit is contained in:
Alexander Roederer
2024-05-14 23:42:07 +00:00
parent aa6eb398fd
commit c512046eb9
11 changed files with 767 additions and 0 deletions

View File

@@ -7994,6 +7994,7 @@
}
</string>
<!-- Do not disturb: restrict notifications settings title [CHAR LIMIT=80] -->
<string name="zen_mode_restrict_notifications_title">Display options for filtered
notifications</string>
@@ -9155,6 +9156,17 @@
<!-- [CHAR LIMIT=50] Zen mode settings: Events (ie: calendar events) category in a list of sounds when events is the first or only element in the list. For example “Events can interrupt" or “Events and touch sounds can interrupt" -->
<string name="zen_mode_events_list_first">Events</string>
<!-- [CHAR LIMIT=80] Zen mode settings: Title for page to select which apps allowed to interrupt dnd -->
<string name="zen_mode_apps_title">Apps</string>
<!-- [CHAR LIMIT=100] Zen mode settings: subtitle for page to select which apps allowed to interrupt dnd -->
<string name="zen_mode_apps_category">Apps that can interrupt</string>
<!-- [CHAR LIMIT=60] Zen mode settings: selected apps will be able to bypass dnd -->
<string name="zen_mode_apps_priority_apps">Selected apps</string>
<!-- [CHAR LIMIT=60] Zen mode settings: no apps will be able to bypass dnd -->
<string name="zen_mode_apps_none_apps">None</string>
<!-- [CHAR LIMIT=60] Zen mode settings: all apps will be able to bypass dnd -->
<string name="zen_mode_apps_all_apps">All</string>
<!-- [CHAR LIMIT=100] Zen mode settings: Allow apps to bypass DND -->
<string name="zen_mode_bypassing_apps">Allow apps to override</string>
<!-- [CHAR LIMIT=100] Zen mode settings: Allow apps to bypass DND header -->

View File

@@ -29,6 +29,10 @@
android:key="zen_mode_people"
android:title="@string/zen_category_people"/>
<Preference
android:key="zen_mode_apps"
android:title="@string/zen_category_apps" />
<Preference
android:key="zen_other_settings"
android:title="@string/zen_category_exceptions" />

View File

@@ -0,0 +1,44 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2024 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<PreferenceScreen
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:settings="http://schemas.android.com/apk/res-auto"
android:title="@string/zen_mode_apps_title" >
<PreferenceCategory
android:key="zen_mode_apps_category"
android:title="@string/zen_mode_apps_category">
<com.android.settingslib.widget.SelectorWithWidgetPreference
android:key="zen_mode_apps_priority"
android:title="@string/zen_mode_apps_priority_apps"
settings:searchable="false"/>
<com.android.settingslib.widget.SelectorWithWidgetPreference
android:key="zen_mode_apps_none"
android:title="@string/zen_mode_apps_none_apps"
settings:searchable="false"/>
<com.android.settingslib.widget.SelectorWithWidgetPreference
android:key="zen_mode_apps_all"
android:title="@string/zen_mode_apps_all_apps"
settings:searchable="false"/>
</PreferenceCategory>
</PreferenceScreen>

View File

@@ -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;
/**
* Mode > Apps
*/
public class ZenModeAppsFragment extends ZenModeFragmentBase {
@Override
protected List<AbstractPreferenceController> createPreferenceControllers(Context context) {
List<AbstractPreferenceController> controllers = new ArrayList<>();
controllers.add(new ZenModeAppsPreferenceController(
context, ZenModeAppsPreferenceController.KEY_PRIORITY, mBackend));
controllers.add(new ZenModeAppsPreferenceController(
context, ZenModeAppsPreferenceController.KEY_NONE, mBackend));
// TODO: b/308819928 - The manual DND mode cannot have the ALL type;
// unify the controllers into one and only create a preference if isManualDnd is false.
controllers.add(new ZenModeAppsPreferenceController(
context, ZenModeAppsPreferenceController.KEY_ALL, mBackend));
return controllers;
}
@Override
protected int getPreferenceScreenResId() {
return R.xml.zen_mode_apps_settings;
}
@Override
public int getMetricsCategory() {
// TODO: b/332937635 - make this the correct metrics category
return SettingsEnums.NOTIFICATION_ZEN_MODE_PRIORITY;
}
}

View File

@@ -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.annotation.NonNull;
import androidx.preference.Preference;
import com.android.settings.core.SubSettingLauncher;
/**
* Preference with a link and summary about what apps can break through the mode
*/
public class ZenModeAppsLinkPreferenceController extends AbstractZenModePreferenceController {
private final ZenModeSummaryHelper mSummaryHelper;
public ZenModeAppsLinkPreferenceController(Context context, String key,
ZenModesBackend backend) {
super(context, key, backend);
mSummaryHelper = new ZenModeSummaryHelper(mContext, mBackend);
}
@Override
public void updateState(Preference preference, @NonNull ZenMode zenMode) {
Bundle bundle = new Bundle();
bundle.putString(MODE_ID, zenMode.getId());
// TODO(b/332937635): Update metrics category
preference.setIntent(new SubSettingLauncher(mContext)
.setDestination(ZenModeAppsFragment.class.getName())
.setSourceMetricsCategory(0)
.setArguments(bundle)
.toIntent());
preference.setSummary(mSummaryHelper.getAppsSummary(zenMode));
}
}

View File

@@ -0,0 +1,116 @@
/*
* 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 androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import androidx.preference.Preference;
import androidx.preference.PreferenceScreen;
import androidx.preference.TwoStatePreference;
import com.android.settings.core.SubSettingLauncher;
import com.android.settings.notification.zen.ZenModeBypassingAppsSettings;
import com.android.settingslib.widget.SelectorWithWidgetPreference;
public class ZenModeAppsPreferenceController extends
AbstractZenModePreferenceController implements Preference.OnPreferenceChangeListener {
static final String KEY_PRIORITY = "zen_mode_apps_priority";
static final String KEY_NONE = "zen_mode_apps_none";
static final String KEY_ALL = "zen_mode_apps_all";
public ZenModeAppsPreferenceController(@NonNull Context context,
@NonNull String key, @Nullable ZenModesBackend backend) {
super(context, key, backend);
}
@Override
public void displayPreference(PreferenceScreen screen) {
SelectorWithWidgetPreference pref = screen.findPreference(getPreferenceKey());
if (pref != null) {
pref.setOnClickListener(mSelectorClickListener);
// Adds the widget to only the priority category.
if (getPreferenceKey().equals(KEY_PRIORITY)) {
pref.setExtraWidgetOnClickListener(p -> {
launchPrioritySettings();
});
}
}
super.displayPreference(screen);
}
@Override
public void updateState(Preference preference, @NonNull ZenMode zenMode) {
TwoStatePreference pref = (TwoStatePreference) preference;
switch (getPreferenceKey()) {
case KEY_PRIORITY:
boolean policy_priority = zenMode.getPolicy().getAllowedChannels()
== ZenPolicy.CHANNEL_POLICY_PRIORITY;
pref.setChecked(policy_priority);
break;
case KEY_NONE:
boolean policy_none = zenMode.getPolicy().getAllowedChannels()
== ZenPolicy.CHANNEL_POLICY_NONE;
pref.setChecked(policy_none);
break;
case KEY_ALL:
// A UI-only setting; the underlying policy never actually has this value,
// but ZenMode acts as though it does for the sake of UI consistency.
boolean policy_all = zenMode.getPolicy().getAllowedChannels()
== ZenMode.CHANNEL_POLICY_ALL;
pref.setChecked(policy_all);
break;
}
}
@Override
public boolean onPreferenceChange(@NonNull Preference preference, Object newValue) {
switch (getPreferenceKey()) {
case KEY_PRIORITY:
return savePolicy(p -> p.allowChannels(ZenPolicy.CHANNEL_POLICY_PRIORITY));
case KEY_NONE:
return savePolicy(p -> p.allowChannels(ZenPolicy.CHANNEL_POLICY_NONE));
case KEY_ALL:
return savePolicy(p -> p.allowChannels(ZenMode.CHANNEL_POLICY_ALL));
}
return true;
}
@VisibleForTesting
SelectorWithWidgetPreference.OnClickListener mSelectorClickListener =
new SelectorWithWidgetPreference.OnClickListener() {
@Override
public void onRadioButtonClicked(SelectorWithWidgetPreference preference) {
onPreferenceChange(preference, true);
}
};
private void launchPrioritySettings() {
// TODO(b/332937635): Update metrics category
new SubSettingLauncher(mContext)
.setDestination(ZenModeBypassingAppsSettings.class.getName())
.setSourceMetricsCategory(SettingsEnums.SETTINGS_ZEN_NOTIFICATIONS)
.launch();
}
}

View File

@@ -39,6 +39,8 @@ public class ZenModeFragment extends ZenModeFragmentBase {
prefControllers.add(new ZenModeHeaderController(context, "header", this, mBackend));
prefControllers.add(new ZenModePeopleLinkPreferenceController(
context, "zen_mode_people", mBackend));
prefControllers.add(new ZenModeAppsLinkPreferenceController(
context, "zen_mode_apps", mBackend));
prefControllers.add(new ZenModeOtherLinkPreferenceController(
context, "zen_other_settings", mBackend));
prefControllers.add(new ZenModeDisplayLinkPreferenceController(

View File

@@ -395,4 +395,19 @@ class ZenModeSummaryHelper {
return mContext.getResources().getString(R.string.zen_mode_people_some);
}
}
/**
* Generates a summary to display under the top level "Apps" preference for a mode.
*/
public String getAppsSummary(ZenMode zenMode) {
// TODO: b/308819928 - Set summary using priority app list if Selected Apps Chosen.
if (zenMode.getPolicy().getAllowedChannels() == ZenPolicy.CHANNEL_POLICY_PRIORITY) {
return mContext.getResources().getString(R.string.zen_mode_apps_priority_apps);
} else if (zenMode.getPolicy().getAllowedChannels() == ZenPolicy.CHANNEL_POLICY_NONE) {
return mContext.getResources().getString(R.string.zen_mode_apps_none_apps);
} else if (zenMode.getPolicy().getAllowedChannels() == ZenMode.CHANNEL_POLICY_ALL) {
return mContext.getResources().getString(R.string.zen_mode_apps_all_apps);
}
return "";
}
}

View File

@@ -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 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)
@EnableFlags(Flags.FLAG_MODES_UI)
public final class ZenModeAppsLinkPreferenceControllerTest {
private ZenModeAppsLinkPreferenceController mController;
private Context mContext;
@Mock
private ZenModesBackend mBackend;
@Rule
public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
mContext = RuntimeEnvironment.application;
mController = new ZenModeAppsLinkPreferenceController(
mContext, "controller_key", 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)
.setZenPolicy(new ZenPolicy.Builder().allowAllSounds().build())
.build(), true);
mController.updateZenMode(pref, zenMode);
verify(pref).setSummary(any());
}
}

View File

@@ -0,0 +1,343 @@
/*
* 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_ALL;
import static android.app.NotificationManager.INTERRUPTION_FILTER_NONE;
import static android.app.NotificationManager.INTERRUPTION_FILTER_PRIORITY;
import static com.android.settings.notification.modes.ZenModeAppsPreferenceController.KEY_ALL;
import static com.android.settings.notification.modes.ZenModeAppsPreferenceController.KEY_NONE;
import static com.android.settings.notification.modes.ZenModeAppsPreferenceController.KEY_PRIORITY;
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.ZenPolicy;
import androidx.preference.PreferenceCategory;
import androidx.preference.PreferenceManager;
import androidx.preference.PreferenceScreen;
import androidx.preference.TwoStatePreference;
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.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
@RunWith(RobolectricTestRunner.class)
@EnableFlags(Flags.FLAG_MODES_UI)
public final class ZenModeAppsPreferenceControllerTest {
private Context mContext;
@Mock
private ZenModesBackend mBackend;
private ZenModeAppsPreferenceController mPriorityController;
private ZenModeAppsPreferenceController mAllController;
private ZenModeAppsPreferenceController mNoneController;
private SelectorWithWidgetPreference mPriorityPref;
private SelectorWithWidgetPreference mAllPref;
private SelectorWithWidgetPreference mNonePref;
private PreferenceCategory mPrefCategory;
private PreferenceScreen mPreferenceScreen;
@Rule
public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
mContext = RuntimeEnvironment.application;
mPriorityController = new ZenModeAppsPreferenceController(mContext, KEY_PRIORITY, mBackend);
mNoneController = new ZenModeAppsPreferenceController(mContext, KEY_NONE, mBackend);
mAllController = new ZenModeAppsPreferenceController(mContext, KEY_ALL, mBackend);
mPriorityPref = makePreference(KEY_PRIORITY, mPriorityController);
mAllPref = makePreference(KEY_ALL, mAllController);
mNonePref = makePreference(KEY_NONE, mNoneController);
mPrefCategory = new PreferenceCategory(mContext);
mPrefCategory.setKey("zen_mode_apps_category");
PreferenceManager preferenceManager = new PreferenceManager(mContext);
mPreferenceScreen = preferenceManager.createPreferenceScreen(mContext);
mPreferenceScreen.addPreference(mPrefCategory);
mPrefCategory.addPreference(mPriorityPref);
mPrefCategory.addPreference(mAllPref);
mPrefCategory.addPreference(mNonePref);
mAllController.displayPreference(mPreferenceScreen);
mPriorityController.displayPreference(mPreferenceScreen);
mNoneController.displayPreference(mPreferenceScreen);
}
private SelectorWithWidgetPreference makePreference(String key,
ZenModeAppsPreferenceController controller) {
final SelectorWithWidgetPreference pref = new SelectorWithWidgetPreference(mContext, false);
pref.setKey(key);
pref.setOnClickListener(controller.mSelectorClickListener);
return pref;
}
@Test
public void testUpdateState_All() {
TwoStatePreference preference = mock(TwoStatePreference.class);
ZenMode zenMode = new ZenMode("id",
new AutomaticZenRule.Builder("Driving", Uri.parse("drive"))
.setType(AutomaticZenRule.TYPE_DRIVING)
.setZenPolicy(new ZenPolicy.Builder()
.allowChannels(ZenMode.CHANNEL_POLICY_ALL)
.build())
.build(), true);
mAllController.updateZenMode(preference, zenMode);
verify(preference).setChecked(true);
}
@Test
public void testUpdateState_All_Unchecked() {
TwoStatePreference preference = mock(TwoStatePreference.class);
ZenMode zenMode = new ZenMode("id",
new AutomaticZenRule.Builder("Driving", Uri.parse("drive"))
.setType(AutomaticZenRule.TYPE_DRIVING)
.setZenPolicy(new ZenPolicy.Builder()
.allowChannels(ZenPolicy.CHANNEL_POLICY_NONE)
.build())
.build(), true);
mAllController.updateZenMode(preference, zenMode);
verify(preference).setChecked(false);
}
@Test
public void testUpdateState_None() {
TwoStatePreference preference = mock(TwoStatePreference.class);
ZenMode zenMode = new ZenMode("id",
new AutomaticZenRule.Builder("Driving", Uri.parse("drive"))
.setType(AutomaticZenRule.TYPE_DRIVING)
.setZenPolicy(new ZenPolicy.Builder()
.allowChannels(ZenPolicy.CHANNEL_POLICY_NONE)
.build())
.build(), true);
mNoneController.updateZenMode(preference, zenMode);
verify(preference).setChecked(true);
}
@Test
public void testUpdateState_None_Unchecked() {
TwoStatePreference preference = mock(TwoStatePreference.class);
ZenMode zenMode = new ZenMode("id",
new AutomaticZenRule.Builder("Driving", Uri.parse("drive"))
.setType(AutomaticZenRule.TYPE_DRIVING)
.setZenPolicy(new ZenPolicy.Builder()
.allowChannels(ZenMode.CHANNEL_POLICY_ALL)
.build())
.build(), true);
mNoneController.updateZenMode(preference, zenMode);
verify(preference).setChecked(false);
}
@Test
public void testUpdateState_Priority() {
TwoStatePreference preference = mock(TwoStatePreference.class);
ZenMode zenMode = new ZenMode("id",
new AutomaticZenRule.Builder("Driving", Uri.parse("drive"))
.setType(AutomaticZenRule.TYPE_DRIVING)
.setZenPolicy(new ZenPolicy.Builder()
.allowChannels(ZenPolicy.CHANNEL_POLICY_PRIORITY)
.build())
.build(), true);
mPriorityController.updateZenMode(preference, zenMode);
verify(preference).setChecked(true);
}
@Test
public void testUpdateState_Priority_Unchecked() {
TwoStatePreference preference = mock(TwoStatePreference.class);
ZenMode zenMode = new ZenMode("id",
new AutomaticZenRule.Builder("Driving", Uri.parse("drive"))
.setType(AutomaticZenRule.TYPE_DRIVING)
.setZenPolicy(new ZenPolicy.Builder()
.allowChannels(ZenPolicy.CHANNEL_POLICY_NONE)
.build())
.build(), true);
mPriorityController.updateZenMode(preference, zenMode);
verify(preference).setChecked(false);
}
@Test
public void testOnPreferenceChange_All() {
TwoStatePreference preference = mock(TwoStatePreference.class);
ZenMode zenMode = new ZenMode("id",
new AutomaticZenRule.Builder("Driving", Uri.parse("drive"))
.setType(AutomaticZenRule.TYPE_DRIVING)
.setInterruptionFilter(INTERRUPTION_FILTER_NONE)
.setZenPolicy(new ZenPolicy.Builder()
.allowChannels(ZenMode.CHANNEL_POLICY_ALL)
.build())
.build(), true);
mAllController.updateZenMode(preference, zenMode);
mAllController.onPreferenceChange(preference, true);
ArgumentCaptor<ZenMode> captor = ArgumentCaptor.forClass(ZenMode.class);
verify(mBackend).updateMode(captor.capture());
assertThat(captor.getValue().getPolicy().getAllowedChannels())
.isEqualTo(ZenMode.CHANNEL_POLICY_ALL);
}
@Test
public void testPreferenceClick_passesCorrectCheckedState_All() {
ZenMode zenMode = new ZenMode("id",
new AutomaticZenRule.Builder("Driving", Uri.parse("drive"))
.setType(AutomaticZenRule.TYPE_DRIVING)
.setZenPolicy(new ZenPolicy.Builder()
.allowChannels(ZenPolicy.CHANNEL_POLICY_NONE)
.build())
.build(), true);
mAllController.updateZenMode(mAllPref, zenMode);
mNoneController.updateZenMode(mNonePref, zenMode);
mPriorityController.updateZenMode(mPriorityPref, zenMode);
// MPME is checked; ALL and PRIORITY are unchecked.
assertThat(((SelectorWithWidgetPreference) mPrefCategory.findPreference(KEY_NONE))
.isChecked());
assertThat(!((SelectorWithWidgetPreference) mPrefCategory.findPreference(KEY_ALL))
.isChecked());
assertThat(!((SelectorWithWidgetPreference) mPrefCategory.findPreference(KEY_PRIORITY))
.isChecked());
mPrefCategory.findPreference(KEY_ALL).performClick();
ArgumentCaptor<ZenMode> captor = ArgumentCaptor.forClass(ZenMode.class);
verify(mBackend).updateMode(captor.capture());
// Checks the policy value for ALL is set.
// The important part is that the interruption filter is propagated to the backend.
assertThat(captor.getValue().getRule().getInterruptionFilter())
.isEqualTo(INTERRUPTION_FILTER_ALL);
// ALL is now checked; others are unchecked.
assertThat(((SelectorWithWidgetPreference) mPrefCategory.findPreference(KEY_ALL))
.isChecked());
assertThat(!((SelectorWithWidgetPreference) mPrefCategory.findPreference(KEY_NONE))
.isChecked());
assertThat(!((SelectorWithWidgetPreference) mPrefCategory.findPreference(KEY_PRIORITY))
.isChecked());
}
@Test
public void testPreferenceClick_passesCorrectCheckedState_None() {
ZenMode zenMode = new ZenMode("id",
new AutomaticZenRule.Builder("Driving", Uri.parse("drive"))
.setType(AutomaticZenRule.TYPE_DRIVING)
.setZenPolicy(new ZenPolicy.Builder()
.allowChannels(ZenPolicy.CHANNEL_POLICY_PRIORITY)
.build())
.build(), true);
mAllController.updateZenMode(mAllPref, zenMode);
mNoneController.updateZenMode(mNonePref, zenMode);
mPriorityController.updateZenMode(mPriorityPref, zenMode);
assertThat(((SelectorWithWidgetPreference) mPrefCategory.findPreference(KEY_ALL))
.isChecked());
assertThat(!((SelectorWithWidgetPreference) mPrefCategory.findPreference(KEY_NONE))
.isChecked());
assertThat(!((SelectorWithWidgetPreference) mPrefCategory.findPreference(KEY_PRIORITY))
.isChecked());
// Click on NONE
mPrefCategory.findPreference(KEY_NONE).performClick();
ArgumentCaptor<ZenMode> captor = ArgumentCaptor.forClass(ZenMode.class);
verify(mBackend).updateMode(captor.capture());
// NONE is not actually propagated to the backend as an interruption filter;
// the filter is set to priority, and sounds and visual effects are disallowed.
// See AbstractZenModePreferenceController.
assertThat(captor.getValue().getRule().getInterruptionFilter())
.isEqualTo(INTERRUPTION_FILTER_PRIORITY);
// NONE is now checked; others are unchecked.
assertThat(((SelectorWithWidgetPreference) mPrefCategory.findPreference(KEY_NONE))
.isChecked());
assertThat(!((SelectorWithWidgetPreference) mPrefCategory.findPreference(KEY_ALL))
.isChecked());
assertThat(!((SelectorWithWidgetPreference) mPrefCategory.findPreference(KEY_PRIORITY))
.isChecked());
}
@Test
public void testPreferenceClick_passesCorrectCheckedState_Priority() {
ZenMode zenMode = new ZenMode("id",
new AutomaticZenRule.Builder("Driving", Uri.parse("drive"))
.setType(AutomaticZenRule.TYPE_DRIVING)
.setZenPolicy(new ZenPolicy.Builder()
.allowChannels(ZenPolicy.CHANNEL_POLICY_NONE)
.build())
.build(), true);
mAllController.updateZenMode(mAllPref, zenMode);
mNoneController.updateZenMode(mNonePref, zenMode);
mPriorityController.updateZenMode(mPriorityPref, zenMode);
assertThat(((SelectorWithWidgetPreference) mPrefCategory.findPreference(KEY_NONE))
.isChecked());
assertThat(!((SelectorWithWidgetPreference) mPrefCategory.findPreference(KEY_ALL))
.isChecked());
assertThat(!((SelectorWithWidgetPreference) mPrefCategory.findPreference(KEY_PRIORITY))
.isChecked());
// Click on PRIORITY
mPrefCategory.findPreference(KEY_PRIORITY).performClick();
ArgumentCaptor<ZenMode> captor = ArgumentCaptor.forClass(ZenMode.class);
verify(mBackend).updateMode(captor.capture());
// Checks the policy value for PRIORITY is propagated to the backend.
assertThat(captor.getValue().getRule().getInterruptionFilter())
.isEqualTo(INTERRUPTION_FILTER_PRIORITY);
// PRIORITY is now checked; others are unchecked.
assertThat(((SelectorWithWidgetPreference) mPrefCategory.findPreference(KEY_PRIORITY))
.isChecked());
assertThat(!((SelectorWithWidgetPreference) mPrefCategory.findPreference(KEY_ALL))
.isChecked());
assertThat(!((SelectorWithWidgetPreference) mPrefCategory.findPreference(KEY_NONE))
.isChecked());
}
}

View File

@@ -22,6 +22,7 @@ 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;
@@ -29,6 +30,7 @@ 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;
import org.junit.runner.RunWith;
@@ -325,4 +327,46 @@ public class ZenModesSummaryHelperTest {
assertThat(mSummaryHelper.getDisplayEffectsSummary(zenMode)).isEqualTo(
"Notifications partially hidden, grayscale, and 2 more");
}
@Test
public void getAppsSummary_all() {
AutomaticZenRule rule = new AutomaticZenRule.Builder("Bedtime", Uri.parse("bed"))
.setType(AutomaticZenRule.TYPE_BEDTIME)
.setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
.setZenPolicy(new ZenPolicy.Builder()
.allowChannels(ZenMode.CHANNEL_POLICY_ALL)
.build())
.build();
ZenMode zenMode = new ZenMode("id", rule, true);
assertThat(mSummaryHelper.getAppsSummary(zenMode)).isEqualTo("All");
}
@Test
public void getAppsSummary_none() {
AutomaticZenRule rule = new AutomaticZenRule.Builder("Bedtime", Uri.parse("bed"))
.setType(AutomaticZenRule.TYPE_BEDTIME)
.setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
.setZenPolicy(new ZenPolicy.Builder()
.allowChannels(ZenPolicy.CHANNEL_POLICY_NONE)
.build())
.build();
ZenMode zenMode = new ZenMode("id", rule, true);
assertThat(mSummaryHelper.getAppsSummary(zenMode)).isEqualTo("None");
}
@Test
public void getAppsSummary_priorityApps() {
AutomaticZenRule rule = new AutomaticZenRule.Builder("Bedtime", Uri.parse("bed"))
.setType(AutomaticZenRule.TYPE_BEDTIME)
.setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
.setZenPolicy(new ZenPolicy.Builder()
.allowChannels(ZenPolicy.CHANNEL_POLICY_PRIORITY)
.build())
.build();
ZenMode zenMode = new ZenMode("id", rule, true);
assertThat(mSummaryHelper.getAppsSummary(zenMode)).isEqualTo("Selected apps");
}
}