Support (non-editable) display of DND with a filter != PRIORITY

Whenever setInterruptionFilter() is called with NONE or ALARMS, the Do Not Disturb mode now shows the current policy (nothing allowed or alarms/media allowed), instead of the normal PRIORITY policy. This policy is read-only in the UI since it cannot be customized.

This should be, or at least become, pretty rare (with small exceptions, apps targeting V that call setInterruptionFilter() will use an implicit mode instead of changing the global zen mode, plus using these filters is quite nonstandard in itself).

Fixes: 361586248
Test: atest & manual (toggling DND via cmd shell)
Flag: android.app.modes_ui
Change-Id: If2439480235d30aa310ad8925341183b9761784c
This commit is contained in:
Matías Hernández
2024-08-30 11:03:45 +02:00
parent 2d07418b8b
commit 50c954923f
19 changed files with 151 additions and 53 deletions

View File

@@ -44,8 +44,8 @@ class InterruptionFilterPreferenceController extends AbstractZenModePreferenceCo
@Override @Override
public void updateState(Preference preference, @NonNull ZenMode zenMode) { public void updateState(Preference preference, @NonNull ZenMode zenMode) {
preference.setEnabled(zenMode.isEnabled()); preference.setEnabled(zenMode.isEnabled() && zenMode.canEditPolicy());
boolean allowingAll = zenMode.getRule().getInterruptionFilter() == INTERRUPTION_FILTER_ALL; boolean allowingAll = zenMode.getInterruptionFilter() == INTERRUPTION_FILTER_ALL;
((TwoStatePreference) preference).setChecked(allowingAll); ((TwoStatePreference) preference).setChecked(allowingAll);
preference.setSummary(allowingAll preference.setSummary(allowingAll
@@ -57,7 +57,7 @@ class InterruptionFilterPreferenceController extends AbstractZenModePreferenceCo
public boolean onPreferenceChange(@NonNull Preference preference, Object newValue) { public boolean onPreferenceChange(@NonNull Preference preference, Object newValue) {
final boolean allowAll = ((Boolean) newValue); final boolean allowAll = ((Boolean) newValue);
return saveMode(zenMode -> { return saveMode(zenMode -> {
zenMode.getRule().setInterruptionFilter(allowAll zenMode.setInterruptionFilter(allowAll
? INTERRUPTION_FILTER_ALL ? INTERRUPTION_FILTER_ALL
: INTERRUPTION_FILTER_PRIORITY); : INTERRUPTION_FILTER_PRIORITY);
return zenMode; return zenMode;

View File

@@ -228,7 +228,7 @@ public class SetupInterstitialActivity extends FragmentActivity {
return false; return false;
} }
modeToUpdate.getRule().setEnabled(true); modeToUpdate.setEnabled(true);
mBackend.updateMode(modeToUpdate); mBackend.updateMode(modeToUpdate);
return true; return true;
} }

View File

@@ -92,7 +92,7 @@ class ZenModeAppsLinkPreferenceController extends AbstractZenModePreferenceContr
@Override @Override
public boolean isAvailable(ZenMode zenMode) { public boolean isAvailable(ZenMode zenMode) {
return zenMode.getRule().getInterruptionFilter() != INTERRUPTION_FILTER_ALL; return zenMode.getInterruptionFilter() != INTERRUPTION_FILTER_ALL;
} }
@Override @Override
@@ -102,7 +102,7 @@ class ZenModeAppsLinkPreferenceController extends AbstractZenModePreferenceContr
preference.setIntent( preference.setIntent(
ZenSubSettingLauncher.forModeFragment(mContext, ZenModeAppsFragment.class, ZenSubSettingLauncher.forModeFragment(mContext, ZenModeAppsFragment.class,
zenMode.getId(), SettingsEnums.ZEN_PRIORITY_MODE).toIntent()); zenMode.getId(), SettingsEnums.ZEN_PRIORITY_MODE).toIntent());
preference.setEnabled(zenMode.isEnabled()); preference.setEnabled(zenMode.isEnabled() && zenMode.canEditPolicy());
mZenMode = zenMode; mZenMode = zenMode;
mPreference = (CircularIconsPreference) preference; mPreference = (CircularIconsPreference) preference;

View File

@@ -74,7 +74,7 @@ class ZenModeDisplayEffectPreferenceController extends AbstractZenModePreference
updatedEffects.setShouldUseNightMode(allow); updatedEffects.setShouldUseNightMode(allow);
break; break;
} }
zenMode.getRule().setDeviceEffects(updatedEffects.build()); zenMode.setDeviceEffects(updatedEffects.build());
return zenMode; return zenMode;
}); });
} }

View File

@@ -45,7 +45,7 @@ class ZenModeDisplayLinkPreferenceController extends AbstractZenModePreferenceCo
preference.setIntent( preference.setIntent(
ZenSubSettingLauncher.forModeFragment(mContext, ZenModeDisplayFragment.class, ZenSubSettingLauncher.forModeFragment(mContext, ZenModeDisplayFragment.class,
zenMode.getId(), SettingsEnums.ZEN_PRIORITY_MODE).toIntent()); zenMode.getId(), SettingsEnums.ZEN_PRIORITY_MODE).toIntent());
preference.setEnabled(zenMode.isEnabled()); preference.setEnabled(zenMode.isEnabled() && zenMode.canEditPolicy());
} }
@Override @Override

View File

@@ -50,7 +50,8 @@ class ZenModeExitAtAlarmPreferenceController extends
if (mSchedule.exitAtAlarm != exitAtAlarm) { if (mSchedule.exitAtAlarm != exitAtAlarm) {
mSchedule.exitAtAlarm = exitAtAlarm; mSchedule.exitAtAlarm = exitAtAlarm;
return saveMode(mode -> { return saveMode(mode -> {
mode.getRule().setConditionId(ZenModeConfig.toScheduleConditionId(mSchedule)); mode.setCustomModeConditionId(mContext,
ZenModeConfig.toScheduleConditionId(mSchedule));
return mode; return mode;
}); });
} }

View File

@@ -171,7 +171,7 @@ public class ZenModeFragment extends ZenModeFragmentBase {
} else if (menuItem.getItemId() == DELETE_MODE) { } else if (menuItem.getItemId() == DELETE_MODE) {
new AlertDialog.Builder(mContext) new AlertDialog.Builder(mContext)
.setTitle(mContext.getString(R.string.zen_mode_delete_mode_confirmation, .setTitle(mContext.getString(R.string.zen_mode_delete_mode_confirmation,
mZenMode.getRule().getName())) mZenMode.getName()))
.setPositiveButton(R.string.zen_mode_schedule_delete, .setPositiveButton(R.string.zen_mode_schedule_delete,
(dialog, which) -> { (dialog, which) -> {
// start finishing before calling removeMode() so that we // start finishing before calling removeMode() so that we

View File

@@ -41,7 +41,7 @@ class ZenModeNotifVisLinkPreferenceController extends AbstractZenModePreferenceC
@Override @Override
public boolean isAvailable(ZenMode zenMode) { public boolean isAvailable(ZenMode zenMode) {
return zenMode.getRule().getInterruptionFilter() != INTERRUPTION_FILTER_ALL; return zenMode.getInterruptionFilter() != INTERRUPTION_FILTER_ALL;
} }
@Override @Override

View File

@@ -61,7 +61,7 @@ class ZenModeOtherLinkPreferenceController extends AbstractZenModePreferenceCont
@Override @Override
public boolean isAvailable(ZenMode zenMode) { public boolean isAvailable(ZenMode zenMode) {
return zenMode.getRule().getInterruptionFilter() != INTERRUPTION_FILTER_ALL; return zenMode.getInterruptionFilter() != INTERRUPTION_FILTER_ALL;
} }
@Override @Override
@@ -70,7 +70,7 @@ class ZenModeOtherLinkPreferenceController extends AbstractZenModePreferenceCont
ZenSubSettingLauncher.forModeFragment(mContext, ZenModeOtherFragment.class, ZenSubSettingLauncher.forModeFragment(mContext, ZenModeOtherFragment.class,
zenMode.getId(), SettingsEnums.ZEN_PRIORITY_MODE).toIntent()); zenMode.getId(), SettingsEnums.ZEN_PRIORITY_MODE).toIntent());
preference.setEnabled(zenMode.isEnabled()); preference.setEnabled(zenMode.isEnabled() && zenMode.canEditPolicy());
preference.setSummary(mSummaryHelper.getOtherSoundCategoriesSummary(zenMode)); preference.setSummary(mSummaryHelper.getOtherSoundCategoriesSummary(zenMode));
((CircularIconsPreference) preference).setIcons(getSoundIcons(zenMode.getPolicy())); ((CircularIconsPreference) preference).setIcons(getSoundIcons(zenMode.getPolicy()));
} }

View File

@@ -84,7 +84,7 @@ class ZenModePeopleLinkPreferenceController extends AbstractZenModePreferenceCon
@Override @Override
public boolean isAvailable(ZenMode zenMode) { public boolean isAvailable(ZenMode zenMode) {
return zenMode.getRule().getInterruptionFilter() != INTERRUPTION_FILTER_ALL; return zenMode.getInterruptionFilter() != INTERRUPTION_FILTER_ALL;
} }
@Override @Override
@@ -94,7 +94,7 @@ class ZenModePeopleLinkPreferenceController extends AbstractZenModePreferenceCon
ZenSubSettingLauncher.forModeFragment(mContext, ZenModePeopleFragment.class, ZenSubSettingLauncher.forModeFragment(mContext, ZenModePeopleFragment.class,
zenMode.getId(), SettingsEnums.ZEN_PRIORITY_MODE).toIntent()); zenMode.getId(), SettingsEnums.ZEN_PRIORITY_MODE).toIntent());
preference.setEnabled(zenMode.isEnabled()); preference.setEnabled(zenMode.isEnabled() && zenMode.canEditPolicy());
preference.setSummary(mSummaryHelper.getPeopleSummary(zenMode.getPolicy())); preference.setSummary(mSummaryHelper.getPeopleSummary(zenMode.getPolicy()));
((CircularIconsPreference) preference).setIcons(getPeopleIcons(zenMode.getPolicy()), ((CircularIconsPreference) preference).setIcons(getPeopleIcons(zenMode.getPolicy()),
PEOPLE_ITEM_EQUIVALENCE); PEOPLE_ITEM_EQUIVALENCE);

View File

@@ -209,48 +209,46 @@ class ZenModeSummaryHelper {
boolean isFirst = true; boolean isFirst = true;
List<String> enabledEffects = new ArrayList<>(); List<String> enabledEffects = new ArrayList<>();
if (!zenMode.getPolicy().shouldShowAllVisualEffects() if (!zenMode.getPolicy().shouldShowAllVisualEffects()
&& zenMode.getRule().getInterruptionFilter() != INTERRUPTION_FILTER_ALL) { && zenMode.getInterruptionFilter() != INTERRUPTION_FILTER_ALL) {
enabledEffects.add(getBlockedEffectsSummary(zenMode)); enabledEffects.add(getBlockedEffectsSummary(zenMode));
isFirst = false; isFirst = false;
} }
ZenDeviceEffects currEffects = zenMode.getRule().getDeviceEffects(); ZenDeviceEffects currEffects = zenMode.getDeviceEffects();
if (currEffects != null) { if (currEffects.shouldDisplayGrayscale()) {
if (currEffects.shouldDisplayGrayscale()) { if (isFirst) {
if (isFirst) { enabledEffects.add(mContext.getString(R.string.mode_grayscale_title));
enabledEffects.add(mContext.getString(R.string.mode_grayscale_title)); } else {
} else { enabledEffects.add(mContext.getString(
enabledEffects.add(mContext.getString( R.string.mode_grayscale_title_secondary_list));
R.string.mode_grayscale_title_secondary_list));
}
isFirst = false;
} }
if (currEffects.shouldSuppressAmbientDisplay()) { isFirst = false;
if (isFirst) { }
enabledEffects.add(mContext.getString(R.string.mode_aod_title)); if (currEffects.shouldSuppressAmbientDisplay()) {
} else { if (isFirst) {
enabledEffects.add(mContext.getString( enabledEffects.add(mContext.getString(R.string.mode_aod_title));
R.string.mode_aod_title_secondary_list)); } else {
} enabledEffects.add(mContext.getString(
isFirst = false; R.string.mode_aod_title_secondary_list));
} }
if (currEffects.shouldDimWallpaper()) { isFirst = false;
if (isFirst) { }
enabledEffects.add(mContext.getString(R.string.mode_wallpaper_title)); if (currEffects.shouldDimWallpaper()) {
} else { if (isFirst) {
enabledEffects.add(mContext.getString( enabledEffects.add(mContext.getString(R.string.mode_wallpaper_title));
R.string.mode_wallpaper_title_secondary_list)); } else {
} enabledEffects.add(mContext.getString(
isFirst = false; R.string.mode_wallpaper_title_secondary_list));
} }
if (currEffects.shouldUseNightMode()) { isFirst = false;
if (isFirst) { }
enabledEffects.add(mContext.getString(R.string.mode_dark_theme_title)); if (currEffects.shouldUseNightMode()) {
} else { if (isFirst) {
enabledEffects.add(mContext.getString( enabledEffects.add(mContext.getString(R.string.mode_dark_theme_title));
R.string.mode_dark_theme_title_secondary_list)); } else {
} enabledEffects.add(mContext.getString(
isFirst = false; R.string.mode_dark_theme_title_secondary_list));
} }
isFirst = false;
} }
int numCategories = enabledEffects.size(); int numCategories = enabledEffects.size();

View File

@@ -87,7 +87,7 @@ class ZenModeTriggerUpdatePreferenceController extends AbstractZenModePreference
mModeName = zenMode.getName(); mModeName = zenMode.getName();
PrimarySwitchPreference triggerPref = (PrimarySwitchPreference) preference; PrimarySwitchPreference triggerPref = (PrimarySwitchPreference) preference;
triggerPref.setChecked(zenMode.getRule().isEnabled()); triggerPref.setChecked(zenMode.isEnabled());
triggerPref.setOnPreferenceChangeListener(mSwitchChangeListener); triggerPref.setOnPreferenceChangeListener(mSwitchChangeListener);
if (zenMode.isSystemOwned()) { if (zenMode.isSystemOwned()) {
setUpForSystemOwnedTrigger(triggerPref, zenMode); setUpForSystemOwnedTrigger(triggerPref, zenMode);
@@ -213,7 +213,7 @@ class ZenModeTriggerUpdatePreferenceController extends AbstractZenModePreference
private void setModeEnabled(boolean enabled) { private void setModeEnabled(boolean enabled) {
saveMode((zenMode) -> { saveMode((zenMode) -> {
if (enabled != zenMode.getRule().isEnabled()) { if (enabled != zenMode.isEnabled()) {
zenMode.getRule().setEnabled(enabled); zenMode.getRule().setEnabled(enabled);
} }
return zenMode; return zenMode;

View File

@@ -118,7 +118,7 @@ class ZenModesListPreferenceController extends BasePreferenceController
for (ZenMode mode : mBackend.getModes()) { for (ZenMode mode : mBackend.getModes()) {
SearchIndexableRaw data = new SearchIndexableRaw(mContext); SearchIndexableRaw data = new SearchIndexableRaw(mContext);
data.key = mode.getId(); data.key = mode.getId();
data.title = mode.getRule().getName(); data.title = mode.getName();
data.screenTitle = res.getString(R.string.zen_modes_list_title); data.screenTitle = res.getString(R.string.zen_modes_list_title);
rawData.add(data); rawData.add(data);
} }

View File

@@ -17,6 +17,7 @@
package com.android.settings.notification.modes; package com.android.settings.notification.modes;
import static android.app.NotificationManager.INTERRUPTION_FILTER_ALL; 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 android.app.NotificationManager.INTERRUPTION_FILTER_PRIORITY;
import static android.service.notification.ZenPolicy.STATE_DISALLOW; import static android.service.notification.ZenPolicy.STATE_DISALLOW;
@@ -67,6 +68,26 @@ public final class InterruptionFilterPreferenceControllerTest {
mController = new InterruptionFilterPreferenceController(mContext, "something", mBackend); mController = new InterruptionFilterPreferenceController(mContext, "something", mBackend);
} }
@Test
public void updateState_dnd_enabled() {
TwoStatePreference preference = mock(TwoStatePreference.class);
ZenMode dnd = TestModeBuilder.MANUAL_DND_ACTIVE;
mController.updateState(preference, dnd);
verify(preference).setEnabled(true);
}
@Test
public void updateState_specialDnd_disabled() {
TwoStatePreference preference = mock(TwoStatePreference.class);
ZenMode specialDnd = TestModeBuilder.manualDnd(INTERRUPTION_FILTER_NONE, true);
mController.updateState(preference, specialDnd);
verify(preference).setEnabled(false);
}
@Test @Test
public void testUpdateState_disabled() { public void testUpdateState_disabled() {
TwoStatePreference preference = mock(TwoStatePreference.class); TwoStatePreference preference = mock(TwoStatePreference.class);

View File

@@ -16,6 +16,7 @@
package com.android.settings.notification.modes; package com.android.settings.notification.modes;
import static android.app.NotificationManager.INTERRUPTION_FILTER_NONE;
import static android.app.NotificationManager.INTERRUPTION_FILTER_PRIORITY; import static android.app.NotificationManager.INTERRUPTION_FILTER_PRIORITY;
import static android.provider.Settings.EXTRA_AUTOMATIC_ZEN_RULE_ID; import static android.provider.Settings.EXTRA_AUTOMATIC_ZEN_RULE_ID;
@@ -149,6 +150,20 @@ public final class ZenModeAppsLinkPreferenceControllerTest {
assertThat(mController.isAvailable()).isTrue(); assertThat(mController.isAvailable()).isTrue();
} }
@Test
public void updateState_dnd_enabled() {
ZenMode dnd = TestModeBuilder.MANUAL_DND_ACTIVE;
mController.updateState(mPreference, dnd);
assertThat(mPreference.isEnabled()).isTrue();
}
@Test
public void updateState_specialDnd_disabled() {
ZenMode specialDnd = TestModeBuilder.manualDnd(INTERRUPTION_FILTER_NONE, true);
mController.updateState(mPreference, specialDnd);
assertThat(mPreference.isEnabled()).isFalse();
}
@Test @Test
public void testUpdateState_disabled() { public void testUpdateState_disabled() {
ZenMode zenMode = new TestModeBuilder() ZenMode zenMode = new TestModeBuilder()

View File

@@ -16,6 +16,8 @@
package com.android.settings.notification.modes; package com.android.settings.notification.modes;
import static android.app.NotificationManager.INTERRUPTION_FILTER_NONE;
import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verify;
@@ -63,6 +65,26 @@ public final class ZenModeDisplayLinkPreferenceControllerTest {
mContext, "something", mBackend, mHelperBackend); mContext, "something", mBackend, mHelperBackend);
} }
@Test
public void updateState_dnd_enabled() {
Preference preference = mock(Preference.class);
ZenMode dnd = TestModeBuilder.MANUAL_DND_ACTIVE;
mController.updateState(preference, dnd);
verify(preference).setEnabled(true);
}
@Test
public void updateState_specialDnd_disabled() {
Preference preference = mock(Preference.class);
ZenMode specialDnd = TestModeBuilder.manualDnd(INTERRUPTION_FILTER_NONE, true);
mController.updateState(preference, specialDnd);
verify(preference).setEnabled(false);
}
@Test @Test
public void testUpdateState_disabled() { public void testUpdateState_disabled() {
Preference preference = mock(Preference.class); Preference preference = mock(Preference.class);

View File

@@ -16,6 +16,8 @@
package com.android.settings.notification.modes; package com.android.settings.notification.modes;
import static android.service.notification.SystemZenRules.PACKAGE_ANDROID;
import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock;
@@ -77,6 +79,7 @@ public class ZenModeExitAtAlarmPreferenceControllerTest {
scheduleInfo.exitAtAlarm = false; scheduleInfo.exitAtAlarm = false;
ZenMode mode = new TestModeBuilder() ZenMode mode = new TestModeBuilder()
.setPackage(PACKAGE_ANDROID)
.setConditionId(ZenModeConfig.toScheduleConditionId(scheduleInfo)) .setConditionId(ZenModeConfig.toScheduleConditionId(scheduleInfo))
.build(); .build();
@@ -105,6 +108,7 @@ public class ZenModeExitAtAlarmPreferenceControllerTest {
scheduleInfo.exitAtAlarm = true; scheduleInfo.exitAtAlarm = true;
ZenMode mode = new TestModeBuilder() ZenMode mode = new TestModeBuilder()
.setPackage(PACKAGE_ANDROID)
.setConditionId(ZenModeConfig.toScheduleConditionId(scheduleInfo)) .setConditionId(ZenModeConfig.toScheduleConditionId(scheduleInfo))
.build(); .build();
mPrefController.updateZenMode(preference, mode); mPrefController.updateZenMode(preference, mode);

View File

@@ -16,6 +16,8 @@
package com.android.settings.notification.modes; package com.android.settings.notification.modes;
import static android.app.NotificationManager.INTERRUPTION_FILTER_NONE;
import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.argThat; import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock;
@@ -61,6 +63,26 @@ public final class ZenModeOtherLinkPreferenceControllerTest {
mContext, "something", mHelperBackend); mContext, "something", mHelperBackend);
} }
@Test
public void updateState_dnd_enabled() {
CircularIconsPreference preference = mock(CircularIconsPreference.class);
ZenMode dnd = TestModeBuilder.MANUAL_DND_ACTIVE;
mController.updateState(preference, dnd);
verify(preference).setEnabled(true);
}
@Test
public void updateState_specialDnd_disabled() {
CircularIconsPreference preference = mock(CircularIconsPreference.class);
ZenMode specialDnd = TestModeBuilder.manualDnd(INTERRUPTION_FILTER_NONE, true);
mController.updateState(preference, specialDnd);
verify(preference).setEnabled(false);
}
@Test @Test
public void updateState_disabled() { public void updateState_disabled() {
CircularIconsPreference pref = mock(CircularIconsPreference.class); CircularIconsPreference pref = mock(CircularIconsPreference.class);

View File

@@ -16,6 +16,7 @@
package com.android.settings.notification.modes; package com.android.settings.notification.modes;
import static android.app.NotificationManager.INTERRUPTION_FILTER_NONE;
import static android.service.notification.ZenPolicy.CONVERSATION_SENDERS_IMPORTANT; import static android.service.notification.ZenPolicy.CONVERSATION_SENDERS_IMPORTANT;
import static android.service.notification.ZenPolicy.PEOPLE_TYPE_ANYONE; import static android.service.notification.ZenPolicy.PEOPLE_TYPE_ANYONE;
import static android.service.notification.ZenPolicy.PEOPLE_TYPE_CONTACTS; import static android.service.notification.ZenPolicy.PEOPLE_TYPE_CONTACTS;
@@ -116,6 +117,20 @@ public final class ZenModePeopleLinkPreferenceControllerTest {
anyBoolean())).thenReturn(new ColorDrawable(Color.BLACK)); anyBoolean())).thenReturn(new ColorDrawable(Color.BLACK));
} }
@Test
public void updateState_dnd_enabled() {
ZenMode dnd = TestModeBuilder.MANUAL_DND_ACTIVE;
mController.updateState(mPreference, dnd);
assertThat(mPreference.isEnabled()).isTrue();
}
@Test
public void updateState_specialDnd_disabled() {
ZenMode specialDnd = TestModeBuilder.manualDnd(INTERRUPTION_FILTER_NONE, true);
mController.updateState(mPreference, specialDnd);
assertThat(mPreference.isEnabled()).isFalse();
}
@Test @Test
public void updateState_disabled() { public void updateState_disabled() {
ZenMode zenMode = new TestModeBuilder() ZenMode zenMode = new TestModeBuilder()