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

Commit 9cf8822c authored by Matías Hernández's avatar Matías Hernández
Browse files

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

Tweak ZenModesBackend to return the current DND interruption filter if manual DND is on. This will be used to show the corresponding policy (e.g. the policy for INTERRUPTION_FILTER_ALAMS) in this situation. The policy will be read-only in the UI since it cannot be customized.

Also clarify some related documentation.

Bug: 361586248
Test: atest ZenModeHelperTest ZenModeTest ZenModesBackendTest NotificationManagerZenTest
Flag: android.app.modes_ui
Change-Id: Ie09032bbf4ded76d02d6c9fcc47c746030536191
parent ee215211
Loading
Loading
Loading
Loading
+16 −3
Original line number Diff line number Diff line
@@ -49,7 +49,7 @@ public final class AutomaticZenRule implements Parcelable {

    /**
     * Rule is of an unknown type. This is the default value if not provided by the owning app,
     * and the value returned if the true type was added in an API level lower than the calling
     * and the value returned if the true type was added in an API level higher than the calling
     * app's targetSdk.
     */
    @FlaggedApi(Flags.FLAG_MODES_API)
@@ -315,7 +315,8 @@ public final class AutomaticZenRule implements Parcelable {
    }

    /**
     * Gets the zen policy.
     * Gets the {@link ZenPolicy} applied if {@link #getInterruptionFilter()} is
     * {@link NotificationManager#INTERRUPTION_FILTER_PRIORITY}.
     */
    @Nullable
    public ZenPolicy getZenPolicy() {
@@ -345,6 +346,17 @@ public final class AutomaticZenRule implements Parcelable {

    /**
     * Sets the interruption filter that is applied when this rule is active.
     *
     * <ul>
     *     <li>When {@link NotificationManager#INTERRUPTION_FILTER_PRIORITY}, the rule will use
     *     the {@link ZenPolicy} supplied to {@link #setZenPolicy} (or a default one).
     *     <li>When {@link NotificationManager#INTERRUPTION_FILTER_ALARMS} or
     *     {@link NotificationManager#INTERRUPTION_FILTER_NONE}, the rule will use a fixed
     *     {@link ZenPolicy} matching the filter.
     *     <li>When {@link NotificationManager#INTERRUPTION_FILTER_ALL}, the rule will not block
     *     notifications, but can still have {@link ZenDeviceEffects}.
     * </ul>
     *
     * @param interruptionFilter The do not disturb mode to enter when this rule is active.
     */
    public void setInterruptionFilter(@InterruptionFilter int interruptionFilter) {
@@ -374,7 +386,8 @@ public final class AutomaticZenRule implements Parcelable {
    }

    /**
     * Sets the zen policy.
     * Sets the {@link ZenPolicy} applied if {@link #getInterruptionFilter()} is
     * {@link NotificationManager#INTERRUPTION_FILTER_PRIORITY}.
     *
     * <p>When updating an existing rule via {@link NotificationManager#updateAutomaticZenRule},
     * a {@code null} value here means the previous policy is retained.
+1 −1
Original line number Diff line number Diff line
@@ -2939,7 +2939,7 @@ public class ZenModeConfig implements Parcelable {
            }
        }

        // TODO: b/333527800 - Rename to isActive()
        // TODO: b/363193376 - Rename to isActive()
        public boolean isAutomaticActive() {
            if (Flags.modesApi() && Flags.modesUi()) {
                if (!enabled || getPkg() == null) {
+3 −0
Original line number Diff line number Diff line
@@ -1364,6 +1364,9 @@
    <!-- Keywords for setting screen for controlling apps that can schedule alarms [CHAR LIMIT=100] -->
    <string name="keywords_alarms_and_reminders">schedule, alarm, reminder, clock</string>

    <!-- Priority Modes: Name of the "manual" Do Not Disturb mode. [CHAR LIMIT=50] -->
    <string name="zen_mode_do_not_disturb_name">Do Not Disturb</string>

    <!-- Sound: Title for the Do not Disturb option and associated settings page. [CHAR LIMIT=50]-->
    <string name="zen_mode_settings_title">Do Not Disturb</string>

+23 −6
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@

package com.android.settingslib.notification.modes;

import static android.app.NotificationManager.INTERRUPTION_FILTER_PRIORITY;
import static android.service.notification.ZenModeConfig.ORIGIN_UNKNOWN;
import static android.service.notification.ZenModeConfig.ORIGIN_USER_IN_SYSTEMUI;

@@ -24,11 +25,13 @@ import android.app.NotificationManager;
import android.content.ComponentName;
import android.net.Uri;
import android.service.notification.Condition;
import android.service.notification.SystemZenRules;
import android.service.notification.ZenDeviceEffects;
import android.service.notification.ZenModeConfig;
import android.service.notification.ZenPolicy;

import androidx.annotation.DrawableRes;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

import java.util.Random;
@@ -40,13 +43,27 @@ public class TestModeBuilder {
    private ZenModeConfig.ZenRule mConfigZenRule;

    public static final ZenMode EXAMPLE = new TestModeBuilder().build();
    public static final ZenMode MANUAL_DND_ACTIVE = manualDnd(Uri.EMPTY, true);
    public static final ZenMode MANUAL_DND_INACTIVE = manualDnd(Uri.EMPTY, false);

    public static ZenMode manualDnd(Uri conditionId, boolean isActive) {
    public static final ZenMode MANUAL_DND_ACTIVE = manualDnd(Uri.EMPTY,
            INTERRUPTION_FILTER_PRIORITY, true);

    public static final ZenMode MANUAL_DND_INACTIVE = manualDnd(Uri.EMPTY,
            INTERRUPTION_FILTER_PRIORITY, false);

    @NonNull
    public static ZenMode manualDnd(@NotificationManager.InterruptionFilter int filter,
            boolean isActive) {
        return manualDnd(Uri.EMPTY, filter, isActive);
    }

    private static ZenMode manualDnd(Uri conditionId,
            @NotificationManager.InterruptionFilter int filter, boolean isActive) {
        return ZenMode.manualDndMode(
                new AutomaticZenRule.Builder("Do Not Disturb", conditionId)
                        .setInterruptionFilter(NotificationManager.INTERRUPTION_FILTER_PRIORITY)
                        .setInterruptionFilter(filter)
                        .setType(AutomaticZenRule.TYPE_OTHER)
                        .setManualInvocationAllowed(true)
                        .setPackage(SystemZenRules.PACKAGE_ANDROID)
                        .setZenPolicy(new ZenPolicy.Builder().disallowAllSounds().build())
                        .build(),
                isActive);
@@ -58,7 +75,7 @@ public class TestModeBuilder {
        mId = "rule_" + id;
        mRule = new AutomaticZenRule.Builder("Test Rule #" + id, Uri.parse("rule://" + id))
                .setPackage("some_package")
                .setInterruptionFilter(NotificationManager.INTERRUPTION_FILTER_PRIORITY)
                .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
                .setZenPolicy(new ZenPolicy.Builder().disallowAllSounds().build())
                .build();
        mConfigZenRule = new ZenModeConfig.ZenRule();
@@ -134,7 +151,7 @@ public class TestModeBuilder {
            @NotificationManager.InterruptionFilter int interruptionFilter) {
        mRule.setInterruptionFilter(interruptionFilter);
        mConfigZenRule.zenMode = NotificationManager.zenModeFromInterruptionFilter(
                interruptionFilter, NotificationManager.INTERRUPTION_FILTER_PRIORITY);
                interruptionFilter, INTERRUPTION_FILTER_PRIORITY);
        return this;
    }

+71 −8
Original line number Diff line number Diff line
@@ -18,9 +18,10 @@ package com.android.settingslib.notification.modes;

import static android.app.AutomaticZenRule.TYPE_SCHEDULE_CALENDAR;
import static android.app.AutomaticZenRule.TYPE_SCHEDULE_TIME;
import static android.app.NotificationManager.INTERRUPTION_FILTER_ALARMS;
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.service.notification.SystemZenRules.PACKAGE_ANDROID;
import static android.service.notification.SystemZenRules.getTriggerDescriptionForScheduleEvent;
import static android.service.notification.SystemZenRules.getTriggerDescriptionForScheduleTime;
import static android.service.notification.ZenModeConfig.tryParseCountdownConditionId;
@@ -69,7 +70,7 @@ public class ZenMode implements Parcelable {
    static final String TEMP_NEW_MODE_ID = "temp_new_mode";

    // Must match com.android.server.notification.ZenModeHelper#applyCustomPolicy.
    private static final ZenPolicy POLICY_INTERRUPTION_FILTER_ALARMS =
    static final ZenPolicy POLICY_INTERRUPTION_FILTER_ALARMS =
            new ZenPolicy.Builder()
                    .disallowAllSounds()
                    .allowAlarms(true)
@@ -78,7 +79,7 @@ public class ZenMode implements Parcelable {
                    .build();

    // Must match com.android.server.notification.ZenModeHelper#applyCustomPolicy.
    private static final ZenPolicy POLICY_INTERRUPTION_FILTER_NONE =
    static final ZenPolicy POLICY_INTERRUPTION_FILTER_NONE =
            new ZenPolicy.Builder()
                    .disallowAllSounds()
                    .hideAllVisualEffects()
@@ -171,13 +172,9 @@ public class ZenMode implements Parcelable {
    }

    static ZenMode manualDndMode(AutomaticZenRule manualRule, boolean isActive) {
        // Manual rule is owned by the system, so we set it here
        AutomaticZenRule manualRuleWithPkg = new AutomaticZenRule.Builder(manualRule)
                .setPackage(PACKAGE_ANDROID)
                .build();
        return new ZenMode(
                MANUAL_DND_MODE_ID,
                manualRuleWithPkg,
                manualRule,
                Kind.MANUAL_DND,
                isActive ? Status.ENABLED_AND_ACTIVE : Status.ENABLED);
    }
@@ -298,6 +295,23 @@ public class ZenMode implements Parcelable {
        }
    }

    /** Returns the interruption filter of the mode. */
    @NotificationManager.InterruptionFilter
    public int getInterruptionFilter() {
        return mRule.getInterruptionFilter();
    }

    /**
     * Sets the interruption filter of the mode. This is valid for {@link AutomaticZenRule}-backed
     * modes (and not manual DND).
     */
    public void setInterruptionFilter(@NotificationManager.InterruptionFilter int filter) {
        if (isManualDnd() || !canEditPolicy()) {
            throw new IllegalStateException("Cannot update interruption filter for mode " + this);
        }
        mRule.setInterruptionFilter(filter);
    }

    @NonNull
    public ZenPolicy getPolicy() {
        switch (mRule.getInterruptionFilter()) {
@@ -326,6 +340,10 @@ public class ZenMode implements Parcelable {
     */
    @SuppressLint("WrongConstant")
    public void setPolicy(@NonNull ZenPolicy policy) {
        if (!canEditPolicy()) {
            throw new IllegalStateException("Cannot update ZenPolicy for mode " + this);
        }

        ZenPolicy currentPolicy = getPolicy();
        if (currentPolicy.equals(policy)) {
            return;
@@ -342,6 +360,12 @@ public class ZenMode implements Parcelable {
        mRule.setZenPolicy(policy);
    }

    /**
     * Returns the {@link ZenDeviceEffects} of the mode.
     *
     * <p>This is never {@code null}; if the backing AutomaticZenRule doesn't have effects set then
     * a default (empty) effects set is returned.
     */
    @NonNull
    public ZenDeviceEffects getDeviceEffects() {
        return mRule.getDeviceEffects() != null
@@ -349,6 +373,15 @@ public class ZenMode implements Parcelable {
                : new ZenDeviceEffects.Builder().build();
    }

    /** Sets the {@link ZenDeviceEffects} of the mode. */
    public void setDeviceEffects(@NonNull ZenDeviceEffects effects) {
        checkNotNull(effects);
        if (!canEditPolicy()) {
            throw new IllegalStateException("Cannot update device effects for mode " + this);
        }
        mRule.setDeviceEffects(effects);
    }

    public void setCustomModeConditionId(Context context, Uri conditionId) {
        checkState(SystemZenRules.PACKAGE_ANDROID.equals(mRule.getPackageName()),
                "Trying to change condition of non-system-owned rule %s (to %s)",
@@ -391,6 +424,18 @@ public class ZenMode implements Parcelable {
        return !isManualDnd();
    }

    /**
     * Whether the mode has an editable policy. Calling {@link #setPolicy},
     * {@link #setDeviceEffects}, or {@link #setInterruptionFilter} is not valid for modes with a
     * read-only policy.
     */
    public boolean canEditPolicy() {
        // Cannot edit the policy of a temporarily active non-PRIORITY DND mode.
        // Note that it's fine to edit the policy of an *AutomaticZenRule* with non-PRIORITY filter;
        // the filter will we set to PRIORITY if you do.
        return !isManualDndWithSpecialFilter();
    }

    public boolean canBeDeleted() {
        return !isManualDnd();
    }
@@ -399,6 +444,12 @@ public class ZenMode implements Parcelable {
        return mKind == Kind.MANUAL_DND;
    }

    private boolean isManualDndWithSpecialFilter() {
        return isManualDnd()
                && (mRule.getInterruptionFilter() == INTERRUPTION_FILTER_ALARMS
                || mRule.getInterruptionFilter() == INTERRUPTION_FILTER_NONE);
    }

    /**
     * A <em>custom manual</em> mode is a mode created by the user, and not yet assigned an
     * automatic trigger condition (neither time schedule nor a calendar).
@@ -414,6 +465,18 @@ public class ZenMode implements Parcelable {
        return mRule.isEnabled();
    }

    /**
     * Enables or disables the mode.
     *
     * <p>The DND mode cannot be disabled; trying to do so will fail.
     */
    public void setEnabled(boolean enabled) {
        if (isManualDnd()) {
            throw new IllegalStateException("Cannot update enabled for manual DND mode " + this);
        }
        mRule.setEnabled(enabled);
    }

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