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

Commit 7635ab0a authored by Julia Reynolds's avatar Julia Reynolds
Browse files

Differentiate dnd countdowns/countdowns to an alarm

Test: runtest systemui, manual
Change-Id: I63b6c6b4128c7c3551efc64f54ce3e55ab2de54d
Fixes: 36569568
parent 96995bb7
Loading
Loading
Loading
Loading
+53 −13
Original line number Diff line number Diff line
@@ -712,7 +712,8 @@ public class ZenModeConfig implements Parcelable {
            int userHandle, boolean shortVersion) {
        final int num;
        String summary, line1, line2;
        final CharSequence formattedTime = getFormattedTime(context, time, userHandle);
        final CharSequence formattedTime =
                getFormattedTime(context, time, isToday(time), userHandle);
        final Resources res = context.getResources();
        if (minutes < 60) {
            // display as minutes
@@ -738,33 +739,43 @@ public class ZenModeConfig implements Parcelable {
            // display as day/time
            summary = line1 = line2 = res.getString(R.string.zen_mode_until, formattedTime);
        }
        final Uri id = toCountdownConditionId(time);
        final Uri id = toCountdownConditionId(time, false);
        return new Condition(id, summary, line1, line2, 0, Condition.STATE_TRUE,
                Condition.FLAG_RELEVANT_NOW);
    }

    public static Condition toNextAlarmCondition(Context context, long now, long alarm,
    /**
     * Converts countdown to alarm parameters into a condition with user facing summary
     */
    public static Condition toNextAlarmCondition(Context context, long alarm,
            int userHandle) {
        final CharSequence formattedTime = getFormattedTime(context, alarm, userHandle);
        boolean isSameDay = isToday(alarm);
        final CharSequence formattedTime = getFormattedTime(context, alarm, isSameDay, userHandle);
        final Resources res = context.getResources();
        final String line1 = res.getString(R.string.zen_mode_alarm, formattedTime);
        final Uri id = toCountdownConditionId(alarm);
        final String line1 = res.getString(R.string.zen_mode_until, formattedTime);
        final Uri id = toCountdownConditionId(alarm, true);
        return new Condition(id, "", line1, "", 0, Condition.STATE_TRUE,
                Condition.FLAG_RELEVANT_NOW);
    }

    private static CharSequence getFormattedTime(Context context, long time, int userHandle) {
        String skeleton = "EEE " + (DateFormat.is24HourFormat(context, userHandle) ? "Hm" : "hma");
    private static CharSequence getFormattedTime(Context context, long time, boolean isSameDay,
            int userHandle) {
        String skeleton = (!isSameDay ? "EEE " : "")
                + (DateFormat.is24HourFormat(context, userHandle) ? "Hm" : "hma");
        final String pattern = DateFormat.getBestDateTimePattern(Locale.getDefault(), skeleton);
        return DateFormat.format(pattern, time);
    }

    private static boolean isToday(long time) {
        GregorianCalendar now = new GregorianCalendar();
        GregorianCalendar endTime = new GregorianCalendar();
        endTime.setTimeInMillis(time);
        if (now.get(Calendar.YEAR) == endTime.get(Calendar.YEAR)
                && now.get(Calendar.MONTH) == endTime.get(Calendar.MONTH)
                && now.get(Calendar.DATE) == endTime.get(Calendar.DATE)) {
            skeleton = DateFormat.is24HourFormat(context, userHandle) ? "Hm" : "hma";
            return true;
        }
        final String pattern = DateFormat.getBestDateTimePattern(Locale.getDefault(), skeleton);
        return DateFormat.format(pattern, time);
        return false;
    }

    // ==== Built-in system conditions ====
@@ -775,17 +786,24 @@ public class ZenModeConfig implements Parcelable {

    public static final String COUNTDOWN_PATH = "countdown";

    public static Uri toCountdownConditionId(long time) {
    public static final String IS_ALARM_PATH = "alarm";

    /**
     * Converts countdown condition parameters into a condition id.
     */
    public static Uri toCountdownConditionId(long time, boolean alarm) {
        return new Uri.Builder().scheme(Condition.SCHEME)
                .authority(SYSTEM_AUTHORITY)
                .appendPath(COUNTDOWN_PATH)
                .appendPath(Long.toString(time))
                .appendPath(IS_ALARM_PATH)
                .appendPath(Boolean.toString(alarm))
                .build();
    }

    public static long tryParseCountdownConditionId(Uri conditionId) {
        if (!Condition.isValidId(conditionId, SYSTEM_AUTHORITY)) return 0;
        if (conditionId.getPathSegments().size() != 2
        if (conditionId.getPathSegments().size() < 2
                || !COUNTDOWN_PATH.equals(conditionId.getPathSegments().get(0))) return 0;
        try {
            return Long.parseLong(conditionId.getPathSegments().get(1));
@@ -795,10 +813,32 @@ public class ZenModeConfig implements Parcelable {
        }
    }

    /**
     * Returns whether this condition is a countdown condition.
     */
    public static boolean isValidCountdownConditionId(Uri conditionId) {
        return tryParseCountdownConditionId(conditionId) != 0;
    }

    /**
     * Returns whether this condition is a countdown to an alarm.
     */
    public static boolean isValidCountdownToAlarmConditionId(Uri conditionId) {
        if (tryParseCountdownConditionId(conditionId) != 0) {
            if (conditionId.getPathSegments().size() < 4
                    || !IS_ALARM_PATH.equals(conditionId.getPathSegments().get(2))) {
                return false;
            }
            try {
                return Boolean.parseBoolean(conditionId.getPathSegments().get(3));
            } catch (RuntimeException e) {
                Slog.w(TAG, "Error parsing countdown alarm condition: " + conditionId, e);
                return false;
            }
        }
        return false;
    }

    // ==== Built-in system condition: schedule ====

    public static final String SCHEDULE_PATH = "schedule";
+83 −162
Original line number Diff line number Diff line
@@ -41,6 +41,7 @@ import android.util.ArraySet;
import android.util.AttributeSet;
import android.util.Log;
import android.util.MathUtils;
import android.util.Slog;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@@ -53,6 +54,7 @@ import android.widget.RadioButton;
import android.widget.RadioGroup;
import android.widget.TextView;

import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.systemui.Prefs;
@@ -116,8 +118,6 @@ public class ZenModePanel extends FrameLayout {

    private Callback mCallback;
    private ZenModeController mController;
    private boolean mCountdownConditionSupported;
    private boolean mRequestingConditions;
    private Condition mExitCondition;
    private int mBucketIndex = -1;
    private boolean mExpanded;
@@ -126,8 +126,6 @@ public class ZenModePanel extends FrameLayout {
    private int mAttachedZen;
    private boolean mAttached;
    private Condition mSessionExitCondition;
    private Condition[] mConditions;
    private Condition mTimeCondition;
    private boolean mVoiceCapable;

    protected int mZenModeConditionLayoutId;
@@ -155,8 +153,6 @@ public class ZenModePanel extends FrameLayout {

    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
        pw.println("ZenModePanel state:");
        pw.print("  mCountdownConditionSupported="); pw.println(mCountdownConditionSupported);
        pw.print("  mRequestingConditions="); pw.println(mRequestingConditions);
        pw.print("  mAttached="); pw.println(mAttached);
        pw.print("  mHidden="); pw.println(mHidden);
        pw.print("  mExpanded="); pw.println(mExpanded);
@@ -292,7 +288,6 @@ public class ZenModePanel extends FrameLayout {

    private void onAttach() {
        setExpanded(true);
        mAttached = true;
        mAttachedZen = mController.getZen();
        ZenRule manualRule = mController.getManualRule();
        mExitCondition = manualRule != null ? manualRule.condition : null;
@@ -304,23 +299,26 @@ public class ZenModePanel extends FrameLayout {
        mController.addCallback(mZenCallback);
        setSessionExitCondition(copy(mExitCondition));
        updateWidgets();
        setRequestingConditions(!mHidden);
        ensureSelection();
        setAttached(true);
    }

    private void onDetach() {
        if (DEBUG) Log.d(mTag, "onDetach");
        setExpanded(false);
        checkForAttachedZenChange();
        mAttached = false;
        setAttached(false);
        mAttachedZen = -1;
        mSessionZen = -1;
        mController.removeCallback(mZenCallback);
        setSessionExitCondition(null);
        setRequestingConditions(false);
        mTransitionHelper.clear();
    }

    @VisibleForTesting
    void setAttached(boolean attached) {
        mAttached = attached;
    }

    @Override
    public void onVisibilityAggregated(boolean isVisible) {
        super.onVisibilityAggregated(isVisible);
@@ -342,7 +340,6 @@ public class ZenModePanel extends FrameLayout {
        if (mHidden == hidden) return;
        if (DEBUG) Log.d(mTag, "hidden=" + hidden);
        mHidden = hidden;
        setRequestingConditions(mAttached && !mHidden);
        updateWidgets();
    }

@@ -365,29 +362,6 @@ public class ZenModePanel extends FrameLayout {
        fireExpanded();
    }

    /** Start or stop requesting relevant zen mode exit conditions */
    private void setRequestingConditions(final boolean requesting) {
        if (mRequestingConditions == requesting) return;
        if (DEBUG) Log.d(mTag, "setRequestingConditions " + requesting);
        mRequestingConditions = requesting;
        if (mRequestingConditions) {
            mTimeCondition = parseExistingTimeCondition(mContext, mExitCondition);
            if (mTimeCondition != null) {
                mBucketIndex = -1;
            } else {
                mBucketIndex = DEFAULT_BUCKET_INDEX;
                mTimeCondition = ZenModeConfig.toTimeCondition(mContext,
                        MINUTE_BUCKETS[mBucketIndex], ActivityManager.getCurrentUser());
            }
            if (DEBUG) Log.d(mTag, "Initial bucket index: " + mBucketIndex);

            mConditions = null; // reset conditions
            handleUpdateConditions();
        } else {
            hideAllConditions();
        }
    }

    protected void addZenConditions(int count) {
        for (int i = 0; i < count; i++) {
            final View rb = mInflater.inflate(mZenModeButtonLayoutId, mEdit, false);
@@ -401,9 +375,7 @@ public class ZenModePanel extends FrameLayout {

    public void init(ZenModeController controller) {
        mController = controller;
        mCountdownConditionSupported = mController.isCountdownConditionSupported();
        final int countdownDelta = mCountdownConditionSupported ? COUNTDOWN_CONDITION_COUNT : 0;
        final int minConditions = 1 /*forever*/ + countdownDelta;
        final int minConditions = 1 /*forever*/ + COUNTDOWN_CONDITION_COUNT;
        addZenConditions(minConditions);
        mSessionZen = getSelectedZen(-1);
        handleUpdateManualRule(mController.getManualRule());
@@ -426,10 +398,6 @@ public class ZenModePanel extends FrameLayout {
        return isForever(condition) ? null : getConditionId(condition);
    }

    private static boolean sameConditionId(Condition lhs, Condition rhs) {
        return lhs == null ? rhs == null : rhs != null && lhs.id.equals(rhs.id);
    }

    private static Condition copy(Condition condition) {
        return condition == null ? null : condition.copy();
    }
@@ -438,17 +406,24 @@ public class ZenModePanel extends FrameLayout {
        mCallback = callback;
    }

    private void handleUpdateManualRule(ZenRule rule) {
    @VisibleForTesting
    void handleUpdateManualRule(ZenRule rule) {
        final int zen = rule != null ? rule.zenMode : Global.ZEN_MODE_OFF;
        handleUpdateZen(zen);
        final Condition c = rule == null ? null
                : rule.condition != null ? rule.condition
                : createCondition(rule.conditionId);
        handleExitConditionChanged(c);
        handleUpdateConditions(c);
        setExitCondition(c);
    }

    private Condition createCondition(Uri conditionId) {
        if (ZenModeConfig.isValidCountdownConditionId(conditionId)) {
        if (ZenModeConfig.isValidCountdownToAlarmConditionId(conditionId)) {
            long time = ZenModeConfig.tryParseCountdownConditionId(conditionId);
            Condition c = ZenModeConfig.toNextAlarmCondition(
                    mContext, time, ActivityManager.getCurrentUser());
            return c;
        } else if (ZenModeConfig.isValidCountdownConditionId(conditionId)) {
            long time = ZenModeConfig.tryParseCountdownConditionId(conditionId);
            int mins = (int) ((time - System.currentTimeMillis() + DateUtils.MINUTE_IN_MILLIS / 2)
                    / DateUtils.MINUTE_IN_MILLIS);
@@ -466,48 +441,10 @@ public class ZenModePanel extends FrameLayout {
        }
        mZenButtons.setSelectedValue(zen, false /* fromClick */);
        updateWidgets();
        handleUpdateConditions();
        if (mExpanded) {
            final Condition selected = getSelectedCondition();
            if (!Objects.equals(mExitCondition, selected)) {
                select(selected);
            }
        }
    }

    private void handleExitConditionChanged(Condition exitCondition) {
        setExitCondition(exitCondition);
        if (DEBUG) Log.d(mTag, "handleExitConditionChanged " + mExitCondition);
        if (exitCondition == null) return;
        final int N = getVisibleConditions();
        for (int i = 0; i < N; i++) {
            final ConditionTag tag = getConditionTagAt(i);
            if (tag != null && sameConditionId(tag.condition, mExitCondition)) {
                bind(exitCondition, mZenRadioGroupContent.getChildAt(i), i);
                tag.rb.setChecked(true);
                return;
            }
        }
        if (mCountdownConditionSupported && ZenModeConfig.isValidCountdownConditionId(
                exitCondition.id)) {
            bind(exitCondition, mZenRadioGroupContent.getChildAt(COUNTDOWN_CONDITION_INDEX),
                    COUNTDOWN_CONDITION_INDEX);
            getConditionTagAt(COUNTDOWN_CONDITION_INDEX).rb.setChecked(true);
        }
    }

    private Condition getSelectedCondition() {
        final int N = getVisibleConditions();
        for (int i = 0; i < N; i++) {
            final ConditionTag tag = getConditionTagAt(i);
            if (tag != null && tag.rb.isChecked()) {
                return tag.condition;
            }
        }
        return null;
    }

    private int getSelectedZen(int defValue) {
    @VisibleForTesting
    int getSelectedZen(int defValue) {
        final Object zen = mZenButtons.getSelectedValue();
        return zen != null ? (Integer) zen : defValue;
    }
@@ -575,56 +512,66 @@ public class ZenModePanel extends FrameLayout {
        return getResources().getString(warningRes, template);
    }

    private static Condition parseExistingTimeCondition(Context context, Condition condition) {
        if (condition == null) return null;
        final long time = ZenModeConfig.tryParseCountdownConditionId(condition.id);
        if (time == 0) return null;
        final long now = System.currentTimeMillis();
        final long span = time - now;
        if (span <= 0 || span > MAX_BUCKET_MINUTES * MINUTES_MS) return null;
        return ZenModeConfig.toTimeCondition(context,
                time, Math.round(span / (float) MINUTES_MS), ActivityManager.getCurrentUser(),
                false /*shortVersion*/);
    }

    private void handleUpdateConditions() {
    @VisibleForTesting
    void handleUpdateConditions(Condition c) {
        if (mTransitionHelper.isTransitioning()) {
            return;
        }
        final int conditionCount = mConditions == null ? 0 : mConditions.length;
        if (DEBUG) Log.d(mTag, "handleUpdateConditions conditionCount=" + conditionCount);
        // forever
        bind(forever(), mZenRadioGroupContent.getChildAt(FOREVER_CONDITION_INDEX),
                FOREVER_CONDITION_INDEX);
        // countdown
        if (mCountdownConditionSupported && mTimeCondition != null) {
            bind(mTimeCondition, mZenRadioGroupContent.getChildAt(COUNTDOWN_CONDITION_INDEX),
        if (c == null) {
            bindGenericCountdown();
            bindNextAlarm(getTimeUntilNextAlarmCondition());
        } else if (isForever(c)) {
            getConditionTagAt(FOREVER_CONDITION_INDEX).rb.setChecked(true);
            bindGenericCountdown();
            bindNextAlarm(getTimeUntilNextAlarmCondition());
        } else {
            if (isAlarm(c)) {
                bindGenericCountdown();

                bindNextAlarm(c);
                getConditionTagAt(COUNTDOWN_ALARM_CONDITION_INDEX).rb.setChecked(true);
            } else if (isCountdown(c)) {
                bindNextAlarm(getTimeUntilNextAlarmCondition());

                bind(c, mZenRadioGroupContent.getChildAt(COUNTDOWN_CONDITION_INDEX),
                        COUNTDOWN_CONDITION_INDEX);
        }
        // countdown until alarm
        if (mCountdownConditionSupported) {
            Condition nextAlarmCondition = getTimeUntilNextAlarmCondition();
            if (nextAlarmCondition != null) {
                mZenRadioGroup.getChildAt(
                        COUNTDOWN_ALARM_CONDITION_INDEX).setVisibility(View.VISIBLE);
                mZenRadioGroupContent.getChildAt(
                        COUNTDOWN_ALARM_CONDITION_INDEX).setVisibility(View.VISIBLE);
                bind(nextAlarmCondition,
                        mZenRadioGroupContent.getChildAt(COUNTDOWN_ALARM_CONDITION_INDEX),
                        COUNTDOWN_ALARM_CONDITION_INDEX);
                getConditionTagAt(COUNTDOWN_CONDITION_INDEX).rb.setChecked(true);
            } else {
                mZenRadioGroup.getChildAt(COUNTDOWN_ALARM_CONDITION_INDEX).setVisibility(View.GONE);
                mZenRadioGroupContent.getChildAt(
                        COUNTDOWN_ALARM_CONDITION_INDEX).setVisibility(View.GONE);
                Slog.wtf(TAG, "Invalid manual condition: " + c);
            }
        }
        // ensure something is selected
        if (mExpanded) {
            ensureSelection();
        }
        mZenConditions.setVisibility(mSessionZen != Global.ZEN_MODE_OFF ? View.VISIBLE : View.GONE);
    }

    private void bindGenericCountdown() {
        mBucketIndex = DEFAULT_BUCKET_INDEX;
        Condition countdown = ZenModeConfig.toTimeCondition(mContext,
                MINUTE_BUCKETS[mBucketIndex], ActivityManager.getCurrentUser());
        // don't change the hour condition while the user is viewing the panel
        if (!mAttached || getConditionTagAt(COUNTDOWN_CONDITION_INDEX).condition == null) {
            bind(countdown, mZenRadioGroupContent.getChildAt(COUNTDOWN_CONDITION_INDEX),
                    COUNTDOWN_CONDITION_INDEX);
        }
    }

    private void bindNextAlarm(Condition c) {
        View alarmContent = mZenRadioGroupContent.getChildAt(COUNTDOWN_ALARM_CONDITION_INDEX);
        ConditionTag tag = (ConditionTag) alarmContent.getTag();
        // Don't change the alarm condition while the user is viewing the panel
        if (c != null && (!mAttached || tag == null || tag.condition == null)) {
            bind(c, alarmContent, COUNTDOWN_ALARM_CONDITION_INDEX);
        }

        tag = (ConditionTag) alarmContent.getTag();
        boolean showAlarm = tag != null && tag.condition != null;
        mZenRadioGroup.getChildAt(COUNTDOWN_ALARM_CONDITION_INDEX).setVisibility(
                showAlarm ? View.VISIBLE : View.GONE);
        alarmContent.setVisibility(showAlarm ? View.VISIBLE : View.GONE);
    }

    private Condition forever() {
        return new Condition(mForeverId, foreverSummary(mContext), "", "", 0 /*icon*/,
                Condition.STATE_TRUE, 0 /*flags*/);
@@ -637,7 +584,6 @@ public class ZenModePanel extends FrameLayout {
    // Returns a time condition if the next alarm is within the next week.
    private Condition getTimeUntilNextAlarmCondition() {
        GregorianCalendar weekRange = new GregorianCalendar();
        final long now = weekRange.getTimeInMillis();
        setToMidnight(weekRange);
        weekRange.add(Calendar.DATE, 6);
        final long nextAlarmMs = mController.getNextAlarm();
@@ -647,9 +593,8 @@ public class ZenModePanel extends FrameLayout {
            setToMidnight(nextAlarm);

            if (weekRange.compareTo(nextAlarm) >= 0) {
                return ZenModeConfig.toTimeCondition(mContext, nextAlarmMs,
                        Math.round((nextAlarmMs - now) / (float) MINUTES_MS),
                        ActivityManager.getCurrentUser(), true);
                return ZenModeConfig.toNextAlarmCondition(mContext, nextAlarmMs,
                        ActivityManager.getCurrentUser());
            }
        }
        return null;
@@ -662,11 +607,13 @@ public class ZenModePanel extends FrameLayout {
        calendar.set(Calendar.MILLISECOND, 0);
    }

    private ConditionTag getConditionTagAt(int index) {
    @VisibleForTesting
    ConditionTag getConditionTagAt(int index) {
        return (ConditionTag) mZenRadioGroupContent.getChildAt(index).getTag();
    }

    private int getVisibleConditions() {
    @VisibleForTesting
    int getVisibleConditions() {
        int rt = 0;
        final int N = mZenRadioGroupContent.getChildCount();
        for (int i = 0; i < N; i++) {
@@ -682,34 +629,8 @@ public class ZenModePanel extends FrameLayout {
        }
    }

    private void ensureSelection() {
        // are we left without anything selected?  if so, set a default
        final int visibleConditions = getVisibleConditions();
        if (visibleConditions == 0) return;
        for (int i = 0; i < visibleConditions; i++) {
            final ConditionTag tag = getConditionTagAt(i);
            if (tag != null && tag.rb.isChecked()) {
                if (DEBUG) Log.d(mTag, "Not selecting a default, checked=" + tag.condition);
                return;
            }
        }
        final ConditionTag foreverTag = getConditionTagAt(FOREVER_CONDITION_INDEX);
        if (foreverTag == null) return;
        if (DEBUG) Log.d(mTag, "Selecting a default");
        final int favoriteIndex = mPrefs.getMinuteIndex();
        if (mExitCondition != null && mExitCondition.equals(mTimeCondition)) {
            getConditionTagAt(COUNTDOWN_CONDITION_INDEX).rb.setChecked(true);
        } else if (favoriteIndex == -1 || !mCountdownConditionSupported ||
                mAttachedZen != Global.ZEN_MODE_OFF) {
            foreverTag.rb.setChecked(true);
        } else {
            mTimeCondition = ZenModeConfig.toTimeCondition(mContext,
                    MINUTE_BUCKETS[favoriteIndex], ActivityManager.getCurrentUser());
            mBucketIndex = favoriteIndex;
            bind(mTimeCondition, mZenRadioGroupContent.getChildAt(COUNTDOWN_CONDITION_INDEX),
                    COUNTDOWN_CONDITION_INDEX);
            getConditionTagAt(COUNTDOWN_CONDITION_INDEX).rb.setChecked(true);
        }
    private static boolean isAlarm(Condition c) {
        return c != null && ZenModeConfig.isValidCountdownToAlarmConditionId(c.id);
    }

    private static boolean isCountdown(Condition c) {
@@ -877,10 +798,9 @@ public class ZenModePanel extends FrameLayout {
            newCondition = ZenModeConfig.toTimeCondition(mContext,
                    MINUTE_BUCKETS[mBucketIndex], ActivityManager.getCurrentUser());
        }
        mTimeCondition = newCondition;
        bind(mTimeCondition, row, rowId);
        bind(newCondition, row, rowId);
        tag.rb.setChecked(true);
        select(mTimeCondition);
        select(newCondition);
        announceConditionSelection(tag);
    }

@@ -902,7 +822,7 @@ public class ZenModePanel extends FrameLayout {
        setExitCondition(condition);
        if (realConditionId == null) {
            mPrefs.setMinuteIndex(-1);
        } else if (isCountdown(condition) && mBucketIndex != -1) {
        } else if ((isAlarm(condition) || isCountdown(condition)) && mBucketIndex != -1) {
            mPrefs.setMinuteIndex(mBucketIndex);
        }
        setSessionExitCondition(copy(condition));
@@ -951,7 +871,8 @@ public class ZenModePanel extends FrameLayout {
    }

    // used as the view tag on condition rows
    private static class ConditionTag {
    @VisibleForTesting
    static class ConditionTag {
        RadioButton rb;
        View lines;
        TextView line1;
+217 −0

File added.

Preview size limit exceeded, changes collapsed.

+9 −5

File changed.

Preview size limit exceeded, changes collapsed.