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

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

Merge "User toggling of a mode overrides automatic triggering" into main

parents f89d9d09 f42149d2
Loading
Loading
Loading
Loading
+134 −11
Original line number Diff line number Diff line
@@ -25,7 +25,6 @@ import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_LIGHTS;
import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_PEEK;
import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_SCREEN_OFF;
import static android.provider.Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
import static android.service.notification.Condition.STATE_TRUE;
import static android.service.notification.SystemZenRules.PACKAGE_ANDROID;
import static android.service.notification.ZenAdapters.peopleTypeToPrioritySenders;
import static android.service.notification.ZenAdapters.prioritySendersToPeopleType;
@@ -76,8 +75,9 @@ import android.util.PluralsMessageFormatter;
import android.util.Slog;
import android.util.proto.ProtoOutputStream;

import androidx.annotation.VisibleForTesting;

import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.XmlUtils;
import com.android.modules.utils.TypedXmlPullParser;
import com.android.modules.utils.TypedXmlSerializer;
@@ -1074,7 +1074,7 @@ public class ZenModeConfig implements Parcelable {
                        rt.manualRule.type = AutomaticZenRule.TYPE_OTHER;
                        rt.manualRule.condition = new Condition(
                                rt.manualRule.conditionId != null ? rt.manualRule.conditionId
                                        : Uri.EMPTY, "", STATE_TRUE);
                                        : Uri.EMPTY, "", Condition.STATE_TRUE);
                    }
                }
                return rt;
@@ -2540,10 +2540,34 @@ public class ZenModeConfig implements Parcelable {
    }

    public static class ZenRule implements Parcelable {

        /** No manual override. Rule owner can decide its state. */
        public static final int OVERRIDE_NONE = 0;
        /**
         * User has manually activated a mode. This will temporarily overrule the rule owner's
         * decision to deactivate it (see {@link #reconsiderConditionOverride}).
         */
        public static final int OVERRIDE_ACTIVATE = 1;
        /**
         * User has manually deactivated an active mode, or setting ZEN_MODE_OFF (for the few apps
         * still allowed to do that) snoozed the mode. This will temporarily overrule the rule
         * owner's decision to activate it (see {@link #reconsiderConditionOverride}).
         */
        public static final int OVERRIDE_DEACTIVATE = 2;

        @IntDef(prefix = { "OVERRIDE" }, value = {
                OVERRIDE_NONE,
                OVERRIDE_ACTIVATE,
                OVERRIDE_DEACTIVATE
        })
        @Retention(RetentionPolicy.SOURCE)
        public @interface ConditionOverride {}

        @UnsupportedAppUsage
        public boolean enabled;
        @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
        public boolean snoozing;         // user manually disabled this instance
        @Deprecated
        public boolean snoozing; // user manually disabled this instance. Obsolete with MODES_UI
        @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
        public String name;              // required for automatic
        @UnsupportedAppUsage
@@ -2579,6 +2603,15 @@ public class ZenModeConfig implements Parcelable {
        // ZenPolicy, so we store them here, only for the manual rule.
        @FlaggedApi(Flags.FLAG_MODES_UI)
        int legacySuppressedEffects;
        /**
         * Signals a user's action to (temporarily or permanently) activate or deactivate this
         * rule, overruling the condition set by the owner. This value is not stored to disk, as
         * it shouldn't survive reboots or be involved in B&R. It might be reset by certain
         * owner-provided state transitions as well.
         */
        @FlaggedApi(Flags.FLAG_MODES_UI)
        @ConditionOverride
        int conditionOverride = OVERRIDE_NONE;

        public ZenRule() { }

@@ -2620,6 +2653,7 @@ public class ZenModeConfig implements Parcelable {
                if (Flags.modesUi()) {
                    disabledOrigin = source.readInt();
                    legacySuppressedEffects = source.readInt();
                    conditionOverride = source.readInt();
                }
            }
        }
@@ -2698,6 +2732,7 @@ public class ZenModeConfig implements Parcelable {
                if (Flags.modesUi()) {
                    dest.writeInt(disabledOrigin);
                    dest.writeInt(legacySuppressedEffects);
                    dest.writeInt(conditionOverride);
                }
            }
        }
@@ -2708,9 +2743,16 @@ public class ZenModeConfig implements Parcelable {
                    .append("id=").append(id)
                    .append(",state=").append(condition == null ? "STATE_FALSE"
                            : Condition.stateToString(condition.state))
                    .append(",enabled=").append(String.valueOf(enabled).toUpperCase())
                    .append(",snoozing=").append(snoozing)
                    .append(",name=").append(name)
                    .append(",enabled=").append(String.valueOf(enabled).toUpperCase());

            if (Flags.modesUi()) {
                sb.append(",conditionOverride=")
                        .append(conditionOverrideToString(conditionOverride));
            } else {
                sb.append(",snoozing=").append(snoozing);
            }

            sb.append(",name=").append(name)
                    .append(",zenMode=").append(Global.zenModeToString(zenMode))
                    .append(",conditionId=").append(conditionId)
                    .append(",pkg=").append(pkg)
@@ -2753,6 +2795,15 @@ public class ZenModeConfig implements Parcelable {
            return sb.append(']').toString();
        }

        private static String conditionOverrideToString(@ConditionOverride int value) {
            return switch(value) {
                case OVERRIDE_ACTIVATE -> "OVERRIDE_ACTIVATE";
                case OVERRIDE_DEACTIVATE -> "OVERRIDE_DEACTIVATE";
                case OVERRIDE_NONE -> "OVERRIDE_NONE";
                default -> "UNKNOWN";
            };
        }

        /** @hide */
        // TODO: add configuration activity
        public void dumpDebug(ProtoOutputStream proto, long fieldId) {
@@ -2763,7 +2814,11 @@ public class ZenModeConfig implements Parcelable {
            proto.write(ZenRuleProto.CREATION_TIME_MS, creationTime);
            proto.write(ZenRuleProto.ENABLED, enabled);
            proto.write(ZenRuleProto.ENABLER, enabler);
            if (Flags.modesApi() && Flags.modesUi()) {
                proto.write(ZenRuleProto.IS_SNOOZING, conditionOverride == OVERRIDE_DEACTIVATE);
            } else {
                proto.write(ZenRuleProto.IS_SNOOZING, snoozing);
            }
            proto.write(ZenRuleProto.ZEN_MODE, zenMode);
            if (conditionId != null) {
                proto.write(ZenRuleProto.CONDITION_ID, conditionId.toString());
@@ -2816,7 +2871,8 @@ public class ZenModeConfig implements Parcelable {
                if (Flags.modesUi()) {
                    finalEquals = finalEquals
                            && other.disabledOrigin == disabledOrigin
                            && other.legacySuppressedEffects == legacySuppressedEffects;
                            && other.legacySuppressedEffects == legacySuppressedEffects
                            && other.conditionOverride == conditionOverride;
                }
            }

@@ -2832,7 +2888,8 @@ public class ZenModeConfig implements Parcelable {
                            zenDeviceEffects, modified, allowManualInvocation, iconResName,
                            triggerDescription, type, userModifiedFields,
                            zenPolicyUserModifiedFields, zenDeviceEffectsUserModifiedFields,
                            deletionInstant, disabledOrigin, legacySuppressedEffects);
                            deletionInstant, disabledOrigin, legacySuppressedEffects,
                            conditionOverride);
                } else {
                    return Objects.hash(enabled, snoozing, name, zenMode, conditionId, condition,
                            component, configurationActivity, pkg, id, enabler, zenPolicy,
@@ -2858,9 +2915,75 @@ public class ZenModeConfig implements Parcelable {
            }
        }

        // TODO: b/333527800 - Rename to isActive()
        public boolean isAutomaticActive() {
            if (Flags.modesApi() && Flags.modesUi()) {
                if (!enabled || getPkg() == null) {
                    return false;
                } else if (conditionOverride == OVERRIDE_ACTIVATE) {
                    return true;
                } else if (conditionOverride == OVERRIDE_DEACTIVATE) {
                    return false;
                } else {
                    return isTrueOrUnknown();
                }
            } else {
                return enabled && !snoozing && getPkg() != null && isTrueOrUnknown();
            }
        }

        @VisibleForTesting(otherwise = VisibleForTesting.NONE)
        @ConditionOverride
        public int getConditionOverride() {
            if (Flags.modesApi() && Flags.modesUi()) {
                return conditionOverride;
            } else {
                return snoozing ? OVERRIDE_DEACTIVATE : OVERRIDE_NONE;
            }
        }

        public void setConditionOverride(@ConditionOverride int value) {
            if (Flags.modesApi() && Flags.modesUi()) {
                conditionOverride = value;
            } else {
                if (value == OVERRIDE_ACTIVATE) {
                    Slog.wtf(TAG, "Shouldn't set OVERRIDE_ACTIVATE if MODES_UI is off");
                } else if (value == OVERRIDE_DEACTIVATE) {
                    snoozing = true;
                } else if (value == OVERRIDE_NONE) {
                    snoozing = false;
                }
            }
        }

        public void resetConditionOverride() {
            setConditionOverride(OVERRIDE_NONE);
        }

        /**
         * Possibly remove the override, depending on the rule owner's intended state.
         *
         * <p>This allows rule owners to "take over" manually-provided state with their smartness,
         * but only once both agree.
         *
         * <p>For example, a manually activated rule wins over rule owner's opinion that it should
         * be off, until the owner says it should be on, at which point it will turn off (without
         * manual intervention) when the rule owner says it should be off. And symmetrically for
         * manual deactivation (which used to be called "snoozing").
         */
        public void reconsiderConditionOverride() {
            if (Flags.modesApi() && Flags.modesUi()) {
                if (conditionOverride == OVERRIDE_ACTIVATE && isTrueOrUnknown()) {
                    resetConditionOverride();
                } else if (conditionOverride == OVERRIDE_DEACTIVATE && !isTrueOrUnknown()) {
                    resetConditionOverride();
                }
            } else {
                if (snoozing && !isTrueOrUnknown()) {
                    snoozing = false;
                }
            }
        }

        public String getPkg() {
            return !TextUtils.isEmpty(pkg)
+11 −2
Original line number Diff line number Diff line
@@ -454,6 +454,8 @@ public class ZenModeDiff {
     */
    public static class RuleDiff extends BaseDiff {
        public static final String FIELD_ENABLED = "enabled";
        public static final String FIELD_CONDITION_OVERRIDE = "conditionOverride";
        @Deprecated
        public static final String FIELD_SNOOZING = "snoozing";
        public static final String FIELD_NAME = "name";
        public static final String FIELD_ZEN_MODE = "zenMode";
@@ -507,9 +509,16 @@ public class ZenModeDiff {
            if (from.enabled != to.enabled) {
                addField(FIELD_ENABLED, new FieldDiff<>(from.enabled, to.enabled));
            }
            if (Flags.modesApi() && Flags.modesUi()) {
                if (from.conditionOverride != to.conditionOverride) {
                    addField(FIELD_CONDITION_OVERRIDE,
                            new FieldDiff<>(from.conditionOverride, to.conditionOverride));
                }
            } else {
                if (from.snoozing != to.snoozing) {
                    addField(FIELD_SNOOZING, new FieldDiff<>(from.snoozing, to.snoozing));
                }
            }
            if (!Objects.equals(from.name, to.name)) {
                addField(FIELD_NAME, new FieldDiff<>(from.name, to.name));
            }
+0 −1
Original line number Diff line number Diff line
@@ -174,7 +174,6 @@ public class ZenModesBackend {
            mNotificationManager.setZenMode(Settings.Global.ZEN_MODE_OFF, null, TAG,
                    /* fromUser= */ true);
        } else {
            // TODO: b/333527800 - This should (potentially) snooze the rule if it was active.
            mNotificationManager.setAutomaticZenRuleState(mode.getId(),
                    new Condition(mode.getRule().getConditionId(), "", Condition.STATE_FALSE,
                            Condition.SOURCE_USER_ACTION));
+0 −1
Original line number Diff line number Diff line
@@ -123,7 +123,6 @@ public class ZenModesBackendTest {
        zenRule.id = id;
        zenRule.pkg = "package";
        zenRule.enabled = azr.isEnabled();
        zenRule.snoozing = false;
        zenRule.conditionId = azr.getConditionId();
        zenRule.condition = new Condition(azr.getConditionId(), "",
                active ? Condition.STATE_TRUE : Condition.STATE_FALSE,
+1 −17
Original line number Diff line number Diff line
@@ -16,9 +16,6 @@

package com.android.server.notification;

import static android.service.notification.Condition.STATE_TRUE;
import static android.service.notification.ZenModeConfig.ORIGIN_USER_IN_SYSTEMUI;

import android.app.INotificationManager;
import android.app.NotificationManager;
import android.content.ComponentName;
@@ -322,21 +319,8 @@ public class ConditionProviders extends ManagedServices {
                final Condition c = conditions[i];
                final ConditionRecord r = getRecordLocked(c.id, info.component, true /*create*/);
                r.info = info;
                if (android.app.Flags.modesUi()) {
                    // if user turned on the mode, ignore the update unless the app also wants the
                    // mode on. this will update the origin of the mode and let the owner turn it
                    // off when the context ends
                    if (r.condition != null && r.condition.source == ORIGIN_USER_IN_SYSTEMUI) {
                        if (r.condition.state == STATE_TRUE && c.state == STATE_TRUE) {
                            r.condition = c;
                        }
                    } else {
                r.condition = c;
            }
                } else {
                    r.condition = c;
                }
            }
        }
        final int N = conditions.length;
        for (int i = 0; i < N; i++) {
Loading