public static final int[] MINUTE_BUCKETS = generateMinuteBuckets();
private static final int SECONDS_MS = 1000;
private static final int MINUTES_MS = 60 * SECONDS_MS;
+ private static final int DAY_MINUTES = 24 * 60;
private static final int ZERO_VALUE_MS = 10 * SECONDS_MS;
private static final boolean DEFAULT_ALLOW_CALLS = true;
return tryParseLong(val, defValue);
}
- public ArraySet<String> getAutomaticRuleNames() {
- final ArraySet<String> rt = new ArraySet<String>();
- for (int i = 0; i < automaticRules.size(); i++) {
- rt.add(automaticRules.valueAt(i).name);
- }
- return rt;
- }
-
@Override
public int describeContents() {
return 0;
boolean shortVersion) {
final long now = System.currentTimeMillis();
final long millis = minutesFromNow == 0 ? ZERO_VALUE_MS : minutesFromNow * MINUTES_MS;
- return toTimeCondition(context, now + millis, minutesFromNow, now, userHandle,
- shortVersion);
+ return toTimeCondition(context, now + millis, minutesFromNow, userHandle, shortVersion);
}
- public static Condition toTimeCondition(Context context, long time, int minutes, long now,
+ public static Condition toTimeCondition(Context context, long time, int minutes,
int userHandle, boolean shortVersion) {
- final int num, summaryResId, line1ResId;
+ final int num;
+ String summary, line1, line2;
+ final CharSequence formattedTime = getFormattedTime(context, time, minutes, userHandle);
+ final Resources res = context.getResources();
if (minutes < 60) {
// display as minutes
num = minutes;
- summaryResId = shortVersion ? R.plurals.zen_mode_duration_minutes_summary_short
+ int summaryResId = shortVersion ? R.plurals.zen_mode_duration_minutes_summary_short
: R.plurals.zen_mode_duration_minutes_summary;
- line1ResId = shortVersion ? R.plurals.zen_mode_duration_minutes_short
+ summary = res.getQuantityString(summaryResId, num, num, formattedTime);
+ int line1ResId = shortVersion ? R.plurals.zen_mode_duration_minutes_short
: R.plurals.zen_mode_duration_minutes;
- } else {
+ line1 = res.getQuantityString(line1ResId, num, num, formattedTime);
+ line2 = res.getString(R.string.zen_mode_until, formattedTime);
+ } else if (minutes < DAY_MINUTES) {
// display as hours
num = Math.round(minutes / 60f);
- summaryResId = shortVersion ? R.plurals.zen_mode_duration_hours_summary_short
+ int summaryResId = shortVersion ? R.plurals.zen_mode_duration_hours_summary_short
: R.plurals.zen_mode_duration_hours_summary;
- line1ResId = shortVersion ? R.plurals.zen_mode_duration_hours_short
+ summary = res.getQuantityString(summaryResId, num, num, formattedTime);
+ int line1ResId = shortVersion ? R.plurals.zen_mode_duration_hours_short
: R.plurals.zen_mode_duration_hours;
+ line1 = res.getQuantityString(line1ResId, num, num, formattedTime);
+ line2 = res.getString(R.string.zen_mode_until, formattedTime);
+ } else {
+ // display as day/time
+ summary = line1 = line2 = res.getString(R.string.zen_mode_until, formattedTime);
}
- final String skeleton = DateFormat.is24HourFormat(context, userHandle) ? "Hm" : "hma";
- final String pattern = DateFormat.getBestDateTimePattern(Locale.getDefault(), skeleton);
- final CharSequence formattedTime = DateFormat.format(pattern, time);
- final Resources res = context.getResources();
- final String summary = res.getQuantityString(summaryResId, num, num, formattedTime);
- final String line1 = res.getQuantityString(line1ResId, num, num, formattedTime);
- final String line2 = res.getString(R.string.zen_mode_until, formattedTime);
final Uri id = toCountdownConditionId(time);
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,
+ int userHandle) {
+ int minutes = Math.round((alarm-now) / (float) MINUTES_MS);
+ final CharSequence formattedTime = getFormattedTime(context, alarm, minutes, userHandle);
+ final Resources res = context.getResources();
+ final String line1 = res.getString(R.string.zen_mode_alarm, formattedTime);
+ final Uri id = toCountdownConditionId(alarm);
+ return new Condition(id, "", line1, "", 0, Condition.STATE_TRUE,
+ Condition.FLAG_RELEVANT_NOW);
+ }
+
+ private static CharSequence getFormattedTime(Context context, long time, int minutes,
+ int userHandle) {
+ String skeleton = DateFormat.is24HourFormat(context, userHandle) ? "Hm" : "hma";
+ if (minutes > DAY_MINUTES) {
+ skeleton = "EEE " + (DateFormat.is24HourFormat(context, userHandle) ? "Hm" : "hma");
+ }
+ final String pattern = DateFormat.getBestDateTimePattern(Locale.getDefault(), skeleton);
+ return DateFormat.format(pattern, time);
+ }
+
// ==== Built-in system conditions ====
public static final String SYSTEM_AUTHORITY = "android";
return UUID.randomUUID().toString().replace("-", "");
}
- public static String getConditionLine1(Context context, ZenModeConfig config,
- int userHandle, boolean shortVersion) {
- return getConditionLine(context, config, userHandle, true /*useLine1*/, shortVersion);
- }
-
public static String getConditionSummary(Context context, ZenModeConfig config,
int userHandle, boolean shortVersion) {
return getConditionLine(context, config, userHandle, false /*useLine1*/, shortVersion);
if (time > 0) {
final long now = System.currentTimeMillis();
final long span = time - now;
- c = toTimeCondition(context,
- time, Math.round(span / (float) MINUTES_MS), now, userHandle, shortVersion);
+ c = toTimeCondition(context, time, Math.round(span / (float) MINUTES_MS),
+ userHandle, shortVersion);
}
final String rt = c == null ? "" : useLine1 ? c.line1 : c.summary;
return TextUtils.isEmpty(rt) ? "" : rt;
-/*
+/**
* Copyright (C) 2014 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.Arrays;
+import java.util.Calendar;
+import java.util.GregorianCalendar;
import java.util.Locale;
import java.util.Objects;
private static final int DEFAULT_BUCKET_INDEX = Arrays.binarySearch(MINUTE_BUCKETS, 60);
private static final int FOREVER_CONDITION_INDEX = 0;
private static final int COUNTDOWN_CONDITION_INDEX = 1;
+ private static final int COUNTDOWN_ALARM_CONDITION_INDEX = 2;
public static final Intent ZEN_SETTINGS
= new Intent(Settings.ACTION_ZEN_MODE_SETTINGS);
private Callback mCallback;
private ZenModeController mController;
private boolean mCountdownConditionSupported;
- private int mMaxConditions;
- private int mMaxOptionalConditions;
- private int mFirstConditionIndex;
private boolean mRequestingConditions;
private Condition mExitCondition;
private int mBucketIndex = -1;
private Condition mSessionExitCondition;
private Condition[] mConditions;
private Condition mTimeCondition;
+ private Condition mTimeUntilAlarmCondition;
private boolean mVoiceCapable;
public ZenModePanel(Context context, AttributeSet attrs) {
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
pw.println("ZenModePanel state:");
pw.print(" mCountdownConditionSupported="); pw.println(mCountdownConditionSupported);
- pw.print(" mMaxConditions="); pw.println(mMaxConditions);
pw.print(" mRequestingConditions="); pw.println(mRequestingConditions);
pw.print(" mAttached="); pw.println(mAttached);
pw.print(" mHidden="); pw.println(mHidden);
if (mRequestingConditions == requesting) return;
if (DEBUG) Log.d(mTag, "setRequestingConditions " + requesting);
mRequestingConditions = requesting;
- if (mController != null) {
- AsyncTask.execute(new Runnable() {
- @Override
- public void run() {
- mController.requestConditions(requesting);
- }
- });
- }
if (mRequestingConditions) {
mTimeCondition = parseExistingTimeCondition(mContext, mExitCondition);
if (mTimeCondition != null) {
MINUTE_BUCKETS[mBucketIndex], ActivityManager.getCurrentUser());
}
if (DEBUG) Log.d(mTag, "Initial bucket index: " + mBucketIndex);
+
+ mTimeUntilAlarmCondition = parseExistingTimeCondition(mContext, mExitCondition);
+ if (mTimeUntilAlarmCondition == null) {
+ mTimeUntilAlarmCondition = getTimeUntilNextAlarmCondition();
+ }
mConditions = null; // reset conditions
handleUpdateConditions();
} else {
public void init(ZenModeController controller) {
mController = controller;
mCountdownConditionSupported = mController.isCountdownConditionSupported();
- final int countdownDelta = mCountdownConditionSupported ? 1 : 0;
- mFirstConditionIndex = COUNTDOWN_CONDITION_INDEX + countdownDelta;
+ final int countdownDelta = mCountdownConditionSupported ? 2 : 0;
final int minConditions = 1 /*forever*/ + countdownDelta;
- mMaxConditions = MathUtils.constrain(mContext.getResources()
- .getInteger(R.integer.zen_mode_max_conditions), minConditions, 100);
- mMaxOptionalConditions = mMaxConditions - minConditions;
- for (int i = 0; i < mMaxConditions; i++) {
+ for (int i = 0; i < minConditions; i++) {
mZenConditions.addView(mInflater.inflate(R.layout.zen_mode_condition, this, false));
}
mSessionZen = getSelectedZen(-1);
return condition == null ? null : condition.copy();
}
- public static String getExitConditionText(Context context, Condition exitCondition) {
- if (exitCondition == null) {
- return foreverSummary(context);
- } else if (isCountdown(exitCondition)) {
- final Condition condition = parseExistingTimeCondition(context, exitCondition);
- return condition != null ? condition.summary : foreverSummary(context);
- } else {
- return exitCondition.summary;
- }
- }
-
public void setCallback(Callback callback) {
mCallback = callback;
}
- public void showSilentHint() {
- if (DEBUG) Log.d(mTag, "showSilentHint");
- if (mZenButtons == null || mZenButtons.getChildCount() == 0) return;
- final View noneButton = mZenButtons.getChildAt(0);
- mIconPulser.start(noneButton);
- }
-
private void handleUpdateManualRule(ZenRule rule) {
final int zen = rule != null ? rule.zenMode : Global.ZEN_MODE_OFF;
handleUpdateZen(zen);
final ConditionTag tag = getConditionTagAt(i);
if (tag != null) {
if (sameConditionId(tag.condition, mExitCondition)) {
- bind(exitCondition, mZenConditions.getChildAt(i));
+ bind(exitCondition, mZenConditions.getChildAt(i), i);
}
}
}
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), now, ActivityManager.getCurrentUser(),
+ time, Math.round(span / (float) MINUTES_MS), ActivityManager.getCurrentUser(),
false /*shortVersion*/);
}
- private void handleUpdateConditions(Condition[] conditions) {
- conditions = trimConditions(conditions);
- if (Arrays.equals(conditions, mConditions)) {
- final int count = mConditions == null ? 0 : mConditions.length;
- if (DEBUG) Log.d(mTag, "handleUpdateConditions unchanged conditionCount=" + count);
- return;
- }
- mConditions = conditions;
- handleUpdateConditions();
- }
-
- private Condition[] trimConditions(Condition[] conditions) {
- if (conditions == null || conditions.length <= mMaxOptionalConditions) {
- // no need to trim
- return conditions;
- }
- // look for current exit condition, ensure it is included if found
- int found = -1;
- for (int i = 0; i < conditions.length; i++) {
- final Condition c = conditions[i];
- if (mSessionExitCondition != null && sameConditionId(mSessionExitCondition, c)) {
- found = i;
- break;
- }
- }
- final Condition[] rt = Arrays.copyOf(conditions, mMaxOptionalConditions);
- if (found >= mMaxOptionalConditions) {
- // found after the first N, promote to the end of the first N
- rt[mMaxOptionalConditions - 1] = conditions[found];
- }
- return rt;
- }
-
private void handleUpdateConditions() {
if (mTransitionHelper.isTransitioning()) {
- mTransitionHelper.pendingUpdateConditions();
return;
}
final int conditionCount = mConditions == null ? 0 : mConditions.length;
if (DEBUG) Log.d(mTag, "handleUpdateConditions conditionCount=" + conditionCount);
// forever
- bind(forever(), mZenConditions.getChildAt(FOREVER_CONDITION_INDEX));
+ bind(forever(), mZenConditions.getChildAt(FOREVER_CONDITION_INDEX),
+ FOREVER_CONDITION_INDEX);
// countdown
if (mCountdownConditionSupported && mTimeCondition != null) {
- bind(mTimeCondition, mZenConditions.getChildAt(COUNTDOWN_CONDITION_INDEX));
+ bind(mTimeCondition, mZenConditions.getChildAt(COUNTDOWN_CONDITION_INDEX),
+ COUNTDOWN_CONDITION_INDEX);
}
- // provider conditions
- for (int i = 0; i < conditionCount; i++) {
- bind(mConditions[i], mZenConditions.getChildAt(mFirstConditionIndex + i));
- }
- // hide the rest
- for (int i = mZenConditions.getChildCount() - 1; i > mFirstConditionIndex + conditionCount;
- i--) {
- mZenConditions.getChildAt(i).setVisibility(GONE);
+ // countdown until alarm
+ if (mCountdownConditionSupported && mTimeUntilAlarmCondition != null) {
+ bind(mTimeUntilAlarmCondition,
+ mZenConditions.getChildAt(COUNTDOWN_ALARM_CONDITION_INDEX),
+ COUNTDOWN_ALARM_CONDITION_INDEX);
}
// ensure something is selected
if (mExpanded && isShown()) {
return context.getString(com.android.internal.R.string.zen_mode_forever);
}
+ // 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.roll(Calendar.DATE, 6);
+ final long nextAlarmMs = mController.getNextAlarm();
+ if (nextAlarmMs > 0) {
+ GregorianCalendar nextAlarm = new GregorianCalendar();
+ nextAlarm.setTimeInMillis(nextAlarmMs);
+ setToMidnight(nextAlarm);
+
+ if (weekRange.compareTo(nextAlarm) >= 0) {
+ return ZenModeConfig.toNextAlarmCondition(mContext, now, nextAlarmMs,
+ ActivityManager.getCurrentUser());
+ }
+ }
+ return null;
+ }
+
+ private void setToMidnight(Calendar calendar) {
+ calendar.set(Calendar.HOUR_OF_DAY, 0);
+ calendar.set(Calendar.MINUTE, 0);
+ calendar.set(Calendar.SECOND, 0);
+ calendar.set(Calendar.MILLISECOND, 0);
+ }
+
private ConditionTag getConditionTagAt(int index) {
return (ConditionTag) mZenConditions.getChildAt(index).getTag();
}
mTimeCondition = ZenModeConfig.toTimeCondition(mContext,
MINUTE_BUCKETS[favoriteIndex], ActivityManager.getCurrentUser());
mBucketIndex = favoriteIndex;
- bind(mTimeCondition, mZenConditions.getChildAt(COUNTDOWN_CONDITION_INDEX));
+ bind(mTimeCondition, mZenConditions.getChildAt(COUNTDOWN_CONDITION_INDEX),
+ COUNTDOWN_CONDITION_INDEX);
getConditionTagAt(COUNTDOWN_CONDITION_INDEX).rb.setChecked(true);
}
}
return c != null && mForeverId.equals(c.id);
}
- private void bind(final Condition condition, final View row) {
+ private void bind(final Condition condition, final View row, final int rowId) {
if (condition == null) throw new IllegalArgumentException("condition must not be null");
final boolean enabled = condition.state == Condition.STATE_TRUE;
final ConditionTag tag =
tag.rb.setEnabled(enabled);
final boolean checked = (mSessionExitCondition != null
|| mAttachedZen != Global.ZEN_MODE_OFF)
- && (sameConditionId(mSessionExitCondition, tag.condition)
- || isCountdown(mSessionExitCondition) && isCountdown(tag.condition));
+ && (sameConditionId(mSessionExitCondition, tag.condition));
if (checked != tag.rb.isChecked()) {
if (DEBUG) Log.d(mTag, "bind checked=" + checked + " condition=" + conditionId);
tag.rb.setChecked(checked);
button1.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
- onClickTimeButton(row, tag, false /*down*/);
+ onClickTimeButton(row, tag, false /*down*/, rowId);
}
});
button2.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
- onClickTimeButton(row, tag, true /*up*/);
+ onClickTimeButton(row, tag, true /*up*/, rowId);
}
});
tag.lines.setOnClickListener(new OnClickListener() {
});
final long time = ZenModeConfig.tryParseCountdownConditionId(conditionId);
- if (time > 0) {
+ if (rowId != COUNTDOWN_ALARM_CONDITION_INDEX && time > 0) {
button1.setVisibility(VISIBLE);
button2.setVisibility(VISIBLE);
if (mBucketIndex > -1) {
tag.line1.getText()));
}
- private void onClickTimeButton(View row, ConditionTag tag, boolean up) {
+ private void onClickTimeButton(View row, ConditionTag tag, boolean up, int rowId) {
MetricsLogger.action(mContext, MetricsLogger.QS_DND_TIME, up);
Condition newCondition = null;
final int N = MINUTE_BUCKETS.length;
if (up && bucketTime > time || !up && bucketTime < time) {
mBucketIndex = j;
newCondition = ZenModeConfig.toTimeCondition(mContext,
- bucketTime, bucketMinutes, now, ActivityManager.getCurrentUser(),
+ bucketTime, bucketMinutes, ActivityManager.getCurrentUser(),
false /*shortVersion*/);
break;
}
MINUTE_BUCKETS[mBucketIndex], ActivityManager.getCurrentUser());
}
mTimeCondition = newCondition;
- bind(mTimeCondition, row);
+ bind(mTimeCondition, row, rowId);
tag.rb.setChecked(true);
select(mTimeCondition);
announceConditionSelection(tag);
private final ZenModeController.Callback mZenCallback = new ZenModeController.Callback() {
@Override
- public void onConditionsChanged(Condition[] conditions) {
- mHandler.obtainMessage(H.UPDATE_CONDITIONS, conditions).sendToTarget();
- }
-
- @Override
public void onManualRuleChanged(ZenRule rule) {
mHandler.obtainMessage(H.MANUAL_RULE_CHANGED, rule).sendToTarget();
}
};
private final class H extends Handler {
- private static final int UPDATE_CONDITIONS = 1;
private static final int MANUAL_RULE_CHANGED = 2;
private static final int UPDATE_WIDGETS = 3;
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
- case UPDATE_CONDITIONS: handleUpdateConditions((Condition[]) msg.obj); break;
case MANUAL_RULE_CHANGED: handleUpdateManualRule((ZenRule) msg.obj); break;
case UPDATE_WIDGETS: updateWidgets(); break;
}
private final ArraySet<View> mTransitioningViews = new ArraySet<View>();
private boolean mTransitioning;
- private boolean mPendingUpdateConditions;
private boolean mPendingUpdateWidgets;
public void clear() {
mTransitioningViews.clear();
- mPendingUpdateConditions = mPendingUpdateWidgets = false;
+ mPendingUpdateWidgets = false;
}
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
pw.println(" TransitionHelper state:");
- pw.print(" mPendingUpdateConditions="); pw.println(mPendingUpdateConditions);
pw.print(" mPendingUpdateWidgets="); pw.println(mPendingUpdateWidgets);
pw.print(" mTransitioning="); pw.println(mTransitioning);
pw.print(" mTransitioningViews="); pw.println(mTransitioningViews);
}
- public void pendingUpdateConditions() {
- mPendingUpdateConditions = true;
- }
-
public void pendingUpdateWidgets() {
mPendingUpdateWidgets = true;
}
@Override
public void run() {
if (DEBUG) Log.d(mTag, "TransitionHelper run"
- + " mPendingUpdateWidgets=" + mPendingUpdateWidgets
- + " mPendingUpdateConditions=" + mPendingUpdateConditions);
+ + " mPendingUpdateWidgets=" + mPendingUpdateWidgets);
if (mPendingUpdateWidgets) {
updateWidgets();
}
- if (mPendingUpdateConditions) {
- handleUpdateConditions();
- }
- mPendingUpdateWidgets = mPendingUpdateConditions = false;
+ mPendingUpdateWidgets = false;
}
private void updateTransitioning() {
mTransitioning = transitioning;
if (DEBUG) Log.d(mTag, "TransitionHelper mTransitioning=" + mTransitioning);
if (!mTransitioning) {
- if (mPendingUpdateConditions || mPendingUpdateWidgets) {
+ if (mPendingUpdateWidgets) {
mHandler.post(this);
} else {
- mPendingUpdateConditions = mPendingUpdateWidgets = false;
+ mPendingUpdateWidgets = false;
}
}
}