Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 89cee0ff authored by Matías Hernández's avatar Matías Hernández Committed by Android (Google) Code Review
Browse files

Merge "Calculate "status" of zen mode" into main

parents d37c63ec 455f0fc5
Loading
Loading
Loading
Loading
+70 −20
Original line number Diff line number Diff line
@@ -44,6 +44,7 @@ import androidx.annotation.Nullable;

import com.android.settingslib.R;

import com.google.common.base.Strings;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;

@@ -59,7 +60,7 @@ public class ZenMode {

    private static final String TAG = "ZenMode";

    public static final String MANUAL_DND_MODE_ID = "manual_dnd";
    static final String MANUAL_DND_MODE_ID = "manual_dnd";

    // Must match com.android.server.notification.ZenModeHelper#applyCustomPolicy.
    private static final ZenPolicy POLICY_INTERRUPTION_FILTER_ALARMS =
@@ -78,24 +79,57 @@ public class ZenMode {
                    .allowPriorityChannels(false)
                    .build();

    public enum Status {
        ENABLED,
        ENABLED_AND_ACTIVE,
        DISABLED_BY_USER,
        DISABLED_BY_OTHER
    }

    private final String mId;
    private AutomaticZenRule mRule;
    private final boolean mIsActive;
    private final AutomaticZenRule mRule;
    private final Status mStatus;
    private final boolean mIsManualDnd;

    public ZenMode(String id, AutomaticZenRule rule, boolean isActive) {
        this(id, rule, isActive, false);
    /**
     * Initializes a {@link ZenMode}, mainly based on the information from the
     * {@link AutomaticZenRule}.
     *
     * <p>Some pieces which are not part of the public API (such as whether the mode is currently
     * active, or the reason it was disabled) are read from the {@link ZenModeConfig.ZenRule} --
     * see {@link #computeStatus}.
     */
    public ZenMode(String id, @NonNull AutomaticZenRule rule,
            @NonNull ZenModeConfig.ZenRule zenRuleExtraData) {
        this(id, rule, computeStatus(zenRuleExtraData), false);
    }

    private ZenMode(String id, AutomaticZenRule rule, boolean isActive, boolean isManualDnd) {
        mId = id;
        mRule = rule;
        mIsActive = isActive;
        mIsManualDnd = isManualDnd;
    private static Status computeStatus(@NonNull ZenModeConfig.ZenRule zenRuleExtraData) {
        if (zenRuleExtraData.enabled) {
            if (zenRuleExtraData.isAutomaticActive()) {
                return Status.ENABLED_AND_ACTIVE;
            } else {
                return Status.ENABLED;
            }
        } else {
            if (zenRuleExtraData.disabledOrigin == ZenModeConfig.UPDATE_ORIGIN_USER) {
                return Status.DISABLED_BY_USER;
            } else {
                return Status.DISABLED_BY_OTHER; // by APP, SYSTEM, UNKNOWN.
            }
        }
    }

    public static ZenMode manualDndMode(AutomaticZenRule manualRule, boolean isActive) {
        return new ZenMode(MANUAL_DND_MODE_ID, manualRule, isActive, true);
        return new ZenMode(MANUAL_DND_MODE_ID, manualRule,
                isActive ? Status.ENABLED_AND_ACTIVE : Status.ENABLED, true);
    }

    private ZenMode(String id, @NonNull AutomaticZenRule rule, Status status, boolean isManualDnd) {
        mId = id;
        mRule = rule;
        mStatus = status;
        mIsManualDnd = isManualDnd;
    }

    @NonNull
@@ -108,6 +142,26 @@ public class ZenMode {
        return mRule;
    }

    @NonNull
    public String getName() {
        return Strings.nullToEmpty(mRule.getName());
    }

    @NonNull
    public Status getStatus() {
        return mStatus;
    }

    @AutomaticZenRule.Type
    public int getType() {
        return mRule.getType();
    }

    @Nullable
    public String getTriggerDescription() {
        return mRule.getTriggerDescription();
    }

    @NonNull
    public ListenableFuture<Drawable> getIcon(@NonNull Context context,
            @NonNull ZenIconLoader iconLoader) {
@@ -225,33 +279,29 @@ public class ZenMode {
    }

    public boolean isActive() {
        return mIsActive;
        return mStatus == Status.ENABLED_AND_ACTIVE;
    }

    public boolean isSystemOwned() {
        return SystemZenRules.PACKAGE_ANDROID.equals(mRule.getPackageName());
    }

    @AutomaticZenRule.Type
    public int getType() {
        return mRule.getType();
    }

    @Override
    public boolean equals(@Nullable Object obj) {
        return obj instanceof ZenMode other
                && mId.equals(other.mId)
                && mRule.equals(other.mRule)
                && mIsActive == other.mIsActive;
                && mStatus.equals(other.mStatus)
                && mIsManualDnd == other.mIsManualDnd;
    }

    @Override
    public int hashCode() {
        return Objects.hash(mId, mRule, mIsActive);
        return Objects.hash(mId, mRule, mStatus, mIsManualDnd);
    }

    @Override
    public String toString() {
        return mId + "(" + (mIsActive ? "active" : "inactive") + ") -> " + mRule;
        return mId + " (" + mStatus + ") -> " + mRule;
    }
}
+18 −23
Original line number Diff line number Diff line
@@ -31,6 +31,7 @@ import com.android.settingslib.R;

import java.time.Duration;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Map;

@@ -64,25 +65,27 @@ public class ZenModesBackend {
    }

    public List<ZenMode> getModes() {
        ArrayList<ZenMode> modes = new ArrayList<>();
        Map<String, AutomaticZenRule> zenRules = mNotificationManager.getAutomaticZenRules();
        ZenModeConfig currentConfig = mNotificationManager.getZenModeConfig();

        ArrayList<ZenMode> modes = new ArrayList<>();
        modes.add(getManualDndMode(currentConfig));

        Map<String, AutomaticZenRule> zenRules = mNotificationManager.getAutomaticZenRules();
        for (Map.Entry<String, AutomaticZenRule> zenRuleEntry : zenRules.entrySet()) {
            String ruleId = zenRuleEntry.getKey();
            modes.add(new ZenMode(ruleId, zenRuleEntry.getValue(),
                    isRuleActive(ruleId, currentConfig)));
            ZenModeConfig.ZenRule extraData = currentConfig.automaticRules.get(ruleId);
            if (extraData != null) {
                modes.add(new ZenMode(ruleId, zenRuleEntry.getValue(), extraData));
            } else {
                Log.w(TAG, "Found AZR " + zenRuleEntry.getValue()
                        + " but no corresponding entry in ZenModeConfig (" + currentConfig
                        + "). Skipping");
            }

        modes.sort((l, r) -> {
            if (l.isManualDnd()) {
                return -1;
            } else if (r.isManualDnd()) {
                return 1;
        }
            return l.getRule().getName().compareTo(r.getRule().getName());
        });

        // Manual DND first, then alphabetically.
        modes.sort(Comparator.comparing(ZenMode::isManualDnd).reversed()
                .thenComparing(ZenMode::getName));

        return modes;
    }
@@ -94,10 +97,11 @@ public class ZenModesBackend {
            return getManualDndMode(currentConfig);
        } else {
            AutomaticZenRule rule = mNotificationManager.getAutomaticZenRule(id);
            if (rule == null) {
            ZenModeConfig.ZenRule extraData = currentConfig.automaticRules.get(id);
            if (rule == null || extraData == null) {
                return null;
            }
            return new ZenMode(id, rule, isRuleActive(id, currentConfig));
            return new ZenMode(id, rule, extraData);
        }
    }

@@ -117,15 +121,6 @@ public class ZenModesBackend {
        return ZenMode.manualDndMode(manualDndRule, config != null && config.isManualActive());
    }

    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();
    }

    public void updateMode(ZenMode mode) {
        if (mode.isManualDnd()) {
            try {
+67 −9
Original line number Diff line number Diff line
@@ -24,6 +24,8 @@ import static com.google.common.truth.Truth.assertThat;

import android.app.AutomaticZenRule;
import android.net.Uri;
import android.service.notification.Condition;
import android.service.notification.ZenModeConfig;
import android.service.notification.ZenPolicy;

import org.junit.Test;
@@ -37,6 +39,7 @@ public class ZenModeTest {

    private static final AutomaticZenRule ZEN_RULE =
            new AutomaticZenRule.Builder("Driving", Uri.parse("drive"))
                    .setPackage("com.some.driving.thing")
                    .setType(AutomaticZenRule.TYPE_DRIVING)
                    .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
                    .setZenPolicy(ZEN_POLICY)
@@ -44,7 +47,7 @@ public class ZenModeTest {

    @Test
    public void testBasicMethods() {
        ZenMode zenMode = new ZenMode("id", ZEN_RULE, true);
        ZenMode zenMode = new ZenMode("id", ZEN_RULE, zenConfigRuleFor(ZEN_RULE, true));

        assertThat(zenMode.getId()).isEqualTo("id");
        assertThat(zenMode.getRule()).isEqualTo(ZEN_RULE);
@@ -59,22 +62,64 @@ public class ZenModeTest {
        assertThat(manualMode.isActive()).isFalse();
    }

    @Test
    public void constructor_enabledRule_statusEnabled() {
        AutomaticZenRule azr = new AutomaticZenRule.Builder(ZEN_RULE).setEnabled(true).build();
        ZenModeConfig.ZenRule configZenRule = zenConfigRuleFor(azr, false);

        ZenMode mode = new ZenMode("id", azr, configZenRule);
        assertThat(mode.getStatus()).isEqualTo(ZenMode.Status.ENABLED);
        assertThat(mode.isActive()).isFalse();
    }

    @Test
    public void constructor_activeRule_statusActive() {
        AutomaticZenRule azr = new AutomaticZenRule.Builder(ZEN_RULE).setEnabled(true).build();
        ZenModeConfig.ZenRule configZenRule = zenConfigRuleFor(azr, true);

        ZenMode mode = new ZenMode("id", azr, configZenRule);
        assertThat(mode.getStatus()).isEqualTo(ZenMode.Status.ENABLED_AND_ACTIVE);
        assertThat(mode.isActive()).isTrue();
    }

    @Test
    public void constructor_disabledRuleByUser_statusDisabledByUser() {
        AutomaticZenRule azr = new AutomaticZenRule.Builder(ZEN_RULE).setEnabled(false).build();
        ZenModeConfig.ZenRule configZenRule = zenConfigRuleFor(azr, false);
        configZenRule.disabledOrigin = ZenModeConfig.UPDATE_ORIGIN_USER;

        ZenMode mode = new ZenMode("id", azr, configZenRule);
        assertThat(mode.getStatus()).isEqualTo(ZenMode.Status.DISABLED_BY_USER);
    }

    @Test
    public void constructor_disabledRuleByOther_statusDisabledByOther() {
        AutomaticZenRule azr = new AutomaticZenRule.Builder(ZEN_RULE).setEnabled(false).build();
        ZenModeConfig.ZenRule configZenRule = zenConfigRuleFor(azr, false);
        configZenRule.disabledOrigin = ZenModeConfig.UPDATE_ORIGIN_APP;

        ZenMode mode = new ZenMode("id", azr, configZenRule);
        assertThat(mode.getStatus()).isEqualTo(ZenMode.Status.DISABLED_BY_OTHER);
    }

    @Test
    public void getPolicy_interruptionFilterPriority_returnsZenPolicy() {
        ZenMode zenMode = new ZenMode("id", new AutomaticZenRule.Builder("Rule", Uri.EMPTY)
        AutomaticZenRule azr = new AutomaticZenRule.Builder("Rule", Uri.EMPTY)
                .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
                .setZenPolicy(ZEN_POLICY)
                .build(), false);
                .build();
        ZenMode zenMode = new ZenMode("id", azr, zenConfigRuleFor(azr, false));

        assertThat(zenMode.getPolicy()).isEqualTo(ZEN_POLICY);
    }

    @Test
    public void getPolicy_interruptionFilterAlarms_returnsPolicyAllowingAlarms() {
        ZenMode zenMode = new ZenMode("id", new AutomaticZenRule.Builder("Rule", Uri.EMPTY)
        AutomaticZenRule azr = new AutomaticZenRule.Builder("Rule", Uri.EMPTY)
                .setInterruptionFilter(INTERRUPTION_FILTER_ALARMS)
                .setZenPolicy(ZEN_POLICY) // should be ignored
                .build(), false);
                .build();
        ZenMode zenMode = new ZenMode("id", azr, zenConfigRuleFor(azr, false));

        assertThat(zenMode.getPolicy()).isEqualTo(
                new ZenPolicy.Builder()
@@ -87,10 +132,11 @@ public class ZenModeTest {

    @Test
    public void getPolicy_interruptionFilterNone_returnsPolicyAllowingNothing() {
        ZenMode zenMode = new ZenMode("id", new AutomaticZenRule.Builder("Rule", Uri.EMPTY)
        AutomaticZenRule azr = new AutomaticZenRule.Builder("Rule", Uri.EMPTY)
                .setInterruptionFilter(INTERRUPTION_FILTER_NONE)
                .setZenPolicy(ZEN_POLICY) // should be ignored
                .build(), false);
                .build();
        ZenMode zenMode = new ZenMode("id", azr, zenConfigRuleFor(azr, false));

        assertThat(zenMode.getPolicy()).isEqualTo(
                new ZenPolicy.Builder()
@@ -102,9 +148,10 @@ public class ZenModeTest {

    @Test
    public void setPolicy_setsInterruptionFilterPriority() {
        ZenMode zenMode = new ZenMode("id", new AutomaticZenRule.Builder("Rule", Uri.EMPTY)
        AutomaticZenRule azr = new AutomaticZenRule.Builder("Rule", Uri.EMPTY)
                .setInterruptionFilter(INTERRUPTION_FILTER_ALARMS)
                .build(), false);
                .build();
        ZenMode zenMode = new ZenMode("id", azr, zenConfigRuleFor(azr, false));

        zenMode.setPolicy(ZEN_POLICY);

@@ -113,4 +160,15 @@ public class ZenModeTest {
        assertThat(zenMode.getPolicy()).isEqualTo(ZEN_POLICY);
        assertThat(zenMode.getRule().getZenPolicy()).isEqualTo(ZEN_POLICY);
    }

    private static ZenModeConfig.ZenRule zenConfigRuleFor(AutomaticZenRule azr, boolean isActive) {
        ZenModeConfig.ZenRule zenRule = new ZenModeConfig.ZenRule();
        zenRule.pkg = azr.getPackageName();
        zenRule.conditionId = azr.getConditionId();
        zenRule.enabled = azr.isEnabled();
        if (isActive) {
            zenRule.condition = new Condition(azr.getConditionId(), "active", Condition.STATE_TRUE);
        }
        return zenRule;
    }
}
+36 −19
Original line number Diff line number Diff line
@@ -22,6 +22,7 @@ import static android.provider.Settings.Global.ZEN_MODE_OFF;
import static android.service.notification.Condition.SOURCE_UNKNOWN;
import static android.service.notification.Condition.STATE_FALSE;
import static android.service.notification.Condition.STATE_TRUE;
import static android.service.notification.ZenAdapters.notificationPolicyToZenPolicy;
import static android.service.notification.ZenPolicy.STATE_ALLOW;

import static com.google.common.truth.Truth.assertThat;
@@ -42,7 +43,6 @@ import android.platform.test.annotations.EnableFlags;
import android.platform.test.flag.junit.SetFlagsRule;
import android.provider.Settings;
import android.service.notification.Condition;
import android.service.notification.ZenAdapters;
import android.service.notification.ZenDeviceEffects;
import android.service.notification.ZenModeConfig;
import android.service.notification.ZenPolicy;
@@ -93,7 +93,7 @@ public class ZenModesBackendTest {
            SetFlagsRule.DefaultInitValueType.DEVICE_DEFAULT);

    // Helper methods to add active/inactive rule state to a config. Returns a copy.
    private ZenModeConfig configWithManualRule(ZenModeConfig base, boolean active) {
    private static ZenModeConfig configWithManualRule(ZenModeConfig base, boolean active) {
        ZenModeConfig out = base.copy();

        if (active) {
@@ -108,22 +108,31 @@ public class ZenModesBackendTest {
        return out;
    }

    private ZenModeConfig configWithRule(ZenModeConfig base, String ruleId, AutomaticZenRule rule,
            boolean active) {
    private static ZenModeConfig configWithRule(ZenModeConfig base, String ruleId,
            AutomaticZenRule rule, boolean active) {
        ZenModeConfig out = base.copy();
        out.automaticRules.put(ruleId, zenConfigRuleForRule(ruleId, rule, active));
        return out;
    }

    private static ZenModeConfig.ZenRule zenConfigRuleForRule(String id, AutomaticZenRule azr,
            boolean active) {
        // 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.id = id;
        zenRule.pkg = "package";
        zenRule.enabled = active;
        zenRule.enabled = azr.isEnabled();
        zenRule.snoozing = false;
        zenRule.condition = new Condition(rule.getConditionId(), "",
        zenRule.conditionId = azr.getConditionId();
        zenRule.condition = new Condition(azr.getConditionId(), "",
                active ? Condition.STATE_TRUE : Condition.STATE_FALSE,
                Condition.SOURCE_USER_ACTION);
        out.automaticRules.put(ruleId, zenRule);
        return zenRule;
    }

        return out;
    private static ZenMode newZenMode(String id, AutomaticZenRule azr, boolean active) {
        return new ZenMode(id, azr, zenConfigRuleForRule(id, azr, active));
    }

    @Before
@@ -151,15 +160,17 @@ public class ZenModesBackendTest {
                Policy.PRIORITY_SENDERS_CONTACTS, Policy.PRIORITY_SENDERS_CONTACTS);
        when(mNm.getAutomaticZenRules()).thenReturn(
                ImmutableMap.of("rule1", ZEN_RULE, "rule2", rule2));

        ZenModeConfig config = new ZenModeConfig();
        config.applyNotificationPolicy(dndPolicy);
        config = configWithRule(config, "rule1", ZEN_RULE, false);
        config = configWithRule(config, "rule2", rule2, false);
        assertThat(config.manualRule.zenPolicy.getPriorityCategoryAlarms()).isEqualTo(STATE_ALLOW);
        when(mNm.getZenModeConfig()).thenReturn(config);

        List<ZenMode> modes = mBackend.getModes();

        // all modes exist, but none of them are currently active
        ZenPolicy zenPolicy = ZenAdapters.notificationPolicyToZenPolicy(dndPolicy);
        assertThat(modes).containsExactly(
                        ZenMode.manualDndMode(
                                new AutomaticZenRule.Builder(
@@ -167,7 +178,7 @@ public class ZenModesBackendTest {
                                        Uri.EMPTY)
                                        .setType(AutomaticZenRule.TYPE_OTHER)
                                        .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
                                        .setZenPolicy(zenPolicy)
                                        .setZenPolicy(notificationPolicyToZenPolicy(dndPolicy))
                                        .setManualInvocationAllowed(true)
                                        .build(),
                                false),
@@ -192,7 +203,7 @@ public class ZenModesBackendTest {
                                mContext.getString(R.string.zen_mode_settings_title), Uri.EMPTY)
                                .setType(AutomaticZenRule.TYPE_OTHER)
                                .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
                                .setZenPolicy(ZenAdapters.notificationPolicyToZenPolicy(dndPolicy))
                                .setZenPolicy(notificationPolicyToZenPolicy(dndPolicy))
                                .setManualInvocationAllowed(true)
                                .build(), false));
    }
@@ -200,6 +211,8 @@ public class ZenModesBackendTest {
    @Test
    public void getMode_zenRule_returnsMode() {
        when(mNm.getAutomaticZenRule(eq(ZEN_RULE_ID))).thenReturn(ZEN_RULE);
        when(mNm.getZenModeConfig()).thenReturn(
                configWithRule(new ZenModeConfig(), ZEN_RULE_ID, ZEN_RULE, false));

        ZenMode mode = mBackend.getMode(ZEN_RULE_ID);

@@ -230,11 +243,13 @@ public class ZenModesBackendTest {
        ZenMode mode = mBackend.getMode(ZenMode.MANUAL_DND_MODE_ID);

        // By default, manual rule is inactive
        assertThat(mode).isNotNull();
        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).isNotNull();
        assertThat(activeMode.isActive()).isTrue();
    }

@@ -252,11 +267,13 @@ public class ZenModesBackendTest {

        // Round 1: the current config should indicate that the rule is not active
        ZenMode mode = mBackend.getMode(ZEN_RULE_ID);
        assertThat(mode).isNotNull();
        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).isNotNull();
        assertThat(activeMode.isActive()).isTrue();
    }