am 2ea3650d: Merge "Zen: New behavior for built-in downtime + nextalarm conditions." into lmp-mr1-dev

* commit '2ea3650d18ab0ce4dba2cee101d639400b411452':
  Zen: New behavior for built-in downtime + nextalarm conditions.
This commit is contained in:
John Spurlock
2014-12-08 16:52:49 +00:00
committed by Android Git Automerger
17 changed files with 824 additions and 386 deletions

View File

@@ -71,6 +71,7 @@ interface INotificationManager
ComponentName getEffectsSuppressor();
boolean matchesCallFilter(in Bundle extras);
boolean isSystemConditionProviderEnabled(String path);
ZenModeConfig getZenModeConfig();
boolean setZenModeConfig(in ZenModeConfig config);

View File

@@ -264,5 +264,17 @@ public class NotificationManager
}
}
/**
* @hide
*/
public boolean isSystemConditionProviderEnabled(String path) {
INotificationManager service = getService();
try {
return service.isSystemConditionProviderEnabled(path);
} catch (RemoteException e) {
return false;
}
}
private Context mContext;
}

View File

@@ -64,7 +64,7 @@ public class ZenModeConfig implements Parcelable {
public static final int[] MINUTE_BUCKETS = new int[] { 15, 30, 45, 60, 120, 180, 240, 480 };
private static final int SECONDS_MS = 1000;
private static final int MINUTES_MS = 60 * SECONDS_MS;
private static final int ZERO_VALUE_MS = 20 * SECONDS_MS;
private static final int ZERO_VALUE_MS = 10 * SECONDS_MS;
private static final boolean DEFAULT_ALLOW_EVENTS = true;
@@ -471,6 +471,8 @@ public class ZenModeConfig implements Parcelable {
downtime.startMinute = sleepStartMinute;
downtime.endHour = sleepEndHour;
downtime.endMinute = sleepEndMinute;
downtime.mode = sleepMode;
downtime.none = sleepNone;
return downtime;
}
@@ -510,7 +512,7 @@ public class ZenModeConfig implements Parcelable {
public static final String SYSTEM_AUTHORITY = "android";
// Built-in countdown conditions, e.g. condition://android/countdown/1399917958951
private static final String COUNTDOWN_PATH = "countdown";
public static final String COUNTDOWN_PATH = "countdown";
public static Uri toCountdownConditionId(long time) {
return new Uri.Builder().scheme(Condition.SCHEME)
@@ -536,8 +538,9 @@ public class ZenModeConfig implements Parcelable {
return tryParseCountdownConditionId(conditionId) != 0;
}
// Built-in downtime conditions, e.g. condition://android/downtime?start=10.00&end=7.00
private static final String DOWNTIME_PATH = "downtime";
// Built-in downtime conditions
// e.g. condition://android/downtime?start=10.00&end=7.00&mode=days%3A5%2C6&none=false
public static final String DOWNTIME_PATH = "downtime";
public static Uri toDowntimeConditionId(DowntimeInfo downtime) {
return new Uri.Builder().scheme(Condition.SCHEME)
@@ -545,6 +548,8 @@ public class ZenModeConfig implements Parcelable {
.appendPath(DOWNTIME_PATH)
.appendQueryParameter("start", downtime.startHour + "." + downtime.startMinute)
.appendQueryParameter("end", downtime.endHour + "." + downtime.endMinute)
.appendQueryParameter("mode", downtime.mode)
.appendQueryParameter("none", Boolean.toString(downtime.none))
.build();
}
@@ -562,6 +567,8 @@ public class ZenModeConfig implements Parcelable {
downtime.startMinute = start[1];
downtime.endHour = end[0];
downtime.endMinute = end[1];
downtime.mode = conditionId.getQueryParameter("mode");
downtime.none = Boolean.toString(true).equals(conditionId.getQueryParameter("none"));
return downtime;
}
@@ -583,6 +590,8 @@ public class ZenModeConfig implements Parcelable {
public int startMinute; // 0-59
public int endHour;
public int endMinute;
public String mode;
public boolean none;
@Override
public int hashCode() {
@@ -596,7 +605,12 @@ public class ZenModeConfig implements Parcelable {
return startHour == other.startHour
&& startMinute == other.startMinute
&& endHour == other.endHour
&& endMinute == other.endMinute;
&& endMinute == other.endMinute
&& Objects.equals(mode, other.mode)
&& none == other.none;
}
}
// built-in next alarm conditions
public static final String NEXT_ALARM_PATH = "next_alarm";
}

View File

@@ -1927,9 +1927,19 @@
-->
<integer name="config_LTE_RSRP_threshold_type">1</integer>
<!-- Enabled built-in zen mode condition providers -->
<string-array translatable="false" name="config_system_condition_providers">
<item>countdown</item>
<item>downtime</item>
<item>next_alarm</item>
</string-array>
<!-- Show the next-alarm as a zen exit condition if it occurs in the next n hours. -->
<integer name="config_next_alarm_condition_lookahead_threshold_hrs">12</integer>
<!-- Show downtime as a zen exit condition if it starts in the next n hours. -->
<integer name="config_downtime_condition_lookahead_threshold_hrs">4</integer>
<!-- Flags enabling default window features. See Window.java -->
<bool name="config_defaultWindowFeatureOptionsPanel">true</bool>
<bool name="config_defaultWindowFeatureContextMenu">true</bool>

View File

@@ -2017,7 +2017,9 @@
<java-symbol type="string" name="zen_mode_until" />
<java-symbol type="string" name="zen_mode_next_alarm_summary" />
<java-symbol type="string" name="zen_mode_next_alarm_line_one" />
<java-symbol type="array" name="config_system_condition_providers" />
<java-symbol type="integer" name="config_next_alarm_condition_lookahead_threshold_hrs" />
<java-symbol type="integer" name="config_downtime_condition_lookahead_threshold_hrs" />
<java-symbol type="string" name="muted_by" />
<java-symbol type="string" name="item_is_selected" />

View File

@@ -23,6 +23,7 @@
android:orientation="vertical" >
<FrameLayout
android:id="@+id/zen_buttons_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minHeight="8dp"

View File

@@ -287,8 +287,8 @@
<!-- Number of times to show the strong alarm warning text in the volume dialog -->
<integer name="zen_mode_alarm_warning_threshold">5</integer>
<!-- Maximum number of optional conditions to display in the zen mode selection panel -->
<integer name="zen_mode_max_conditions">3</integer>
<!-- Maximum number of total conditions to display in the zen mode selection panel -->
<integer name="zen_mode_max_conditions">5</integer>
<!-- Enable the default volume dialog -->
<bool name="enable_volume_ui">true</bool>

View File

@@ -124,8 +124,7 @@ public class VolumePanel extends Handler implements DemoMode {
private static final int MSG_ZEN_MODE_AVAILABLE_CHANGED = 13;
private static final int MSG_USER_ACTIVITY = 14;
private static final int MSG_NOTIFICATION_EFFECTS_SUPPRESSOR_CHANGED = 15;
private static final int MSG_ZEN_MODE_CHANGED = 16;
private static final int MSG_INTERNAL_RINGER_MODE_CHANGED = 17;
private static final int MSG_INTERNAL_RINGER_MODE_CHANGED = 16;
// Pseudo stream type for master volume
private static final int STREAM_MASTER = -100;
@@ -511,6 +510,9 @@ public class VolumePanel extends Handler implements DemoMode {
pw.println();
}
}
if (mZenPanel != null) {
mZenPanel.dump(fd, pw, args);
}
}
private void initZenModePanel() {
@@ -723,7 +725,7 @@ public class VolumePanel extends Handler implements DemoMode {
mSliderPanel.addView(active.group);
mActiveStreamType = activeStreamType;
active.group.setVisibility(View.VISIBLE);
updateSlider(active);
updateSlider(active, true /*forceReloadIcon*/);
updateTimeoutDelay();
updateZenPanelVisible();
}
@@ -799,11 +801,12 @@ public class VolumePanel extends Handler implements DemoMode {
}
/** Update the mute and progress state of a slider */
private void updateSlider(StreamControl sc) {
private void updateSlider(StreamControl sc, boolean forceReloadIcon) {
updateSliderProgress(sc, -1);
final boolean muted = isMuted(sc.streamType);
// Force reloading the image resource
sc.icon.setImageDrawable(null);
if (forceReloadIcon) {
sc.icon.setImageDrawable(null);
}
updateSliderIcon(sc, muted);
updateSliderEnabled(sc, muted, false);
updateSliderSuppressor(sc);
@@ -907,11 +910,18 @@ public class VolumePanel extends Handler implements DemoMode {
}
}
public void updateStates() {
private void updateStates() {
final int count = mSliderPanel.getChildCount();
for (int i = 0; i < count; i++) {
StreamControl sc = (StreamControl) mSliderPanel.getChildAt(i).getTag();
updateSlider(sc);
updateSlider(sc, true /*forceReloadIcon*/);
}
}
private void updateActiveSlider() {
final StreamControl active = mStreamControls.get(mActiveStreamType);
if (active != null) {
updateSlider(active, false /*forceReloadIcon*/);
}
}
@@ -1449,12 +1459,11 @@ public class VolumePanel extends Handler implements DemoMode {
break;
}
case MSG_ZEN_MODE_CHANGED:
case MSG_RINGER_MODE_CHANGED:
case MSG_INTERNAL_RINGER_MODE_CHANGED:
case MSG_NOTIFICATION_EFFECTS_SUPPRESSOR_CHANGED: {
if (isShowing()) {
updateStates();
updateActiveSlider();
}
break;
}
@@ -1563,10 +1572,6 @@ public class VolumePanel extends Handler implements DemoMode {
mNotificationEffectsSuppressor = mZenController.getEffectsSuppressor();
sendEmptyMessage(MSG_NOTIFICATION_EFFECTS_SUPPRESSOR_CHANGED);
}
public void onZenChanged(int zen) {
sendEmptyMessage(MSG_ZEN_MODE_CHANGED);
}
};
private final MediaController.Callback mMediaControllerCb = new MediaController.Callback() {
@@ -1591,6 +1596,7 @@ public class VolumePanel extends Handler implements DemoMode {
public void start(StreamControl sc) {
if (sc == null) throw new IllegalArgumentException();
if (LOGD) Log.d(mTag, "Secondary icon animation start");
if (mTarget != null) {
cancel();
}
@@ -1643,6 +1649,7 @@ public class VolumePanel extends Handler implements DemoMode {
@Override
public void run() {
if (mTarget == null) return;
if (LOGD) Log.d(mTag, "Secondary icon animation complete, show notification slider");
mAudioManager.forceVolumeControlStream(StreamResources.NotificationStream.streamType);
mAudioManager.adjustStreamVolume(StreamResources.NotificationStream.streamType,
AudioManager.ADJUST_SAME, AudioManager.FLAG_SHOW_UI);

View File

@@ -17,13 +17,16 @@
package com.android.systemui.volume;
import android.animation.LayoutTransition;
import android.animation.LayoutTransition.TransitionListener;
import android.app.ActivityManager;
import android.app.NotificationManager;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
import android.content.res.Resources;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
@@ -32,6 +35,7 @@ import android.provider.Settings.Global;
import android.service.notification.Condition;
import android.service.notification.ZenModeConfig;
import android.text.TextUtils;
import android.util.ArraySet;
import android.util.AttributeSet;
import android.util.Log;
import android.util.MathUtils;
@@ -50,6 +54,8 @@ import android.widget.TextView;
import com.android.systemui.R;
import com.android.systemui.statusbar.policy.ZenModeController;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.Arrays;
import java.util.Objects;
@@ -67,8 +73,7 @@ public class ZenModePanel extends LinearLayout {
private static final int MAX_BUCKET_MINUTES = MINUTE_BUCKETS[MINUTE_BUCKETS.length - 1];
private static final int DEFAULT_BUCKET_INDEX = Arrays.binarySearch(MINUTE_BUCKETS, 60);
private static final int FOREVER_CONDITION_INDEX = 0;
private static final int TIME_CONDITION_INDEX = 1;
private static final int FIRST_CONDITION_INDEX = 2;
private static final int COUNTDOWN_CONDITION_INDEX = 1;
public static final Intent ZEN_SETTINGS = new Intent(Settings.ACTION_ZEN_MODE_SETTINGS);
@@ -81,6 +86,10 @@ public class ZenModePanel extends LinearLayout {
private final int mSubheadColor;
private final Interpolator mInterpolator;
private final int mMaxConditions;
private final int mMaxOptionalConditions;
private final boolean mCountdownConditionSupported;
private final int mFirstConditionIndex;
private final TransitionHelper mTransitionHelper = new TransitionHelper();
private String mTag = TAG + "/" + Integer.toHexString(System.identityHashCode(this));
@@ -98,7 +107,7 @@ public class ZenModePanel extends LinearLayout {
private String mExitConditionText;
private int mBucketIndex = -1;
private boolean mExpanded;
private boolean mHidden = false;
private boolean mHidden;
private int mSessionZen;
private int mAttachedZen;
private boolean mAttached;
@@ -117,11 +126,30 @@ public class ZenModePanel extends LinearLayout {
mSubheadColor = res.getColor(R.color.qs_subhead);
mInterpolator = AnimationUtils.loadInterpolator(mContext,
com.android.internal.R.interpolator.fast_out_slow_in);
mCountdownConditionSupported = NotificationManager.from(mContext)
.isSystemConditionProviderEnabled(ZenModeConfig.COUNTDOWN_PATH);
final int countdownDelta = mCountdownConditionSupported ? 1 : 0;
mFirstConditionIndex = COUNTDOWN_CONDITION_INDEX + countdownDelta;
final int minConditions = 1 /*forever*/ + countdownDelta;
mMaxConditions = MathUtils.constrain(res.getInteger(R.integer.zen_mode_max_conditions),
1, 100);
minConditions, 100);
mMaxOptionalConditions = mMaxConditions - minConditions;
if (DEBUG) Log.d(mTag, "new ZenModePanel");
}
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);
pw.print(" mExpanded="); pw.println(mExpanded);
pw.print(" mSessionZen="); pw.println(mSessionZen);
pw.print(" mAttachedZen="); pw.println(mAttachedZen);
mTransitionHelper.dump(fd, pw, args);
}
@Override
protected void onFinishInflate() {
super.onFinishInflate();
@@ -135,6 +163,9 @@ public class ZenModePanel extends LinearLayout {
Global.ZEN_MODE_OFF);
mZenButtons.setCallback(mZenButtonsCallback);
final ViewGroup zenButtonsContainer = (ViewGroup) findViewById(R.id.zen_buttons_container);
zenButtonsContainer.setLayoutTransition(newLayoutTransition(null));
mZenSubhead = findViewById(R.id.zen_subhead);
mZenSubheadCollapsed = (TextView) findViewById(R.id.zen_subhead_collapsed);
@@ -159,15 +190,22 @@ public class ZenModePanel extends LinearLayout {
Interaction.register(mMoreSettings, mInteractionCallback);
mZenConditions = (LinearLayout) findViewById(R.id.zen_conditions);
setLayoutTransition(newLayoutTransition());
for (int i = 0; i < mMaxConditions; i++) {
mZenConditions.addView(mInflater.inflate(R.layout.zen_mode_condition, this, false));
}
setLayoutTransition(newLayoutTransition(mTransitionHelper));
}
private LayoutTransition newLayoutTransition() {
private LayoutTransition newLayoutTransition(TransitionListener listener) {
final LayoutTransition transition = new LayoutTransition();
transition.disableTransitionType(LayoutTransition.DISAPPEARING);
transition.disableTransitionType(LayoutTransition.CHANGE_DISAPPEARING);
transition.setInterpolator(LayoutTransition.APPEARING, mInterpolator);
transition.disableTransitionType(LayoutTransition.APPEARING);
transition.setInterpolator(LayoutTransition.CHANGE_APPEARING, mInterpolator);
if (listener != null) {
transition.addTransitionListener(listener);
}
return transition;
}
@@ -175,11 +213,11 @@ public class ZenModePanel extends LinearLayout {
protected void onAttachedToWindow() {
super.onAttachedToWindow();
if (DEBUG) Log.d(mTag, "onAttachedToWindow");
((ViewGroup) getParent()).setLayoutTransition(newLayoutTransition());
mAttached = true;
mAttachedZen = getSelectedZen(-1);
mSessionZen = mAttachedZen;
mSessionExitCondition = copy(mExitCondition);
mTransitionHelper.clear();
setSessionExitCondition(copy(mExitCondition));
refreshExitConditionText();
updateWidgets();
setRequestingConditions(!mHidden);
@@ -193,9 +231,16 @@ public class ZenModePanel extends LinearLayout {
mAttached = false;
mAttachedZen = -1;
mSessionZen = -1;
mSessionExitCondition = null;
setSessionExitCondition(null);
setExpanded(false);
setRequestingConditions(false);
mTransitionHelper.clear();
}
private void setSessionExitCondition(Condition condition) {
if (Objects.equals(condition, mSessionExitCondition)) return;
if (DEBUG) Log.d(mTag, "mSessionExitCondition=" + getConditionId(condition));
mSessionExitCondition = condition;
}
public void setHidden(boolean hidden) {
@@ -228,12 +273,17 @@ public class ZenModePanel extends LinearLayout {
}
/** Start or stop requesting relevant zen mode exit conditions */
private void setRequestingConditions(boolean requesting) {
private void setRequestingConditions(final boolean requesting) {
if (mRequestingConditions == requesting) return;
if (DEBUG) Log.d(mTag, "setRequestingConditions " + requesting);
mRequestingConditions = requesting;
if (mController != null) {
mController.requestConditions(mRequestingConditions);
AsyncTask.execute(new Runnable() {
@Override
public void run() {
mController.requestConditions(requesting);
}
});
}
if (mRequestingConditions) {
mTimeCondition = parseExistingTimeCondition(mExitCondition);
@@ -248,7 +298,7 @@ public class ZenModePanel extends LinearLayout {
mConditions = null; // reset conditions
handleUpdateConditions();
} else {
mZenConditions.removeAllViews();
hideAllConditions();
}
}
@@ -259,7 +309,7 @@ public class ZenModePanel extends LinearLayout {
mSessionZen = getSelectedZen(-1);
handleUpdateZen(mController.getZen());
if (DEBUG) Log.d(mTag, "init mExitCondition=" + mExitCondition);
mZenConditions.removeAllViews();
hideAllConditions();
mController.addCallback(mZenCallback);
}
@@ -270,6 +320,7 @@ public class ZenModePanel extends LinearLayout {
private void setExitCondition(Condition exitCondition) {
if (Objects.equals(mExitCondition, exitCondition)) return;
mExitCondition = exitCondition;
if (DEBUG) Log.d(mTag, "mExitCondition=" + getConditionId(mExitCondition));
refreshExitConditionText();
updateWidgets();
}
@@ -290,7 +341,7 @@ public class ZenModePanel extends LinearLayout {
final String forever = mContext.getString(com.android.internal.R.string.zen_mode_forever);
if (mExitCondition == null) {
mExitConditionText = forever;
} else if (ZenModeConfig.isValidCountdownConditionId(mExitCondition.id)) {
} else if (isCountdown(mExitCondition)) {
final Condition condition = parseExistingTimeCondition(mExitCondition);
mExitConditionText = condition != null ? condition.summary : forever;
} else {
@@ -316,6 +367,24 @@ public class ZenModePanel extends LinearLayout {
}
mZenButtons.setSelectedValue(zen);
updateWidgets();
handleUpdateConditions();
if (mExpanded) {
final Condition selected = getSelectedCondition();
if (!Objects.equals(mExitCondition, selected)) {
select(selected);
}
}
}
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) {
@@ -324,6 +393,10 @@ public class ZenModePanel extends LinearLayout {
}
private void updateWidgets() {
if (mTransitionHelper.isTransitioning()) {
mTransitionHelper.pendingUpdateWidgets();
return;
}
final int zen = getSelectedZen(Global.ZEN_MODE_OFF);
final boolean zenOff = zen == Global.ZEN_MODE_OFF;
final boolean zenImportant = zen == Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
@@ -371,7 +444,7 @@ public class ZenModePanel extends LinearLayout {
}
private Condition[] trimConditions(Condition[] conditions) {
if (conditions == null || conditions.length <= mMaxConditions) {
if (conditions == null || conditions.length <= mMaxOptionalConditions) {
// no need to trim
return conditions;
}
@@ -384,33 +457,34 @@ public class ZenModePanel extends LinearLayout {
break;
}
}
final Condition[] rt = Arrays.copyOf(conditions, mMaxConditions);
if (found >= mMaxConditions) {
final Condition[] rt = Arrays.copyOf(conditions, mMaxOptionalConditions);
if (found >= mMaxOptionalConditions) {
// found after the first N, promote to the end of the first N
rt[mMaxConditions - 1] = conditions[found];
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);
for (int i = mZenConditions.getChildCount() - 1; i >= FIRST_CONDITION_INDEX; i--) {
mZenConditions.removeViewAt(i);
}
// forever
bind(null, mZenConditions.getChildAt(FOREVER_CONDITION_INDEX));
// countdown
bind(mTimeCondition, mZenConditions.getChildAt(TIME_CONDITION_INDEX));
// provider conditions
boolean foundDowntime = false;
for (int i = 0; i < conditionCount; i++) {
bind(mConditions[i], mZenConditions.getChildAt(FIRST_CONDITION_INDEX + i));
foundDowntime |= isDowntime(mConditions[i]);
if (mCountdownConditionSupported) {
bind(mTimeCondition, mZenConditions.getChildAt(COUNTDOWN_CONDITION_INDEX));
}
// ensure downtime exists, if active
if (isDowntime(mSessionExitCondition) && !foundDowntime) {
bind(mSessionExitCondition, null);
// 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);
}
// ensure something is selected
if (mExpanded) {
@@ -418,78 +492,101 @@ public class ZenModePanel extends LinearLayout {
}
}
private static boolean isDowntime(Condition c) {
return ZenModeConfig.isValidDowntimeConditionId(getConditionId(c));
}
private ConditionTag getConditionTagAt(int index) {
return (ConditionTag) mZenConditions.getChildAt(index).getTag();
}
private int getVisibleConditions() {
int rt = 0;
final int N = mZenConditions.getChildCount();
for (int i = 0; i < N; i++) {
rt += mZenConditions.getChildAt(i).getVisibility() == VISIBLE ? 1 : 0;
}
return rt;
}
private void hideAllConditions() {
final int N = mZenConditions.getChildCount();
for (int i = 0; i < N; i++) {
mZenConditions.getChildAt(i).setVisibility(GONE);
}
}
private void ensureSelection() {
// are we left without anything selected? if so, set a default
if (mZenConditions.getChildCount() == 0) return;
for (int i = 0; i < mZenConditions.getChildCount(); i++) {
if (getConditionTagAt(i).rb.isChecked()) {
if (DEBUG) Log.d(mTag, "Not selecting a default, checked="
+ getConditionTagAt(i).condition);
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 (favoriteIndex == -1) {
getConditionTagAt(FOREVER_CONDITION_INDEX).rb.setChecked(true);
if (favoriteIndex == -1 || !mCountdownConditionSupported) {
foreverTag.rb.setChecked(true);
} else {
mTimeCondition = ZenModeConfig.toTimeCondition(mContext,
MINUTE_BUCKETS[favoriteIndex], ActivityManager.getCurrentUser());
mBucketIndex = favoriteIndex;
bind(mTimeCondition, mZenConditions.getChildAt(TIME_CONDITION_INDEX));
getConditionTagAt(TIME_CONDITION_INDEX).rb.setChecked(true);
bind(mTimeCondition, mZenConditions.getChildAt(COUNTDOWN_CONDITION_INDEX));
getConditionTagAt(COUNTDOWN_CONDITION_INDEX).rb.setChecked(true);
}
}
private void handleExitConditionChanged(Condition exitCondition) {
setExitCondition(exitCondition);
if (DEBUG) Log.d(mTag, "handleExitConditionChanged " + mExitCondition);
final int N = mZenConditions.getChildCount();
final int N = getVisibleConditions();
for (int i = 0; i < N; i++) {
final ConditionTag tag = getConditionTagAt(i);
tag.rb.setChecked(sameConditionId(tag.condition, mExitCondition));
if (tag != null) {
if (sameConditionId(tag.condition, mExitCondition)) {
bind(exitCondition, mZenConditions.getChildAt(i));
}
}
}
}
private void bind(final Condition condition, View convertView) {
private boolean isCountdown(Condition c) {
return c != null && ZenModeConfig.isValidCountdownConditionId(c.id);
}
private void bind(final Condition condition, final View row) {
final boolean enabled = condition == null || condition.state == Condition.STATE_TRUE;
final View row;
if (convertView == null) {
row = mInflater.inflate(R.layout.zen_mode_condition, this, false);
if (DEBUG) Log.d(mTag, "Adding new condition view for: " + condition);
mZenConditions.addView(row);
} else {
row = convertView;
}
final ConditionTag tag =
row.getTag() != null ? (ConditionTag) row.getTag() : new ConditionTag();
row.setTag(tag);
final boolean first = tag.rb == null;
if (tag.rb == null) {
tag.rb = (RadioButton) row.findViewById(android.R.id.checkbox);
}
tag.condition = condition;
final Uri conditionId = getConditionId(tag.condition);
if (DEBUG) Log.d(mTag, "bind i=" + mZenConditions.indexOfChild(row) + " first=" + first
+ " condition=" + conditionId);
tag.rb.setEnabled(enabled);
if ((mSessionExitCondition != null || mAttachedZen != Global.ZEN_MODE_OFF)
&& sameConditionId(mSessionExitCondition, tag.condition)) {
tag.rb.setChecked(true);
final boolean checked = (mSessionExitCondition != null
|| mAttachedZen != Global.ZEN_MODE_OFF)
&& (sameConditionId(mSessionExitCondition, tag.condition)
|| isCountdown(mSessionExitCondition) && isCountdown(tag.condition));
if (checked != tag.rb.isChecked()) {
if (DEBUG) Log.d(mTag, "bind checked=" + checked + " condition=" + conditionId);
tag.rb.setChecked(checked);
}
tag.rb.setOnCheckedChangeListener(new OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
if (mExpanded && isChecked) {
if (DEBUG) Log.d(mTag, "onCheckedChanged " + tag.condition);
final int N = mZenConditions.getChildCount();
if (DEBUG) Log.d(mTag, "onCheckedChanged " + conditionId);
final int N = getVisibleConditions();
for (int i = 0; i < N; i++) {
ConditionTag childTag = getConditionTagAt(i);
if (childTag == tag) continue;
final ConditionTag childTag = getConditionTagAt(i);
if (childTag == null || childTag == tag) continue;
childTag.rb.setChecked(false);
}
select(tag.condition);
@@ -547,8 +644,10 @@ public class ZenModePanel extends LinearLayout {
}
});
final long time = ZenModeConfig.tryParseCountdownConditionId(getConditionId(tag.condition));
final long time = ZenModeConfig.tryParseCountdownConditionId(conditionId);
if (time > 0) {
button1.setVisibility(VISIBLE);
button2.setVisibility(VISIBLE);
if (mBucketIndex > -1) {
button1.setEnabled(mBucketIndex > 0);
button2.setEnabled(mBucketIndex < MINUTE_BUCKETS.length - 1);
@@ -563,16 +662,17 @@ public class ZenModePanel extends LinearLayout {
button1.setAlpha(button1.isEnabled() ? 1f : .5f);
button2.setAlpha(button2.isEnabled() ? 1f : .5f);
} else {
button1.setVisibility(View.GONE);
button2.setVisibility(View.GONE);
button1.setVisibility(GONE);
button2.setVisibility(GONE);
}
// wire up interaction callbacks for newly-added condition rows
if (convertView == null) {
if (first) {
Interaction.register(tag.rb, mInteractionCallback);
Interaction.register(tag.lines, mInteractionCallback);
Interaction.register(button1, mInteractionCallback);
Interaction.register(button2, mInteractionCallback);
}
row.setVisibility(VISIBLE);
}
private void announceConditionSelection(ConditionTag tag) {
@@ -629,18 +729,23 @@ public class ZenModePanel extends LinearLayout {
announceConditionSelection(tag);
}
private void select(Condition condition) {
private void select(final Condition condition) {
if (DEBUG) Log.d(mTag, "select " + condition);
if (mController != null) {
mController.setExitCondition(condition);
AsyncTask.execute(new Runnable() {
@Override
public void run() {
mController.setExitCondition(condition);
}
});
}
setExitCondition(condition);
if (condition == null) {
mPrefs.setMinuteIndex(-1);
} else if (ZenModeConfig.isValidCountdownConditionId(condition.id) && mBucketIndex != -1) {
} else if (isCountdown(condition) && mBucketIndex != -1) {
mPrefs.setMinuteIndex(mBucketIndex);
}
mSessionExitCondition = copy(condition);
setSessionExitCondition(copy(condition));
}
private void fireMoreSettings() {
@@ -784,10 +889,15 @@ public class ZenModePanel extends LinearLayout {
private final SegmentedButtons.Callback mZenButtonsCallback = new SegmentedButtons.Callback() {
@Override
public void onSelected(Object value) {
public void onSelected(final Object value) {
if (value != null && mZenButtons.isShown()) {
if (DEBUG) Log.d(mTag, "mZenButtonsCallback selected=" + value);
mController.setZen((Integer) value);
AsyncTask.execute(new Runnable() {
@Override
public void run() {
mController.setZen((Integer) value);
}
});
}
}
@@ -803,4 +913,79 @@ public class ZenModePanel extends LinearLayout {
fireInteraction();
}
};
private final class TransitionHelper implements TransitionListener, Runnable {
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;
}
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;
}
public boolean isTransitioning() {
return !mTransitioningViews.isEmpty();
}
@Override
public void startTransition(LayoutTransition transition,
ViewGroup container, View view, int transitionType) {
mTransitioningViews.add(view);
updateTransitioning();
}
@Override
public void endTransition(LayoutTransition transition,
ViewGroup container, View view, int transitionType) {
mTransitioningViews.remove(view);
updateTransitioning();
}
@Override
public void run() {
if (DEBUG) Log.d(mTag, "TransitionHelper run"
+ " mPendingUpdateWidgets=" + mPendingUpdateWidgets
+ " mPendingUpdateConditions=" + mPendingUpdateConditions);
if (mPendingUpdateWidgets) {
updateWidgets();
}
if (mPendingUpdateConditions) {
handleUpdateConditions();
}
mPendingUpdateWidgets = mPendingUpdateConditions = false;
}
private void updateTransitioning() {
final boolean transitioning = isTransitioning();
if (mTransitioning == transitioning) return;
mTransitioning = transitioning;
if (DEBUG) Log.d(mTag, "TransitionHelper mTransitioning=" + mTransitioning);
if (!mTransitioning) {
if (mPendingUpdateConditions || mPendingUpdateWidgets) {
mHandler.post(this);
} else {
mPendingUpdateConditions = mPendingUpdateWidgets = false;
}
}
}
}
}

View File

@@ -50,10 +50,11 @@ public class ConditionProviders extends ManagedServices {
private final ArrayMap<IBinder, IConditionListener> mListeners
= new ArrayMap<IBinder, IConditionListener>();
private final ArrayList<ConditionRecord> mRecords = new ArrayList<ConditionRecord>();
private final CountdownConditionProvider mCountdown = new CountdownConditionProvider();
private final ArraySet<String> mSystemConditionProviders;
private final CountdownConditionProvider mCountdown;
private final DowntimeConditionProvider mDowntime;
private final NextAlarmConditionProvider mNextAlarm;
private final NextAlarmTracker mNextAlarmTracker;
private final DowntimeConditionProvider mDowntime = new DowntimeConditionProvider();
private final NextAlarmConditionProvider mNextAlarm = new NextAlarmConditionProvider();
private Condition mExitCondition;
private ComponentName mExitConditionComponent;
@@ -63,8 +64,22 @@ public class ConditionProviders extends ManagedServices {
super(context, handler, new Object(), userProfiles);
mZenModeHelper = zenModeHelper;
mZenModeHelper.addCallback(new ZenModeHelperCallback());
mSystemConditionProviders = safeSet(PropConfig.getStringArray(mContext,
"system.condition.providers",
R.array.config_system_condition_providers));
final boolean countdown = mSystemConditionProviders.contains(ZenModeConfig.COUNTDOWN_PATH);
final boolean downtime = mSystemConditionProviders.contains(ZenModeConfig.DOWNTIME_PATH);
final boolean nextAlarm = mSystemConditionProviders.contains(ZenModeConfig.NEXT_ALARM_PATH);
mNextAlarmTracker = (downtime || nextAlarm) ? new NextAlarmTracker(mContext) : null;
mCountdown = countdown ? new CountdownConditionProvider() : null;
mDowntime = downtime ? new DowntimeConditionProvider(this, mNextAlarmTracker,
mZenModeHelper) : null;
mNextAlarm = nextAlarm ? new NextAlarmConditionProvider(mNextAlarmTracker) : null;
loadZenConfig();
mNextAlarmTracker = new NextAlarmTracker(context);
}
public boolean isSystemConditionProviderEnabled(String path) {
return mSystemConditionProviders.contains(path);
}
@Override
@@ -100,10 +115,19 @@ public class ConditionProviders extends ManagedServices {
}
}
}
mCountdown.dump(pw, filter);
mDowntime.dump(pw, filter);
mNextAlarm.dump(pw, filter);
mNextAlarmTracker.dump(pw, filter);
pw.print(" mSystemConditionProviders: "); pw.println(mSystemConditionProviders);
if (mCountdown != null) {
mCountdown.dump(pw, filter);
}
if (mDowntime != null) {
mDowntime.dump(pw, filter);
}
if (mNextAlarm != null) {
mNextAlarm.dump(pw, filter);
}
if (mNextAlarmTracker != null) {
mNextAlarmTracker.dump(pw, filter);
}
}
@Override
@@ -114,24 +138,32 @@ public class ConditionProviders extends ManagedServices {
@Override
public void onBootPhaseAppsCanStart() {
super.onBootPhaseAppsCanStart();
mNextAlarmTracker.init();
mCountdown.attachBase(mContext);
registerService(mCountdown.asInterface(), CountdownConditionProvider.COMPONENT,
UserHandle.USER_OWNER);
mDowntime.attachBase(mContext);
registerService(mDowntime.asInterface(), DowntimeConditionProvider.COMPONENT,
UserHandle.USER_OWNER);
mDowntime.setCallback(new DowntimeCallback());
mNextAlarm.attachBase(mContext);
registerService(mNextAlarm.asInterface(), NextAlarmConditionProvider.COMPONENT,
UserHandle.USER_OWNER);
mNextAlarm.setCallback(new NextAlarmCallback());
if (mNextAlarmTracker != null) {
mNextAlarmTracker.init();
}
if (mCountdown != null) {
mCountdown.attachBase(mContext);
registerService(mCountdown.asInterface(), CountdownConditionProvider.COMPONENT,
UserHandle.USER_OWNER);
}
if (mDowntime != null) {
mDowntime.attachBase(mContext);
registerService(mDowntime.asInterface(), DowntimeConditionProvider.COMPONENT,
UserHandle.USER_OWNER);
}
if (mNextAlarm != null) {
mNextAlarm.attachBase(mContext);
registerService(mNextAlarm.asInterface(), NextAlarmConditionProvider.COMPONENT,
UserHandle.USER_OWNER);
}
}
@Override
public void onUserSwitched() {
super.onUserSwitched();
mNextAlarmTracker.onUserSwitched();
if (mNextAlarmTracker != null) {
mNextAlarmTracker.onUserSwitched();
}
}
@Override
@@ -171,6 +203,7 @@ public class ConditionProviders extends ManagedServices {
if (!r.component.equals(removed.component)) continue;
if (r.isManual) {
// removing the current manual condition, exit zen
onManualConditionClearing();
mZenModeHelper.setZenMode(Global.ZEN_MODE_OFF, "manualServiceRemoved");
}
if (r.isAutomatic) {
@@ -273,6 +306,7 @@ public class ConditionProviders extends ManagedServices {
} else if (DEBUG) {
Slog.d(TAG, "Exit zen: manual condition false: " + c);
}
onManualConditionClearing();
mZenModeHelper.setZenMode(Settings.Global.ZEN_MODE_OFF,
"manualConditionExit");
unsubscribeLocked(r);
@@ -304,28 +338,28 @@ public class ConditionProviders extends ManagedServices {
}
}
private void ensureRecordExists(Condition condition, IConditionProvider provider,
ComponentName component) {
// constructed by convention, make sure the record exists...
final ConditionRecord r = getRecordLocked(condition.id, component);
if (r.info == null) {
// ... and is associated with the in-process service
r.info = checkServiceTokenLocked(provider);
}
}
public void setZenModeCondition(Condition condition, String reason) {
if (DEBUG) Slog.d(TAG, "setZenModeCondition " + condition);
if (DEBUG) Slog.d(TAG, "setZenModeCondition " + condition + " reason=" + reason);
synchronized(mMutex) {
ComponentName conditionComponent = null;
if (condition != null) {
if (ZenModeConfig.isValidCountdownConditionId(condition.id)) {
// constructed by the client, make sure the record exists...
final ConditionRecord r = getRecordLocked(condition.id,
if (mCountdown != null && ZenModeConfig.isValidCountdownConditionId(condition.id)) {
ensureRecordExists(condition, mCountdown.asInterface(),
CountdownConditionProvider.COMPONENT);
if (r.info == null) {
// ... and is associated with the in-process service
r.info = checkServiceTokenLocked(mCountdown.asInterface());
}
}
if (ZenModeConfig.isValidDowntimeConditionId(condition.id)) {
// constructed by the client, make sure the record exists...
final ConditionRecord r = getRecordLocked(condition.id,
if (mDowntime != null && ZenModeConfig.isValidDowntimeConditionId(condition.id)) {
ensureRecordExists(condition, mDowntime.asInterface(),
DowntimeConditionProvider.COMPONENT);
if (r.info == null) {
// ... and is associated with the in-process service
r.info = checkServiceTokenLocked(mDowntime.asInterface());
}
}
}
final int N = mRecords.size();
@@ -370,6 +404,7 @@ public class ConditionProviders extends ManagedServices {
ZenLog.traceSubscribe(r != null ? r.id : null, provider, re);
}
@SafeVarargs
private static <T> ArraySet<T> safeSet(T... items) {
final ArraySet<T> rt = new ArraySet<T>();
if (items == null || items.length == 0) return rt;
@@ -485,7 +520,9 @@ public class ConditionProviders extends ManagedServices {
if (changingExit) {
ZenLog.traceExitCondition(mExitCondition, mExitConditionComponent, "config");
}
mDowntime.setConfig(config);
if (mDowntime != null) {
mDowntime.setConfig(config);
}
if (config.conditionComponents == null || config.conditionIds == null
|| config.conditionComponents.length != config.conditionIds.length) {
if (DEBUG) Slog.d(TAG, "loadZenConfig: no conditions");
@@ -538,6 +575,12 @@ public class ConditionProviders extends ManagedServices {
mZenModeHelper.setConfig(config);
}
private void onManualConditionClearing() {
if (mDowntime != null) {
mDowntime.onManualConditionClearing();
}
}
private class ZenModeHelperCallback extends ZenModeHelper.Callback {
@Override
void onConfigChanged() {
@@ -554,46 +597,6 @@ public class ConditionProviders extends ManagedServices {
}
}
private class DowntimeCallback implements DowntimeConditionProvider.Callback {
@Override
public void onDowntimeChanged(int downtimeMode) {
final int mode = mZenModeHelper.getZenMode();
final ZenModeConfig config = mZenModeHelper.getConfig();
final boolean inDowntime = downtimeMode == Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS
|| downtimeMode == Global.ZEN_MODE_NO_INTERRUPTIONS;
final boolean downtimeCurrent = mDowntime.isDowntimeCondition(mExitCondition);
// enter downtime, or update mode if reconfigured during an active downtime
if (inDowntime && (mode == Global.ZEN_MODE_OFF || downtimeCurrent) && config != null) {
final Condition condition = mDowntime.createCondition(config.toDowntimeInfo(),
config.sleepNone, Condition.STATE_TRUE);
mZenModeHelper.setZenMode(downtimeMode, "downtimeEnter");
setZenModeCondition(condition, "downtime");
}
// exit downtime
if (!inDowntime && downtimeCurrent && (mode == Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS
|| mode == Global.ZEN_MODE_NO_INTERRUPTIONS)) {
mZenModeHelper.setZenMode(Global.ZEN_MODE_OFF, "downtimeExit");
}
}
@Override
public NextAlarmTracker getNextAlarmTracker() {
return mNextAlarmTracker;
}
}
private class NextAlarmCallback implements NextAlarmConditionProvider.Callback {
@Override
public boolean isInDowntime() {
return mDowntime.isInDowntime();
}
@Override
public NextAlarmTracker getNextAlarmTracker() {
return mNextAlarmTracker;
}
}
private static class ConditionRecord {
public final Uri id;
public final ComponentName component;

View File

@@ -0,0 +1,113 @@
/**
* Copyright (c) 2014, The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.server.notification;
import java.util.Calendar;
import java.util.Objects;
import java.util.TimeZone;
import android.service.notification.ZenModeConfig;
import android.service.notification.ZenModeConfig.DowntimeInfo;
import android.util.ArraySet;
public class DowntimeCalendar {
private final ArraySet<Integer> mDays = new ArraySet<Integer>();
private final Calendar mCalendar = Calendar.getInstance();
private DowntimeInfo mInfo;
@Override
public String toString() {
return "DowntimeCalendar[mDays=" + mDays + "]";
}
public void setDowntimeInfo(DowntimeInfo info) {
if (Objects.equals(mInfo, info)) return;
mInfo = info;
updateDays();
}
public long nextDowntimeStart(long time) {
if (mInfo == null || mDays.size() == 0) return Long.MAX_VALUE;
final long start = getTime(time, mInfo.startHour, mInfo.startMinute);
for (int i = 0; i < Calendar.SATURDAY; i++) {
final long t = addDays(start, i);
if (t > time && isInDowntime(t)) {
return t;
}
}
return Long.MAX_VALUE;
}
public void setTimeZone(TimeZone tz) {
mCalendar.setTimeZone(tz);
}
public long getNextTime(long now, int hr, int min) {
final long time = getTime(now, hr, min);
return time <= now ? addDays(time, 1) : time;
}
private long getTime(long millis, int hour, int min) {
mCalendar.setTimeInMillis(millis);
mCalendar.set(Calendar.HOUR_OF_DAY, hour);
mCalendar.set(Calendar.MINUTE, min);
mCalendar.set(Calendar.SECOND, 0);
mCalendar.set(Calendar.MILLISECOND, 0);
return mCalendar.getTimeInMillis();
}
public boolean isInDowntime(long time) {
if (mInfo == null || mDays.size() == 0) return false;
final long start = getTime(time, mInfo.startHour, mInfo.startMinute);
long end = getTime(time, mInfo.endHour, mInfo.endMinute);
if (end <= start) {
end = addDays(end, 1);
}
return isInDowntime(-1, time, start, end) || isInDowntime(0, time, start, end);
}
private boolean isInDowntime(int daysOffset, long time, long start, long end) {
final int n = Calendar.SATURDAY;
final int day = ((getDayOfWeek(time) - 1) + (daysOffset % n) + n) % n + 1;
start = addDays(start, daysOffset);
end = addDays(end, daysOffset);
return mDays.contains(day) && time >= start && time < end;
}
private int getDayOfWeek(long time) {
mCalendar.setTimeInMillis(time);
return mCalendar.get(Calendar.DAY_OF_WEEK);
}
private void updateDays() {
mDays.clear();
if (mInfo != null) {
final int[] days = ZenModeConfig.tryParseDays(mInfo.mode);
for (int i = 0; days != null && i < days.length; i++) {
mDays.add(days[i]);
}
}
}
private long addDays(long time, int days) {
mCalendar.setTimeInMillis(time);
mCalendar.add(Calendar.DATE, days);
return mCalendar.getTimeInMillis();
}
}

View File

@@ -35,13 +35,13 @@ import android.text.format.DateFormat;
import android.util.ArraySet;
import android.util.Log;
import android.util.Slog;
import android.util.TimeUtils;
import com.android.internal.R;
import com.android.server.notification.NotificationManagerService.DumpFilter;
import java.io.PrintWriter;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.Locale;
import java.util.Objects;
@@ -61,26 +61,44 @@ public class DowntimeConditionProvider extends ConditionProviderService {
private static final int EXIT_CODE = 101;
private static final String EXTRA_TIME = "time";
private final Calendar mCalendar = Calendar.getInstance();
private static final long SECONDS = 1000;
private static final long MINUTES = 60 * SECONDS;
private static final long HOURS = 60 * MINUTES;
private final Context mContext = this;
private final ArraySet<Integer> mDays = new ArraySet<Integer>();
private final ArraySet<Long> mFiredAlarms = new ArraySet<Long>();
private final DowntimeCalendar mCalendar = new DowntimeCalendar();
private final FiredAlarms mFiredAlarms = new FiredAlarms();
private final ArraySet<Uri> mSubscriptions = new ArraySet<Uri>();
private final ConditionProviders mConditionProviders;
private final NextAlarmTracker mTracker;
private final ZenModeHelper mZenModeHelper;
private boolean mConnected;
private NextAlarmTracker mTracker;
private int mDowntimeMode;
private long mLookaheadThreshold;
private ZenModeConfig mConfig;
private Callback mCallback;
private boolean mDowntimed;
private boolean mConditionClearing;
private boolean mRequesting;
public DowntimeConditionProvider() {
public DowntimeConditionProvider(ConditionProviders conditionProviders,
NextAlarmTracker tracker, ZenModeHelper zenModeHelper) {
if (DEBUG) Slog.d(TAG, "new DowntimeConditionProvider()");
mConditionProviders = conditionProviders;
mTracker = tracker;
mZenModeHelper = zenModeHelper;
}
public void dump(PrintWriter pw, DumpFilter filter) {
pw.println(" DowntimeConditionProvider:");
pw.print(" mConnected="); pw.println(mConnected);
pw.print(" mDowntimeMode="); pw.println(Global.zenModeToString(mDowntimeMode));
pw.print(" mSubscriptions="); pw.println(mSubscriptions);
pw.print(" mLookaheadThreshold="); pw.print(mLookaheadThreshold);
pw.print(" ("); TimeUtils.formatDuration(mLookaheadThreshold, pw); pw.println(")");
pw.print(" mCalendar="); pw.println(mCalendar);
pw.print(" mFiredAlarms="); pw.println(mFiredAlarms);
pw.print(" mDowntimed="); pw.println(mDowntimed);
pw.print(" mConditionClearing="); pw.println(mConditionClearing);
pw.print(" mRequesting="); pw.println(mRequesting);
}
public void attachBase(Context base) {
@@ -91,22 +109,20 @@ public class DowntimeConditionProvider extends ConditionProviderService {
return (IConditionProvider) onBind(null);
}
public void setCallback(Callback callback) {
mCallback = callback;
}
@Override
public void onConnected() {
if (DEBUG) Slog.d(TAG, "onConnected");
mConnected = true;
mLookaheadThreshold = PropConfig.getInt(mContext, "downtime.condition.lookahead",
R.integer.config_downtime_condition_lookahead_threshold_hrs) * HOURS;
final IntentFilter filter = new IntentFilter();
filter.addAction(ENTER_ACTION);
filter.addAction(EXIT_ACTION);
filter.addAction(Intent.ACTION_TIME_CHANGED);
filter.addAction(Intent.ACTION_TIMEZONE_CHANGED);
mContext.registerReceiver(mReceiver, filter);
mTracker = mCallback.getNextAlarmTracker();
mTracker.addCallback(mTrackerCallback);
mZenModeHelper.addCallback(mZenCallback);
init();
}
@@ -114,59 +130,125 @@ public class DowntimeConditionProvider extends ConditionProviderService {
public void onDestroy() {
if (DEBUG) Slog.d(TAG, "onDestroy");
mTracker.removeCallback(mTrackerCallback);
mZenModeHelper.removeCallback(mZenCallback);
mConnected = false;
}
@Override
public void onRequestConditions(int relevance) {
if (DEBUG) Slog.d(TAG, "onRequestConditions relevance=" + relevance);
if ((relevance & Condition.FLAG_RELEVANT_NOW) != 0) {
if (isInDowntime() && mConfig != null) {
notifyCondition(createCondition(mConfig.toDowntimeInfo(), mConfig.sleepNone,
Condition.STATE_TRUE));
}
}
if (!mConnected) return;
mRequesting = (relevance & Condition.FLAG_RELEVANT_NOW) != 0;
evaluateSubscriptions();
}
@Override
public void onSubscribe(Uri conditionId) {
if (DEBUG) Slog.d(TAG, "onSubscribe conditionId=" + conditionId);
final DowntimeInfo downtime = ZenModeConfig.tryParseDowntimeConditionId(conditionId);
if (downtime != null && mConfig != null) {
final int state = mConfig.toDowntimeInfo().equals(downtime) && isInDowntime()
? Condition.STATE_TRUE : Condition.STATE_FALSE;
if (DEBUG) Slog.d(TAG, "notify condition state: " + Condition.stateToString(state));
notifyCondition(createCondition(downtime, mConfig.sleepNone, state));
if (downtime == null) return;
mFiredAlarms.clear();
mSubscriptions.add(conditionId);
notifyCondition(downtime);
}
private boolean shouldShowCondition() {
final long now = System.currentTimeMillis();
if (DEBUG) Slog.d(TAG, "shouldShowCondition now=" + mCalendar.isInDowntime(now)
+ " lookahead="
+ (mCalendar.nextDowntimeStart(now) <= (now + mLookaheadThreshold)));
return mCalendar.isInDowntime(now)
|| mCalendar.nextDowntimeStart(now) <= (now + mLookaheadThreshold);
}
private void notifyCondition(DowntimeInfo downtime) {
if (mConfig == null) {
// we don't know yet
notifyCondition(createCondition(downtime, Condition.STATE_UNKNOWN));
return;
}
if (!downtime.equals(mConfig.toDowntimeInfo())) {
// not the configured downtime, consider it false
notifyCondition(createCondition(downtime, Condition.STATE_FALSE));
return;
}
if (!shouldShowCondition()) {
// configured downtime, but not within the time range
notifyCondition(createCondition(downtime, Condition.STATE_FALSE));
return;
}
if (isZenNone() && mFiredAlarms.findBefore(System.currentTimeMillis())) {
// within the configured time range, but wake up if none and the next alarm is fired
notifyCondition(createCondition(downtime, Condition.STATE_FALSE));
return;
}
// within the configured time range, condition still valid
notifyCondition(createCondition(downtime, Condition.STATE_TRUE));
}
private boolean isZenNone() {
return mZenModeHelper.getZenMode() == Global.ZEN_MODE_NO_INTERRUPTIONS;
}
private boolean isZenOff() {
return mZenModeHelper.getZenMode() == Global.ZEN_MODE_OFF;
}
private void evaluateSubscriptions() {
ArraySet<Uri> conditions = mSubscriptions;
if (mConfig != null && mRequesting && shouldShowCondition()) {
final Uri id = ZenModeConfig.toDowntimeConditionId(mConfig.toDowntimeInfo());
if (!conditions.contains(id)) {
conditions = new ArraySet<Uri>(conditions);
conditions.add(id);
}
}
for (Uri conditionId : conditions) {
final DowntimeInfo downtime = ZenModeConfig.tryParseDowntimeConditionId(conditionId);
if (downtime != null) {
notifyCondition(downtime);
}
}
}
@Override
public void onUnsubscribe(Uri conditionId) {
if (DEBUG) Slog.d(TAG, "onUnsubscribe conditionId=" + conditionId);
final boolean current = mSubscriptions.contains(conditionId);
if (DEBUG) Slog.d(TAG, "onUnsubscribe conditionId=" + conditionId + " current=" + current);
mSubscriptions.remove(conditionId);
mFiredAlarms.clear();
}
public void setConfig(ZenModeConfig config) {
if (Objects.equals(mConfig, config)) return;
if (DEBUG) Slog.d(TAG, "setConfig");
final boolean downtimeChanged = mConfig == null || config == null
|| !mConfig.toDowntimeInfo().equals(config.toDowntimeInfo());
mConfig = config;
if (mConnected) {
if (DEBUG) Slog.d(TAG, "setConfig downtimeChanged=" + downtimeChanged);
if (mConnected && downtimeChanged) {
mDowntimed = false;
init();
}
// when active, mark downtime as entered for today
if (mConfig != null && mConfig.exitCondition != null
&& ZenModeConfig.isValidDowntimeConditionId(mConfig.exitCondition.id)) {
mDowntimed = true;
}
}
public boolean isInDowntime() {
return mDowntimeMode != Global.ZEN_MODE_OFF;
public void onManualConditionClearing() {
mConditionClearing = true;
}
public Condition createCondition(DowntimeInfo downtime, boolean orAlarm, int state) {
private Condition createCondition(DowntimeInfo downtime, int state) {
if (downtime == null) return null;
final Uri id = ZenModeConfig.toDowntimeConditionId(downtime);
final String skeleton = DateFormat.is24HourFormat(mContext) ? "Hm" : "hma";
final Locale locale = Locale.getDefault();
final String pattern = DateFormat.getBestDateTimePattern(locale, skeleton);
final long now = System.currentTimeMillis();
long endTime = getTime(now, downtime.endHour, downtime.endMinute);
if (orAlarm) {
long endTime = mCalendar.getNextTime(now, downtime.endHour, downtime.endMinute);
if (isZenNone()) {
final AlarmClockInfo nextAlarm = mTracker.getNextAlarm();
final long nextAlarmTime = nextAlarm != null ? nextAlarm.getTriggerTime() : 0;
if (nextAlarmTime > now && nextAlarmTime < endTime) {
@@ -179,79 +261,11 @@ public class DowntimeConditionProvider extends ConditionProviderService {
return new Condition(id, summary, line1, formatted, 0, state, Condition.FLAG_RELEVANT_NOW);
}
public boolean isDowntimeCondition(Condition condition) {
return condition != null && ZenModeConfig.isValidDowntimeConditionId(condition.id);
}
private void init() {
updateDays();
reevaluateDowntime();
mCalendar.setDowntimeInfo(mConfig != null ? mConfig.toDowntimeInfo() : null);
evaluateSubscriptions();
updateAlarms();
}
private void updateDays() {
mDays.clear();
if (mConfig != null) {
final int[] days = ZenModeConfig.tryParseDays(mConfig.sleepMode);
for (int i = 0; days != null && i < days.length; i++) {
mDays.add(days[i]);
}
}
}
private boolean isInDowntime(long time) {
if (mConfig == null || mDays.size() == 0) return false;
final long start = getTime(time, mConfig.sleepStartHour, mConfig.sleepStartMinute);
long end = getTime(time, mConfig.sleepEndHour, mConfig.sleepEndMinute);
if (start == end) return false;
if (end < start) {
end = addDays(end, 1);
}
final boolean orAlarm = mConfig.sleepNone;
return isInDowntime(-1, time, start, end, orAlarm)
|| isInDowntime(0, time, start, end, orAlarm);
}
private boolean isInDowntime(int daysOffset, long time, long start, long end, boolean orAlarm) {
final int n = Calendar.SATURDAY;
final int day = ((getDayOfWeek(time) - 1) + (daysOffset % n) + n) % n + 1;
start = addDays(start, daysOffset);
end = addDays(end, daysOffset);
if (orAlarm) {
end = findFiredAlarm(start, end);
}
return mDays.contains(day) && time >= start && time < end;
}
private long findFiredAlarm(long start, long end) {
final int N = mFiredAlarms.size();
for (int i = 0; i < N; i++) {
final long firedAlarm = mFiredAlarms.valueAt(i);
if (firedAlarm > start && firedAlarm < end) {
return firedAlarm;
}
}
return end;
}
private void reevaluateDowntime() {
final long now = System.currentTimeMillis();
final boolean inDowntimeNow = isInDowntime(now);
final int downtimeMode = inDowntimeNow ? (mConfig.sleepNone
? Global.ZEN_MODE_NO_INTERRUPTIONS : Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS)
: Global.ZEN_MODE_OFF;
if (DEBUG) Slog.d(TAG, "downtimeMode=" + downtimeMode);
if (downtimeMode == mDowntimeMode) return;
mDowntimeMode = downtimeMode;
Slog.i(TAG, (isInDowntime() ? "Entering" : "Exiting" ) + " downtime");
ZenLog.traceDowntime(mDowntimeMode, getDayOfWeek(now), mDays);
fireDowntimeChanged();
}
private void fireDowntimeChanged() {
if (mCallback != null) {
mCallback.onDowntimeChanged(mDowntimeMode);
}
evaluateAutotrigger();
}
private void updateAlarms() {
@@ -260,38 +274,11 @@ public class DowntimeConditionProvider extends ConditionProviderService {
updateAlarm(EXIT_ACTION, EXIT_CODE, mConfig.sleepEndHour, mConfig.sleepEndMinute);
}
private int getDayOfWeek(long time) {
mCalendar.setTimeInMillis(time);
return mCalendar.get(Calendar.DAY_OF_WEEK);
}
private long getTime(long millis, int hour, int min) {
mCalendar.setTimeInMillis(millis);
mCalendar.set(Calendar.HOUR_OF_DAY, hour);
mCalendar.set(Calendar.MINUTE, min);
mCalendar.set(Calendar.SECOND, 0);
mCalendar.set(Calendar.MILLISECOND, 0);
return mCalendar.getTimeInMillis();
}
private long addDays(long time, int days) {
mCalendar.setTimeInMillis(time);
mCalendar.add(Calendar.DATE, days);
return mCalendar.getTimeInMillis();
}
private void updateAlarm(String action, int requestCode, int hr, int min) {
final AlarmManager alarms = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
final long now = System.currentTimeMillis();
mCalendar.setTimeInMillis(now);
mCalendar.set(Calendar.HOUR_OF_DAY, hr);
mCalendar.set(Calendar.MINUTE, min);
mCalendar.set(Calendar.SECOND, 0);
mCalendar.set(Calendar.MILLISECOND, 0);
long time = mCalendar.getTimeInMillis();
if (time <= now) {
time = addDays(time, 1);
}
final long time = mCalendar.getNextTime(now, hr, min);
final PendingIntent pendingIntent = PendingIntent.getBroadcast(mContext, requestCode,
new Intent(action)
.addFlags(Intent.FLAG_RECEIVER_FOREGROUND)
@@ -311,31 +298,34 @@ public class DowntimeConditionProvider extends ConditionProviderService {
private void onEvaluateNextAlarm(AlarmClockInfo nextAlarm, long wakeupTime, boolean booted) {
if (!booted) return; // we don't know yet
// update condition description if we're in downtime (mode = none)
if (isInDowntime() && mConfig != null && mConfig.sleepNone) {
notifyCondition(createCondition(mConfig.toDowntimeInfo(), true /*orAlarm*/,
Condition.STATE_TRUE));
}
if (nextAlarm == null) return; // not fireable
if (DEBUG) Slog.d(TAG, "onEvaluateNextAlarm " + mTracker.formatAlarmDebug(nextAlarm));
if (System.currentTimeMillis() > wakeupTime) {
if (nextAlarm != null && wakeupTime > 0 && System.currentTimeMillis() > wakeupTime) {
if (DEBUG) Slog.d(TAG, "Alarm fired: " + mTracker.formatAlarmDebug(wakeupTime));
trimFiredAlarms();
mFiredAlarms.add(wakeupTime);
}
reevaluateDowntime();
evaluateSubscriptions();
}
private void trimFiredAlarms() {
// remove fired alarms over 2 days old
final long keepAfter = System.currentTimeMillis() - 2 * 24 * 60 * 60 * 1000;
final int N = mFiredAlarms.size();
for (int i = N - 1; i >= 0; i--) {
final long firedAlarm = mFiredAlarms.valueAt(i);
if (firedAlarm < keepAfter) {
mFiredAlarms.removeAt(i);
}
private void evaluateAutotrigger() {
String skipReason = null;
if (mConfig == null) {
skipReason = "no config";
} else if (mDowntimed) {
skipReason = "already downtimed";
} else if (mZenModeHelper.getZenMode() != Global.ZEN_MODE_OFF) {
skipReason = "already in zen";
} else if (!mCalendar.isInDowntime(System.currentTimeMillis())) {
skipReason = "not in downtime";
}
if (skipReason != null) {
ZenLog.traceDowntimeAutotrigger("Autotrigger skipped: " + skipReason);
return;
}
ZenLog.traceDowntimeAutotrigger("Autotrigger fired");
mZenModeHelper.setZenMode(mConfig.sleepNone ? Global.ZEN_MODE_NO_INTERRUPTIONS
: Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS, "downtime");
final Condition condition = createCondition(mConfig.toDowntimeInfo(), Condition.STATE_TRUE);
mConditionProviders.setZenModeCondition(condition, "downtime");
}
private BroadcastReceiver mReceiver = new BroadcastReceiver() {
@@ -347,6 +337,12 @@ public class DowntimeConditionProvider extends ConditionProviderService {
final long schTime = intent.getLongExtra(EXTRA_TIME, 0);
if (DEBUG) Slog.d(TAG, String.format("%s scheduled for %s, fired at %s, delta=%s",
action, ts(schTime), ts(now), now - schTime));
if (ENTER_ACTION.equals(action)) {
evaluateAutotrigger();
} else /*EXIT_ACTION*/ {
mDowntimed = false;
}
mFiredAlarms.clear();
} else if (Intent.ACTION_TIMEZONE_CHANGED.equals(action)) {
if (DEBUG) Slog.d(TAG, "timezone changed to " + TimeZone.getDefault());
mCalendar.setTimeZone(TimeZone.getDefault());
@@ -357,7 +353,7 @@ public class DowntimeConditionProvider extends ConditionProviderService {
} else {
if (DEBUG) Slog.d(TAG, action + " fired at " + now);
}
reevaluateDowntime();
evaluateSubscriptions();
updateAlarms();
}
};
@@ -369,8 +365,45 @@ public class DowntimeConditionProvider extends ConditionProviderService {
}
};
public interface Callback {
void onDowntimeChanged(int downtimeMode);
NextAlarmTracker getNextAlarmTracker();
private final ZenModeHelper.Callback mZenCallback = new ZenModeHelper.Callback() {
@Override
void onZenModeChanged() {
if (mConditionClearing && isZenOff()) {
evaluateAutotrigger();
}
mConditionClearing = false;
evaluateSubscriptions();
}
};
private class FiredAlarms {
private final ArraySet<Long> mFiredAlarms = new ArraySet<Long>();
@Override
public String toString() {
final StringBuilder sb = new StringBuilder();
for (int i = 0; i < mFiredAlarms.size(); i++) {
if (i > 0) sb.append(',');
sb.append(mTracker.formatAlarmDebug(mFiredAlarms.valueAt(i)));
}
return sb.toString();
}
public void add(long firedAlarm) {
mFiredAlarms.add(firedAlarm);
}
public void clear() {
mFiredAlarms.clear();
}
public boolean findBefore(long time) {
for (int i = 0; i < mFiredAlarms.size(); i++) {
if (mFiredAlarms.valueAt(i) < time) {
return true;
}
}
return false;
}
}
}

View File

@@ -25,6 +25,8 @@ import android.service.notification.Condition;
import android.service.notification.ConditionProviderService;
import android.service.notification.IConditionProvider;
import android.service.notification.ZenModeConfig;
import android.text.TextUtils;
import android.util.ArraySet;
import android.util.Log;
import android.util.Slog;
import android.util.TimeUtils;
@@ -38,10 +40,7 @@ import java.io.PrintWriter;
* Built-in zen condition provider for alarm-clock-based conditions.
*
* <p>If the user's next alarm is within a lookahead threshold (config, default 12hrs), advertise
* it as an exit condition for zen mode (unless the built-in downtime condition is also available).
*
* <p>When this next alarm is selected as the active exit condition, follow subsequent changes
* to the user's next alarm, assuming it remains within the 12-hr window.
* it as an exit condition for zen mode.
*
* <p>The next alarm is defined as {@link AlarmManager#getNextAlarmClock(int)}, which does not
* survive a reboot. Maintain the illusion of a consistent next alarm value by holding on to
@@ -55,20 +54,22 @@ public class NextAlarmConditionProvider extends ConditionProviderService {
private static final long MINUTES = 60 * SECONDS;
private static final long HOURS = 60 * MINUTES;
private static final String NEXT_ALARM_PATH = "next_alarm";
private static final long BAD_CONDITION = -1;
public static final ComponentName COMPONENT =
new ComponentName("android", NextAlarmConditionProvider.class.getName());
private final Context mContext = this;
private final NextAlarmTracker mTracker;
private final ArraySet<Uri> mSubscriptions = new ArraySet<Uri>();
private NextAlarmTracker mTracker;
private boolean mConnected;
private long mLookaheadThreshold;
private Callback mCallback;
private Uri mCurrentSubscription;
private boolean mRequesting;
public NextAlarmConditionProvider() {
public NextAlarmConditionProvider(NextAlarmTracker tracker) {
if (DEBUG) Slog.d(TAG, "new NextAlarmConditionProvider()");
mTracker = tracker;
}
public void dump(PrintWriter pw, DumpFilter filter) {
@@ -76,20 +77,16 @@ public class NextAlarmConditionProvider extends ConditionProviderService {
pw.print(" mConnected="); pw.println(mConnected);
pw.print(" mLookaheadThreshold="); pw.print(mLookaheadThreshold);
pw.print(" ("); TimeUtils.formatDuration(mLookaheadThreshold, pw); pw.println(")");
pw.print(" mCurrentSubscription="); pw.println(mCurrentSubscription);
}
public void setCallback(Callback callback) {
mCallback = callback;
pw.print(" mSubscriptions="); pw.println(mSubscriptions);
pw.print(" mRequesting="); pw.println(mRequesting);
}
@Override
public void onConnected() {
if (DEBUG) Slog.d(TAG, "onConnected");
mLookaheadThreshold = mContext.getResources()
.getInteger(R.integer.config_next_alarm_condition_lookahead_threshold_hrs) * HOURS;
mLookaheadThreshold = PropConfig.getInt(mContext, "nextalarm.condition.lookahead",
R.integer.config_next_alarm_condition_lookahead_threshold_hrs) * HOURS;
mConnected = true;
mTracker = mCallback.getNextAlarmTracker();
mTracker.addCallback(mTrackerCallback);
}
@@ -103,34 +100,27 @@ public class NextAlarmConditionProvider extends ConditionProviderService {
@Override
public void onRequestConditions(int relevance) {
if (!mConnected || (relevance & Condition.FLAG_RELEVANT_NOW) == 0) return;
final AlarmClockInfo nextAlarm = mTracker.getNextAlarm();
if (nextAlarm == null) return; // no next alarm
if (mCallback != null && mCallback.isInDowntime()) return; // prefer downtime condition
if (!isWithinLookaheadThreshold(nextAlarm)) return; // alarm not within window
// next alarm exists, and is within the configured lookahead threshold
notifyCondition(newConditionId(), nextAlarm, Condition.STATE_TRUE, "request");
if (DEBUG) Slog.d(TAG, "onRequestConditions relevance=" + relevance);
if (!mConnected) return;
mRequesting = (relevance & Condition.FLAG_RELEVANT_NOW) != 0;
mTracker.evaluate();
}
@Override
public void onSubscribe(Uri conditionId) {
if (DEBUG) Slog.d(TAG, "onSubscribe " + conditionId);
if (!isNextAlarmCondition(conditionId)) {
if (tryParseNextAlarmCondition(conditionId) == BAD_CONDITION) {
notifyCondition(conditionId, null, Condition.STATE_FALSE, "badCondition");
return;
}
mCurrentSubscription = conditionId;
mSubscriptions.add(conditionId);
mTracker.evaluate();
}
@Override
public void onUnsubscribe(Uri conditionId) {
if (DEBUG) Slog.d(TAG, "onUnsubscribe " + conditionId);
if (conditionId != null && conditionId.equals(mCurrentSubscription)) {
mCurrentSubscription = null;
}
mSubscriptions.remove(conditionId);
}
public void attachBase(Context base) {
@@ -157,43 +147,72 @@ public class NextAlarmConditionProvider extends ConditionProviderService {
formattedAlarm, 0, state, Condition.FLAG_RELEVANT_NOW));
}
private Uri newConditionId() {
private Uri newConditionId(AlarmClockInfo nextAlarm) {
return new Uri.Builder().scheme(Condition.SCHEME)
.authority(ZenModeConfig.SYSTEM_AUTHORITY)
.appendPath(NEXT_ALARM_PATH)
.appendPath(ZenModeConfig.NEXT_ALARM_PATH)
.appendPath(Integer.toString(mTracker.getCurrentUserId()))
.appendPath(Long.toString(nextAlarm.getTriggerTime()))
.build();
}
private boolean isNextAlarmCondition(Uri conditionId) {
private long tryParseNextAlarmCondition(Uri conditionId) {
return conditionId != null && conditionId.getScheme().equals(Condition.SCHEME)
&& conditionId.getAuthority().equals(ZenModeConfig.SYSTEM_AUTHORITY)
&& conditionId.getPathSegments().size() == 2
&& conditionId.getPathSegments().get(0).equals(NEXT_ALARM_PATH)
&& conditionId.getPathSegments().size() == 3
&& conditionId.getPathSegments().get(0).equals(ZenModeConfig.NEXT_ALARM_PATH)
&& conditionId.getPathSegments().get(1)
.equals(Integer.toString(mTracker.getCurrentUserId()));
.equals(Integer.toString(mTracker.getCurrentUserId()))
? tryParseLong(conditionId.getPathSegments().get(2), BAD_CONDITION)
: BAD_CONDITION;
}
private static long tryParseLong(String value, long defValue) {
if (TextUtils.isEmpty(value)) return defValue;
try {
return Long.valueOf(value);
} catch (NumberFormatException e) {
return defValue;
}
}
private void onEvaluate(AlarmClockInfo nextAlarm, long wakeupTime, boolean booted) {
final boolean withinThreshold = isWithinLookaheadThreshold(nextAlarm);
if (DEBUG) Slog.d(TAG, "onEvaluate mCurrentSubscription=" + mCurrentSubscription
final long nextAlarmTime = nextAlarm != null ? nextAlarm.getTriggerTime() : 0;
if (DEBUG) Slog.d(TAG, "onEvaluate mSubscriptions=" + mSubscriptions
+ " nextAlarmTime=" + mTracker.formatAlarmDebug(nextAlarmTime)
+ " nextAlarmWakeup=" + mTracker.formatAlarmDebug(wakeupTime)
+ " withinThreshold=" + withinThreshold
+ " booted=" + booted);
if (mCurrentSubscription == null) return; // no one cares
if (!booted) {
// we don't know yet
notifyCondition(mCurrentSubscription, nextAlarm, Condition.STATE_UNKNOWN, "!booted");
return;
ArraySet<Uri> conditions = mSubscriptions;
if (mRequesting && nextAlarm != null && withinThreshold) {
final Uri id = newConditionId(nextAlarm);
if (!conditions.contains(id)) {
conditions = new ArraySet<Uri>(conditions);
conditions.add(id);
}
}
if (!withinThreshold) {
// next alarm outside threshold or in the past, condition = false
notifyCondition(mCurrentSubscription, nextAlarm, Condition.STATE_FALSE, "!within");
mCurrentSubscription = null;
return;
for (Uri conditionId : conditions) {
final long time = tryParseNextAlarmCondition(conditionId);
if (time == BAD_CONDITION) {
notifyCondition(conditionId, nextAlarm, Condition.STATE_FALSE, "badCondition");
} else if (!booted) {
// we don't know yet
if (mSubscriptions.contains(conditionId)) {
notifyCondition(conditionId, nextAlarm, Condition.STATE_UNKNOWN, "!booted");
}
} else if (time != nextAlarmTime) {
// next alarm changed since subscription, consider obsolete
notifyCondition(conditionId, nextAlarm, Condition.STATE_FALSE, "changed");
} else if (!withinThreshold) {
// next alarm outside threshold or in the past, condition = false
notifyCondition(conditionId, nextAlarm, Condition.STATE_FALSE, "!within");
} else {
// next alarm within threshold and in the future, condition = true
notifyCondition(conditionId, nextAlarm, Condition.STATE_TRUE, "within");
}
}
// next alarm in the future and within threshold, condition = true
notifyCondition(mCurrentSubscription, nextAlarm, Condition.STATE_TRUE, "within");
}
private final NextAlarmTracker.Callback mTrackerCallback = new NextAlarmTracker.Callback() {
@@ -202,9 +221,4 @@ public class NextAlarmConditionProvider extends ConditionProviderService {
NextAlarmConditionProvider.this.onEvaluate(nextAlarm, wakeupTime, booted);
}
};
public interface Callback {
boolean isInDowntime();
NextAlarmTracker getNextAlarmTracker();
}
}

View File

@@ -1551,6 +1551,12 @@ public class NotificationManagerService extends SystemService {
MATCHES_CALL_FILTER_CONTACTS_TIMEOUT_MS,
MATCHES_CALL_FILTER_TIMEOUT_AFFINITY);
}
@Override
public boolean isSystemConditionProviderEnabled(String path) {
enforceSystemOrSystemUI("INotificationManager.isSystemConditionProviderEnabled");
return mConditionProviders.isSystemConditionProviderEnabled(path);
}
};
private String[] getActiveNotificationKeys(INotificationListener token) {

View File

@@ -0,0 +1,33 @@
/*
* Copyright (C) 2014 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.server.notification;
import android.content.Context;
import android.os.SystemProperties;
public class PropConfig {
private static final String UNSET = "UNSET";
public static int getInt(Context context, String propName, int resId) {
return SystemProperties.getInt(propName, context.getResources().getInteger(resId));
}
public static String[] getStringArray(Context context, String propName, int resId) {
final String prop = SystemProperties.get(propName, UNSET);
return !UNSET.equals(prop) ? prop.split(",") : context.getResources().getStringArray(resId);
}
}

View File

@@ -89,12 +89,12 @@ public class ZenLog {
ringerModeToString(ringerModeExternalOut));
}
public static void traceDowntime(int downtimeMode, int day, ArraySet<Integer> days) {
append(TYPE_DOWNTIME, zenModeToString(downtimeMode) + ",day=" + day + ",days=" + days);
public static void traceDowntimeAutotrigger(String result) {
append(TYPE_DOWNTIME, result);
}
public static void traceSetZenMode(int mode, String reason) {
append(TYPE_SET_ZEN_MODE, zenModeToString(mode) + "," + reason);
public static void traceSetZenMode(int zenMode, String reason) {
append(TYPE_SET_ZEN_MODE, zenModeToString(zenMode) + "," + reason);
}
public static void traceUpdateZenMode(int fromMode, int toMode) {

View File

@@ -108,6 +108,10 @@ public class ZenModeHelper implements AudioManagerInternal.RingerModeDelegate {
mCallbacks.add(callback);
}
public void removeCallback(Callback callback) {
mCallbacks.remove(callback);
}
public void onSystemReady() {
mAudioManager = LocalServices.getService(AudioManagerInternal.class);
if (mAudioManager != null) {
@@ -208,9 +212,9 @@ public class ZenModeHelper implements AudioManagerInternal.RingerModeDelegate {
return mZenMode;
}
public void setZenMode(int zenModeValue, String reason) {
ZenLog.traceSetZenMode(zenModeValue, reason);
Global.putInt(mContext.getContentResolver(), Global.ZEN_MODE, zenModeValue);
public void setZenMode(int zenMode, String reason) {
ZenLog.traceSetZenMode(zenMode, reason);
Global.putInt(mContext.getContentResolver(), Global.ZEN_MODE, zenMode);
}
public void updateZenMode() {