diff --git a/src/com/android/settings/notification/modes/ZenMode.java b/src/com/android/settings/notification/modes/ZenMode.java index b0036788ecd..2e615008ff5 100644 --- a/src/com/android/settings/notification/modes/ZenMode.java +++ b/src/com/android/settings/notification/modes/ZenMode.java @@ -75,20 +75,22 @@ class ZenMode { private final String mId; private final AutomaticZenRule mRule; + private boolean mIsActive; private final boolean mIsManualDnd; - ZenMode(String id, AutomaticZenRule rule) { - this(id, rule, false); + ZenMode(String id, AutomaticZenRule rule, boolean isActive) { + this(id, rule, isActive, false); } - private ZenMode(String id, AutomaticZenRule rule, boolean isManualDnd) { + private ZenMode(String id, AutomaticZenRule rule, boolean isActive, boolean isManualDnd) { mId = id; mRule = rule; + mIsActive = isActive; mIsManualDnd = isManualDnd; } - static ZenMode manualDndMode(AutomaticZenRule dndPolicyAsRule) { - return new ZenMode(MANUAL_DND_MODE_ID, dndPolicyAsRule, true); + static ZenMode manualDndMode(AutomaticZenRule dndPolicyAsRule, boolean isActive) { + return new ZenMode(MANUAL_DND_MODE_ID, dndPolicyAsRule, isActive, true); } @NonNull @@ -190,11 +192,16 @@ class ZenMode { return mIsManualDnd; } + public boolean isActive() { + return mIsActive; + } + @Override public boolean equals(@Nullable Object obj) { return obj instanceof ZenMode other && mId.equals(other.mId) - && mRule.equals(other.mRule); + && mRule.equals(other.mRule) + && mIsActive == other.mIsActive; } @Override @@ -204,6 +211,6 @@ class ZenMode { @Override public String toString() { - return mId + " -> " + mRule; + return mId + "(" + (mIsActive ? "active" : "inactive") + ") -> " + mRule; } } diff --git a/src/com/android/settings/notification/modes/ZenModesBackend.java b/src/com/android/settings/notification/modes/ZenModesBackend.java index 388f13b15f8..306ce5072ac 100644 --- a/src/com/android/settings/notification/modes/ZenModesBackend.java +++ b/src/com/android/settings/notification/modes/ZenModesBackend.java @@ -67,11 +67,14 @@ class ZenModesBackend { List getModes() { ArrayList modes = new ArrayList<>(); - modes.add(getManualDndMode()); + ZenModeConfig currentConfig = mNotificationManager.getZenModeConfig(); + modes.add(getManualDndMode(currentConfig)); Map zenRules = mNotificationManager.getAutomaticZenRules(); for (Map.Entry zenRuleEntry : zenRules.entrySet()) { - modes.add(new ZenMode(zenRuleEntry.getKey(), zenRuleEntry.getValue())); + String ruleId = zenRuleEntry.getKey(); + modes.add(new ZenMode(ruleId, zenRuleEntry.getValue(), + isRuleActive(ruleId, currentConfig))); } // TODO: b/331429435 - Sort modes. @@ -80,15 +83,20 @@ class ZenModesBackend { @Nullable ZenMode getMode(String id) { + ZenModeConfig currentConfig = mNotificationManager.getZenModeConfig(); if (ZenMode.MANUAL_DND_MODE_ID.equals(id)) { - return getManualDndMode(); + // Regardless of its contents, non-null manualRule means that manual rule is active. + return getManualDndMode(currentConfig); } else { AutomaticZenRule rule = mNotificationManager.getAutomaticZenRule(id); - return rule != null ? new ZenMode(id, rule) : null; + if (rule == null) { + return null; + } + return new ZenMode(id, rule, isRuleActive(id, currentConfig)); } } - private ZenMode getManualDndMode() { + private ZenMode getManualDndMode(ZenModeConfig config) { // TODO: b/333530553 - Read ZenDeviceEffects of manual DND. // TODO: b/333682392 - Replace with final strings for name & trigger description AutomaticZenRule manualDndRule = new AutomaticZenRule.Builder( @@ -103,7 +111,17 @@ class ZenModesBackend { .setInterruptionFilter(NotificationManager.INTERRUPTION_FILTER_PRIORITY) .build(); - return ZenMode.manualDndMode(manualDndRule); + return ZenMode.manualDndMode(manualDndRule, + config != null && config.manualRule != null); // isActive + } + + private static boolean isRuleActive(String id, ZenModeConfig config) { + if (config == null) { + // shouldn't happen if the config is coming from NM, but be safe + return false; + } + ZenModeConfig.ZenRule configRule = config.automaticRules.get(id); + return configRule != null && configRule.isAutomaticActive(); } void updateMode(ZenMode mode) { diff --git a/tests/robotests/src/com/android/settings/notification/modes/ZenModeTest.java b/tests/robotests/src/com/android/settings/notification/modes/ZenModeTest.java index 52680ca639c..761f10758c8 100644 --- a/tests/robotests/src/com/android/settings/notification/modes/ZenModeTest.java +++ b/tests/robotests/src/com/android/settings/notification/modes/ZenModeTest.java @@ -45,12 +45,19 @@ public class ZenModeTest { @Test public void testBasicMethods() { - ZenMode zenMode = new ZenMode("id", ZEN_RULE); + ZenMode zenMode = new ZenMode("id", ZEN_RULE, true); assertThat(zenMode.getId()).isEqualTo("id"); assertThat(zenMode.getRule()).isEqualTo(ZEN_RULE); assertThat(zenMode.isManualDnd()).isFalse(); assertThat(zenMode.canBeDeleted()).isTrue(); + assertThat(zenMode.isActive()).isTrue(); + + ZenMode manualMode = ZenMode.manualDndMode(ZEN_RULE, false); + assertThat(manualMode.getId()).isEqualTo(ZenMode.MANUAL_DND_MODE_ID); + assertThat(manualMode.isManualDnd()).isTrue(); + assertThat(manualMode.canBeDeleted()).isFalse(); + assertThat(manualMode.isActive()).isFalse(); } @Test @@ -58,7 +65,7 @@ public class ZenModeTest { ZenMode zenMode = new ZenMode("id", new AutomaticZenRule.Builder("Rule", Uri.EMPTY) .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY) .setZenPolicy(ZEN_POLICY) - .build()); + .build(), false); assertThat(zenMode.getPolicy()).isEqualTo(ZEN_POLICY); } @@ -68,7 +75,7 @@ public class ZenModeTest { ZenMode zenMode = new ZenMode("id", new AutomaticZenRule.Builder("Rule", Uri.EMPTY) .setInterruptionFilter(INTERRUPTION_FILTER_ALL) .setZenPolicy(ZEN_POLICY) // should be ignored - .build()); + .build(), false); assertThat(zenMode.getPolicy()).isEqualTo( new ZenPolicy.Builder().allowAllSounds().showAllVisualEffects().build()); @@ -79,7 +86,7 @@ public class ZenModeTest { ZenMode zenMode = new ZenMode("id", new AutomaticZenRule.Builder("Rule", Uri.EMPTY) .setInterruptionFilter(INTERRUPTION_FILTER_ALARMS) .setZenPolicy(ZEN_POLICY) // should be ignored - .build()); + .build(), false); assertThat(zenMode.getPolicy()).isEqualTo( new ZenPolicy.Builder() @@ -95,7 +102,7 @@ public class ZenModeTest { ZenMode zenMode = new ZenMode("id", new AutomaticZenRule.Builder("Rule", Uri.EMPTY) .setInterruptionFilter(INTERRUPTION_FILTER_NONE) .setZenPolicy(ZEN_POLICY) // should be ignored - .build()); + .build(), false); assertThat(zenMode.getPolicy()).isEqualTo( new ZenPolicy.Builder() diff --git a/tests/robotests/src/com/android/settings/notification/modes/ZenModesBackendTest.java b/tests/robotests/src/com/android/settings/notification/modes/ZenModesBackendTest.java index 06ce80b24d2..a0f6f022fa3 100644 --- a/tests/robotests/src/com/android/settings/notification/modes/ZenModesBackendTest.java +++ b/tests/robotests/src/com/android/settings/notification/modes/ZenModesBackendTest.java @@ -56,6 +56,7 @@ import java.util.List; @RunWith(RobolectricTestRunner.class) public class ZenModesBackendTest { + private static final String ZEN_RULE_ID = "rule"; private static final AutomaticZenRule ZEN_RULE = new AutomaticZenRule.Builder("Driving", Uri.parse("drive")) .setType(AutomaticZenRule.TYPE_DRIVING) @@ -63,13 +64,11 @@ public class ZenModesBackendTest { .setZenPolicy(new ZenPolicy.Builder().allowAllSounds().build()) .build(); - private static final ZenMode MANUAL_DND_MODE = ZenMode.manualDndMode( + private static final AutomaticZenRule MANUAL_DND_RULE = new AutomaticZenRule.Builder("Do Not Disturb", Uri.EMPTY) .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY) .setZenPolicy(new ZenPolicy.Builder().allowAllSounds().build()) - .build()); - - private static final ZenMode ZEN_RULE_MODE = new ZenMode("rule", ZEN_RULE); + .build(); @Mock private NotificationManager mNm; @@ -77,6 +76,35 @@ public class ZenModesBackendTest { private Context mContext; private ZenModesBackend mBackend; + // Helper methods to add active/inactive rule state to a config. Returns a copy. + private ZenModeConfig configWithManualRule(ZenModeConfig base, boolean active) { + ZenModeConfig out = base.copy(); + if (!active) { + out.manualRule = null; + } else { + out.manualRule = new ZenModeConfig.ZenRule(); + } + return out; + } + + private ZenModeConfig configWithRule(ZenModeConfig base, String ruleId, AutomaticZenRule rule, + boolean active) { + ZenModeConfig out = base.copy(); + + // Note that there are many other fields of zenRule, but here we only set the ones + // relevant to determining whether or not it is active. + ZenModeConfig.ZenRule zenRule = new ZenModeConfig.ZenRule(); + zenRule.pkg = "package"; + zenRule.enabled = active; + zenRule.snoozing = false; + zenRule.condition = new Condition(rule.getConditionId(), "", + active ? Condition.STATE_TRUE : Condition.STATE_FALSE, + Condition.SOURCE_USER_ACTION); + out.automaticRules.put(ruleId, zenRule); + + return out; + } + @Before public void setup() { MockitoAnnotations.initMocks(this); @@ -85,6 +113,10 @@ public class ZenModesBackendTest { mContext = RuntimeEnvironment.application; mBackend = new ZenModesBackend(mContext); + + // Default catch-all case with no data. This isn't realistic, but tests below that rely + // on the config to get data on rules active will create those individually. + when(mNm.getZenModeConfig()).thenReturn(new ZenModeConfig()); } @Test @@ -102,6 +134,7 @@ public class ZenModesBackendTest { List modes = mBackend.getModes(); + // all modes exist, but none of them are currently active assertThat(modes).containsExactly( ZenMode.manualDndMode( new AutomaticZenRule.Builder( @@ -112,9 +145,10 @@ public class ZenModesBackendTest { .setTriggerDescription( mContext.getString(R.string.zen_mode_settings_summary)) .setManualInvocationAllowed(true) - .build()), - new ZenMode("rule1", ZEN_RULE), - new ZenMode("rule2", rule2)) + .build(), + false), + new ZenMode("rule1", ZEN_RULE, false), + new ZenMode("rule2", rule2, false)) .inOrder(); } @@ -136,26 +170,70 @@ public class ZenModesBackendTest { .setTriggerDescription( mContext.getString(R.string.zen_mode_settings_summary)) .setManualInvocationAllowed(true) - .build())); + .build(), false)); } @Test public void getMode_zenRule_returnsMode() { - when(mNm.getAutomaticZenRule(eq("rule"))).thenReturn(ZEN_RULE); + when(mNm.getAutomaticZenRule(eq(ZEN_RULE_ID))).thenReturn(ZEN_RULE); - ZenMode mode = mBackend.getMode("rule"); + ZenMode mode = mBackend.getMode(ZEN_RULE_ID); - assertThat(mode).isEqualTo(new ZenMode("rule", ZEN_RULE)); + assertThat(mode).isEqualTo(new ZenMode(ZEN_RULE_ID, ZEN_RULE, false)); } @Test public void getMode_missingRule_returnsNull() { when(mNm.getAutomaticZenRule(any())).thenReturn(null); - ZenMode mode = mBackend.getMode("rule"); + ZenMode mode = mBackend.getMode(ZEN_RULE_ID); assertThat(mode).isNull(); - verify(mNm).getAutomaticZenRule(eq("rule")); + verify(mNm).getAutomaticZenRule(eq(ZEN_RULE_ID)); + } + + @Test + public void getMode_manualDnd_returnsCorrectActiveState() { + // Set up a base config with an active rule to make sure we're looking at the correct info + ZenModeConfig configWithActiveRule = configWithRule(new ZenModeConfig(), ZEN_RULE_ID, + ZEN_RULE, true); + when(mNm.getZenModeConfig()).thenReturn(configWithActiveRule); + + // Equivalent to disallowAllSounds() + Policy dndPolicy = new Policy(0, 0, 0); + when(mNm.getNotificationPolicy()).thenReturn(dndPolicy); + + ZenMode mode = mBackend.getMode(ZenMode.MANUAL_DND_MODE_ID); + + // By default, manual rule is inactive + assertThat(mode.isActive()).isFalse(); + + // Now the returned config will represent the manual rule being active + when(mNm.getZenModeConfig()).thenReturn(configWithManualRule(configWithActiveRule, true)); + ZenMode activeMode = mBackend.getMode(ZenMode.MANUAL_DND_MODE_ID); + assertThat(activeMode.isActive()).isTrue(); + } + + @Test + public void getMode_zenRule_returnsCorrectActiveState() { + // Set up a base config that has an active manual rule and "rule2", to make sure we're + // looking at the correct rule's info. + ZenModeConfig configWithActiveRules = configWithRule( + configWithManualRule(new ZenModeConfig(), true), // active manual rule + "rule2", ZEN_RULE, true); // active rule 2 + + when(mNm.getAutomaticZenRule(eq(ZEN_RULE_ID))).thenReturn(ZEN_RULE); + when(mNm.getZenModeConfig()).thenReturn( + configWithRule(configWithActiveRules, ZEN_RULE_ID, ZEN_RULE, false)); + + // Round 1: the current config should indicate that the rule is not active + ZenMode mode = mBackend.getMode(ZEN_RULE_ID); + assertThat(mode.isActive()).isFalse(); + + when(mNm.getZenModeConfig()).thenReturn( + configWithRule(configWithActiveRules, ZEN_RULE_ID, ZEN_RULE, true)); + ZenMode activeMode = mBackend.getMode(ZEN_RULE_ID); + assertThat(activeMode.isActive()).isTrue(); } @Test @@ -163,7 +241,7 @@ public class ZenModesBackendTest { ZenMode manualDnd = ZenMode.manualDndMode( new AutomaticZenRule.Builder("DND", Uri.EMPTY) .setZenPolicy(new ZenPolicy.Builder().allowAllSounds().build()) - .build()); + .build(), false); mBackend.updateMode(manualDnd); @@ -173,7 +251,7 @@ public class ZenModesBackendTest { @Test public void updateMode_zenRule_updatesRule() { - ZenMode ruleMode = new ZenMode("rule", ZEN_RULE); + ZenMode ruleMode = new ZenMode("rule", ZEN_RULE, false); mBackend.updateMode(ruleMode); @@ -182,7 +260,7 @@ public class ZenModesBackendTest { @Test public void activateMode_manualDnd_setsZenModeImportant() { - mBackend.activateMode(MANUAL_DND_MODE, null); + mBackend.activateMode(ZenMode.manualDndMode(MANUAL_DND_RULE, false), null); verify(mNm).setZenMode(eq(Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS), eq(null), any(), eq(true)); @@ -190,7 +268,8 @@ public class ZenModesBackendTest { @Test public void activateMode_manualDndWithDuration_setsZenModeImportantWithCondition() { - mBackend.activateMode(MANUAL_DND_MODE, Duration.ofMinutes(30)); + mBackend.activateMode(ZenMode.manualDndMode(MANUAL_DND_RULE, false), + Duration.ofMinutes(30)); verify(mNm).setZenMode(eq(Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS), eq(ZenModeConfig.toTimeCondition(mContext, 30, 0, true).id), @@ -200,9 +279,9 @@ public class ZenModesBackendTest { @Test public void activateMode_zenRule_setsRuleStateActive() { - mBackend.activateMode(ZEN_RULE_MODE, null); + mBackend.activateMode(new ZenMode(ZEN_RULE_ID, ZEN_RULE, false), null); - verify(mNm).setAutomaticZenRuleState(eq(ZEN_RULE_MODE.getId()), + verify(mNm).setAutomaticZenRuleState(eq(ZEN_RULE_ID), eq(new Condition(ZEN_RULE.getConditionId(), "", Condition.STATE_TRUE, Condition.SOURCE_USER_ACTION))); } @@ -210,34 +289,36 @@ public class ZenModesBackendTest { @Test public void activateMode_zenRuleWithDuration_fails() { assertThrows(IllegalArgumentException.class, - () -> mBackend.activateMode(ZEN_RULE_MODE, Duration.ofMinutes(30))); + () -> mBackend.activateMode(new ZenMode(ZEN_RULE_ID, ZEN_RULE, false), + Duration.ofMinutes(30))); } @Test public void deactivateMode_manualDnd_setsZenModeOff() { - mBackend.deactivateMode(MANUAL_DND_MODE); + mBackend.deactivateMode(ZenMode.manualDndMode(MANUAL_DND_RULE, true)); verify(mNm).setZenMode(eq(Settings.Global.ZEN_MODE_OFF), eq(null), any(), eq(true)); } @Test public void deactivateMode_zenRule_setsRuleStateInactive() { - mBackend.deactivateMode(ZEN_RULE_MODE); + mBackend.deactivateMode(new ZenMode(ZEN_RULE_ID, ZEN_RULE, false)); - verify(mNm).setAutomaticZenRuleState(eq(ZEN_RULE_MODE.getId()), + verify(mNm).setAutomaticZenRuleState(eq(ZEN_RULE_ID), eq(new Condition(ZEN_RULE.getConditionId(), "", Condition.STATE_FALSE, Condition.SOURCE_USER_ACTION))); } @Test public void removeMode_zenRule_deletesRule() { - mBackend.removeMode(ZEN_RULE_MODE); + mBackend.removeMode(new ZenMode(ZEN_RULE_ID, ZEN_RULE, false)); - verify(mNm).removeAutomaticZenRule(ZEN_RULE_MODE.getId(), true); + verify(mNm).removeAutomaticZenRule(ZEN_RULE_ID, true); } @Test public void removeMode_manualDnd_fails() { - assertThrows(IllegalArgumentException.class, () -> mBackend.removeMode(MANUAL_DND_MODE)); + assertThrows(IllegalArgumentException.class, + () -> mBackend.removeMode(ZenMode.manualDndMode(MANUAL_DND_RULE, false))); } }