Merge "Do not disturb: persist user config."
This commit is contained in:
committed by
Android (Google) Code Review
commit
860107a2cc
@@ -23,6 +23,7 @@ import android.app.Notification;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Intent;
|
||||
import android.service.notification.INotificationListener;
|
||||
import android.service.notification.ZenModeConfig;
|
||||
|
||||
/** {@hide} */
|
||||
interface INotificationManager
|
||||
@@ -49,4 +50,7 @@ interface INotificationManager
|
||||
|
||||
StatusBarNotification[] getActiveNotificationsFromListener(in INotificationListener token, in String[] keys);
|
||||
String[] getActiveNotificationKeysFromListener(in INotificationListener token);
|
||||
|
||||
ZenModeConfig getZenModeConfig();
|
||||
boolean setZenModeConfig(in ZenModeConfig config);
|
||||
}
|
||||
@@ -6119,6 +6119,13 @@ public final class Settings {
|
||||
return "ZEN_MODE_ON";
|
||||
}
|
||||
|
||||
/**
|
||||
* Opaque value, changes when persisted zen mode configuration changes.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public static final String ZEN_MODE_CONFIG_ETAG = "zen_mode_config_etag";
|
||||
|
||||
/**
|
||||
* Defines global heads up toggle. One of HEADS_UP_OFF, HEADS_UP_ON.
|
||||
*
|
||||
|
||||
20
core/java/android/service/notification/ZenModeConfig.aidl
Normal file
20
core/java/android/service/notification/ZenModeConfig.aidl
Normal file
@@ -0,0 +1,20 @@
|
||||
/*
|
||||
* 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 android.service.notification;
|
||||
|
||||
parcelable ZenModeConfig;
|
||||
|
||||
234
core/java/android/service/notification/ZenModeConfig.java
Normal file
234
core/java/android/service/notification/ZenModeConfig.java
Normal file
@@ -0,0 +1,234 @@
|
||||
/**
|
||||
* 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 android.service.notification;
|
||||
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import org.xmlpull.v1.XmlPullParser;
|
||||
import org.xmlpull.v1.XmlPullParserException;
|
||||
import org.xmlpull.v1.XmlSerializer;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* Persisted configuration for zen mode.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public class ZenModeConfig implements Parcelable {
|
||||
|
||||
public static final String SLEEP_MODE_NIGHTS = "nights";
|
||||
public static final String SLEEP_MODE_WEEKNIGHTS = "weeknights";
|
||||
|
||||
private static final int XML_VERSION = 1;
|
||||
private static final String ZEN_TAG = "zen";
|
||||
private static final String ZEN_ATT_VERSION = "version";
|
||||
private static final String ALLOW_TAG = "allow";
|
||||
private static final String ALLOW_ATT_CALLS = "calls";
|
||||
private static final String ALLOW_ATT_MESSAGES = "messages";
|
||||
private static final String SLEEP_TAG = "sleep";
|
||||
private static final String SLEEP_ATT_MODE = "mode";
|
||||
|
||||
private static final String SLEEP_ATT_START_HR = "startHour";
|
||||
private static final String SLEEP_ATT_START_MIN = "startMin";
|
||||
private static final String SLEEP_ATT_END_HR = "endHour";
|
||||
private static final String SLEEP_ATT_END_MIN = "endMin";
|
||||
|
||||
public boolean allowCalls;
|
||||
public boolean allowMessages;
|
||||
|
||||
public String sleepMode;
|
||||
public int sleepStartHour;
|
||||
public int sleepStartMinute;
|
||||
public int sleepEndHour;
|
||||
public int sleepEndMinute;
|
||||
|
||||
public ZenModeConfig() { }
|
||||
|
||||
public ZenModeConfig(Parcel source) {
|
||||
allowCalls = source.readInt() == 1;
|
||||
allowMessages = source.readInt() == 1;
|
||||
if (source.readInt() == 1) {
|
||||
sleepMode = source.readString();
|
||||
}
|
||||
sleepStartHour = source.readInt();
|
||||
sleepStartMinute = source.readInt();
|
||||
sleepEndHour = source.readInt();
|
||||
sleepEndMinute = source.readInt();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToParcel(Parcel dest, int flags) {
|
||||
dest.writeInt(allowCalls ? 1 : 0);
|
||||
dest.writeInt(allowMessages ? 1 : 0);
|
||||
if (sleepMode != null) {
|
||||
dest.writeInt(1);
|
||||
dest.writeString(sleepMode);
|
||||
} else {
|
||||
dest.writeInt(0);
|
||||
}
|
||||
dest.writeInt(sleepStartHour);
|
||||
dest.writeInt(sleepStartMinute);
|
||||
dest.writeInt(sleepEndHour);
|
||||
dest.writeInt(sleepEndMinute);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return new StringBuilder(ZenModeConfig.class.getSimpleName()).append('[')
|
||||
.append("allowCalls=").append(allowCalls)
|
||||
.append(",allowMessages=").append(allowMessages)
|
||||
.append(",sleepMode=").append(sleepMode)
|
||||
.append(",sleepStart=").append(sleepStartHour).append('.').append(sleepStartMinute)
|
||||
.append(",sleepEnd=").append(sleepEndHour).append('.').append(sleepEndMinute)
|
||||
.append(']').toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (!(o instanceof ZenModeConfig)) return false;
|
||||
if (o == this) return true;
|
||||
final ZenModeConfig other = (ZenModeConfig) o;
|
||||
return other.allowCalls == allowCalls
|
||||
&& other.allowMessages == allowMessages
|
||||
&& Objects.equals(other.sleepMode, sleepMode)
|
||||
&& other.sleepStartHour == sleepStartHour
|
||||
&& other.sleepStartMinute == sleepStartMinute
|
||||
&& other.sleepEndHour == sleepEndHour
|
||||
&& other.sleepEndMinute == sleepEndMinute;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(allowCalls, allowMessages, sleepMode, sleepStartHour,
|
||||
sleepStartMinute, sleepEndHour, sleepEndMinute);
|
||||
}
|
||||
|
||||
public boolean isValid() {
|
||||
return isValidHour(sleepStartHour) && isValidMinute(sleepStartMinute)
|
||||
&& isValidHour(sleepEndHour) && isValidMinute(sleepEndMinute)
|
||||
&& (sleepMode == null || sleepMode.equals(SLEEP_MODE_NIGHTS)
|
||||
|| sleepMode.equals(SLEEP_MODE_WEEKNIGHTS));
|
||||
}
|
||||
|
||||
public static ZenModeConfig readXml(XmlPullParser parser)
|
||||
throws XmlPullParserException, IOException {
|
||||
int type = parser.getEventType();
|
||||
if (type != XmlPullParser.START_TAG) return null;
|
||||
String tag = parser.getName();
|
||||
if (!ZEN_TAG.equals(tag)) return null;
|
||||
final ZenModeConfig rt = new ZenModeConfig();
|
||||
final int version = Integer.parseInt(parser.getAttributeValue(null, ZEN_ATT_VERSION));
|
||||
while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) {
|
||||
tag = parser.getName();
|
||||
if (type == XmlPullParser.END_TAG && ZEN_TAG.equals(tag)) return rt;
|
||||
if (type == XmlPullParser.START_TAG) {
|
||||
if (ALLOW_TAG.equals(tag)) {
|
||||
rt.allowCalls = safeBoolean(parser, ALLOW_ATT_CALLS, false);
|
||||
rt.allowMessages = safeBoolean(parser, ALLOW_ATT_MESSAGES, false);
|
||||
} else if (SLEEP_TAG.equals(tag)) {
|
||||
final String mode = parser.getAttributeValue(null, SLEEP_ATT_MODE);
|
||||
rt.sleepMode = (SLEEP_MODE_NIGHTS.equals(mode)
|
||||
|| SLEEP_MODE_WEEKNIGHTS.equals(mode)) ? mode : null;
|
||||
final int startHour = safeInt(parser, SLEEP_ATT_START_HR, 0);
|
||||
final int startMinute = safeInt(parser, SLEEP_ATT_START_MIN, 0);
|
||||
final int endHour = safeInt(parser, SLEEP_ATT_END_HR, 0);
|
||||
final int endMinute = safeInt(parser, SLEEP_ATT_END_MIN, 0);
|
||||
rt.sleepStartHour = isValidHour(startHour) ? startHour : 0;
|
||||
rt.sleepStartMinute = isValidMinute(startMinute) ? startMinute : 0;
|
||||
rt.sleepEndHour = isValidHour(endHour) ? endHour : 0;
|
||||
rt.sleepEndMinute = isValidMinute(endMinute) ? endMinute : 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
return rt;
|
||||
}
|
||||
|
||||
public void writeXml(XmlSerializer out) throws IOException {
|
||||
out.startTag(null, ZEN_TAG);
|
||||
out.attribute(null, ZEN_ATT_VERSION, Integer.toString(XML_VERSION));
|
||||
|
||||
out.startTag(null, ALLOW_TAG);
|
||||
out.attribute(null, ALLOW_ATT_CALLS, Boolean.toString(allowCalls));
|
||||
out.attribute(null, ALLOW_ATT_MESSAGES, Boolean.toString(allowMessages));
|
||||
out.endTag(null, ALLOW_TAG);
|
||||
|
||||
out.startTag(null, SLEEP_TAG);
|
||||
if (sleepMode != null) {
|
||||
out.attribute(null, SLEEP_ATT_MODE, sleepMode);
|
||||
}
|
||||
out.attribute(null, SLEEP_ATT_START_HR, Integer.toString(sleepStartHour));
|
||||
out.attribute(null, SLEEP_ATT_START_MIN, Integer.toString(sleepStartMinute));
|
||||
out.attribute(null, SLEEP_ATT_END_HR, Integer.toString(sleepEndHour));
|
||||
out.attribute(null, SLEEP_ATT_END_MIN, Integer.toString(sleepEndMinute));
|
||||
out.endTag(null, SLEEP_TAG);
|
||||
|
||||
out.endTag(null, ZEN_TAG);
|
||||
}
|
||||
|
||||
public static boolean isValidHour(int val) {
|
||||
return val >= 0 && val < 24;
|
||||
}
|
||||
|
||||
public static boolean isValidMinute(int val) {
|
||||
return val >= 0 && val < 60;
|
||||
}
|
||||
|
||||
private static boolean safeBoolean(XmlPullParser parser, String att, boolean defValue) {
|
||||
final String val = parser.getAttributeValue(null, att);
|
||||
if (TextUtils.isEmpty(val)) return defValue;
|
||||
return Boolean.valueOf(val);
|
||||
}
|
||||
|
||||
private static int safeInt(XmlPullParser parser, String att, int defValue) {
|
||||
final String val = parser.getAttributeValue(null, att);
|
||||
if (TextUtils.isEmpty(val)) return defValue;
|
||||
return Integer.valueOf(val);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
public ZenModeConfig copy() {
|
||||
final Parcel parcel = Parcel.obtain();
|
||||
try {
|
||||
writeToParcel(parcel, 0);
|
||||
parcel.setDataPosition(0);
|
||||
return new ZenModeConfig(parcel);
|
||||
} finally {
|
||||
parcel.recycle();
|
||||
}
|
||||
}
|
||||
|
||||
public static final Parcelable.Creator<ZenModeConfig> CREATOR
|
||||
= new Parcelable.Creator<ZenModeConfig>() {
|
||||
@Override
|
||||
public ZenModeConfig createFromParcel(Parcel source) {
|
||||
return new ZenModeConfig(source);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ZenModeConfig[] newArray(int size) {
|
||||
return new ZenModeConfig[size];
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -1230,6 +1230,7 @@
|
||||
<java-symbol type="xml" name="sms_short_codes" />
|
||||
<java-symbol type="xml" name="audio_assets" />
|
||||
<java-symbol type="xml" name="global_keys" />
|
||||
<java-symbol type="xml" name="default_zen_mode_config" />
|
||||
|
||||
<java-symbol type="raw" name="accessibility_gestures" />
|
||||
<java-symbol type="raw" name="incognito_mode_start_page" />
|
||||
|
||||
24
core/res/res/xml/default_zen_mode_config.xml
Normal file
24
core/res/res/xml/default_zen_mode_config.xml
Normal file
@@ -0,0 +1,24 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
/*
|
||||
** Copyright 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.
|
||||
*/
|
||||
-->
|
||||
|
||||
<!-- Default configuration for zen mode. See android.service.notification.ZenModeConfig. -->
|
||||
<zen version="1">
|
||||
<allow calls="false" messages="false" />
|
||||
<sleep startHour="22" startMin="0" endHour="7" endMin="0" />
|
||||
</zen>
|
||||
@@ -539,6 +539,9 @@
|
||||
<!-- Zen mode: Summary notification content text. [CHAR LIMIT=NONE] -->
|
||||
<string name="zen_mode_notification_text">Touch to show</string>
|
||||
|
||||
<!-- Zen mode: Short title. [CHAR LIMIT=40] -->
|
||||
<string name="zen_mode_title">Do not disturb</string>
|
||||
|
||||
<!-- Text for overflow card on Keyguard when there is not enough space for all notifications on Keyguard. [CHAR LIMIT=12] -->
|
||||
<plurals name="keyguard_more_overflow_text">
|
||||
<item quantity="other">%d more</item>
|
||||
|
||||
@@ -608,7 +608,7 @@ class QuickSettingsModel implements BluetoothStateChangeCallback,
|
||||
Settings.Global.ZEN_MODE, Settings.Global.ZEN_MODE_OFF);
|
||||
mZenModeState.enabled = mode != Settings.Global.ZEN_MODE_OFF;
|
||||
mZenModeState.zenMode = mode;
|
||||
mZenModeState.label = ZenModeView.MODE_LABEL;
|
||||
mZenModeState.label = mContext.getString(R.string.zen_mode_title);
|
||||
mZenModeState.iconId = R.drawable.stat_sys_zen_limited;
|
||||
mZenModeCallback.refreshView(mZenModeTile, mZenModeState);
|
||||
}
|
||||
|
||||
@@ -42,13 +42,13 @@ import android.widget.Switch;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.android.systemui.R;
|
||||
import com.android.systemui.statusbar.phone.ZenModeView.Adapter.ExitCondition;
|
||||
|
||||
public class ZenModeView extends RelativeLayout {
|
||||
private static final String TAG = ZenModeView.class.getSimpleName();
|
||||
private static final boolean DEBUG = false;
|
||||
|
||||
public static final String MODE_LABEL = "Limited interruptions";
|
||||
public static final int BACKGROUND = 0xff282828;
|
||||
|
||||
private static final Typeface CONDENSED =
|
||||
@@ -91,7 +91,7 @@ public class ZenModeView extends RelativeLayout {
|
||||
LayoutParams lp = null;
|
||||
|
||||
mModeText = new TextView(mContext);
|
||||
mModeText.setText(MODE_LABEL);
|
||||
mModeText.setText(R.string.zen_mode_title);
|
||||
mModeText.setId(android.R.id.title);
|
||||
mModeText.setTextColor(GRAY);
|
||||
mModeText.setTypeface(CONDENSED);
|
||||
|
||||
@@ -52,6 +52,7 @@ import android.media.IRingtonePlayer;
|
||||
import android.net.Uri;
|
||||
import android.os.Binder;
|
||||
import android.os.Build;
|
||||
import android.os.Environment;
|
||||
import android.os.Handler;
|
||||
import android.os.IBinder;
|
||||
import android.os.Message;
|
||||
@@ -64,6 +65,7 @@ import android.provider.Settings;
|
||||
import android.service.notification.INotificationListener;
|
||||
import android.service.notification.NotificationListenerService;
|
||||
import android.service.notification.StatusBarNotification;
|
||||
import android.service.notification.ZenModeConfig;
|
||||
import android.telephony.TelephonyManager;
|
||||
import android.text.TextUtils;
|
||||
import android.util.ArrayMap;
|
||||
@@ -78,6 +80,7 @@ import android.widget.Toast;
|
||||
|
||||
import com.android.internal.R;
|
||||
import com.android.internal.notification.NotificationScorer;
|
||||
import com.android.internal.util.FastXmlSerializer;
|
||||
import com.android.server.EventLogTags;
|
||||
import com.android.server.notification.NotificationUsageStats.SingleNotificationStats;
|
||||
import com.android.server.statusbar.StatusBarManagerInternal;
|
||||
@@ -87,11 +90,13 @@ import com.android.server.lights.LightsManager;
|
||||
|
||||
import org.xmlpull.v1.XmlPullParser;
|
||||
import org.xmlpull.v1.XmlPullParserException;
|
||||
import org.xmlpull.v1.XmlSerializer;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileDescriptor;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
import java.lang.reflect.Array;
|
||||
@@ -115,6 +120,7 @@ public class NotificationManagerService extends SystemService {
|
||||
|
||||
// message codes
|
||||
static final int MESSAGE_TIMEOUT = 2;
|
||||
static final int MESSAGE_SAVE_POLICY_FILE = 3;
|
||||
|
||||
static final int LONG_DELAY = 3500; // 3.5 seconds
|
||||
static final int SHORT_DELAY = 2000; // 2 seconds
|
||||
@@ -209,15 +215,6 @@ public class NotificationManagerService extends SystemService {
|
||||
|
||||
private final NotificationUsageStats mUsageStats = new NotificationUsageStats();
|
||||
|
||||
private int mZenMode;
|
||||
// temporary, until we update apps to provide metadata
|
||||
private static final Set<String> CALL_PACKAGES = new HashSet<String>(Arrays.asList(
|
||||
"com.google.android.dialer",
|
||||
"com.android.phone"
|
||||
));
|
||||
private static final Set<String> ALARM_PACKAGES = new HashSet<String>(Arrays.asList(
|
||||
"com.google.android.deskclock"
|
||||
));
|
||||
private static final String EXTRA_INTERCEPT = "android.intercept";
|
||||
|
||||
// Profiles of the current user.
|
||||
@@ -421,53 +418,82 @@ public class NotificationManagerService extends SystemService {
|
||||
|
||||
Archive mArchive = new Archive();
|
||||
|
||||
private void loadBlockDb() {
|
||||
synchronized(mBlockedPackages) {
|
||||
if (mPolicyFile == null) {
|
||||
File dir = new File("/data/system");
|
||||
mPolicyFile = new AtomicFile(new File(dir, "notification_policy.xml"));
|
||||
private void loadPolicyFile() {
|
||||
synchronized(mPolicyFile) {
|
||||
mBlockedPackages.clear();
|
||||
|
||||
mBlockedPackages.clear();
|
||||
FileInputStream infile = null;
|
||||
try {
|
||||
infile = mPolicyFile.openRead();
|
||||
final XmlPullParser parser = Xml.newPullParser();
|
||||
parser.setInput(infile, null);
|
||||
|
||||
FileInputStream infile = null;
|
||||
try {
|
||||
infile = mPolicyFile.openRead();
|
||||
final XmlPullParser parser = Xml.newPullParser();
|
||||
parser.setInput(infile, null);
|
||||
|
||||
int type;
|
||||
String tag;
|
||||
int version = DB_VERSION;
|
||||
while ((type = parser.next()) != END_DOCUMENT) {
|
||||
tag = parser.getName();
|
||||
if (type == START_TAG) {
|
||||
if (TAG_BODY.equals(tag)) {
|
||||
version = Integer.parseInt(
|
||||
parser.getAttributeValue(null, ATTR_VERSION));
|
||||
} else if (TAG_BLOCKED_PKGS.equals(tag)) {
|
||||
while ((type = parser.next()) != END_DOCUMENT) {
|
||||
tag = parser.getName();
|
||||
if (TAG_PACKAGE.equals(tag)) {
|
||||
mBlockedPackages.add(
|
||||
parser.getAttributeValue(null, ATTR_NAME));
|
||||
} else if (TAG_BLOCKED_PKGS.equals(tag) && type == END_TAG) {
|
||||
break;
|
||||
}
|
||||
int type;
|
||||
String tag;
|
||||
int version = DB_VERSION;
|
||||
while ((type = parser.next()) != END_DOCUMENT) {
|
||||
tag = parser.getName();
|
||||
if (type == START_TAG) {
|
||||
if (TAG_BODY.equals(tag)) {
|
||||
version = Integer.parseInt(
|
||||
parser.getAttributeValue(null, ATTR_VERSION));
|
||||
} else if (TAG_BLOCKED_PKGS.equals(tag)) {
|
||||
while ((type = parser.next()) != END_DOCUMENT) {
|
||||
tag = parser.getName();
|
||||
if (TAG_PACKAGE.equals(tag)) {
|
||||
mBlockedPackages.add(
|
||||
parser.getAttributeValue(null, ATTR_NAME));
|
||||
} else if (TAG_BLOCKED_PKGS.equals(tag) && type == END_TAG) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (FileNotFoundException e) {
|
||||
// No data yet
|
||||
} catch (IOException e) {
|
||||
Log.wtf(TAG, "Unable to read blocked notifications database", e);
|
||||
} catch (NumberFormatException e) {
|
||||
Log.wtf(TAG, "Unable to parse blocked notifications database", e);
|
||||
} catch (XmlPullParserException e) {
|
||||
Log.wtf(TAG, "Unable to parse blocked notifications database", e);
|
||||
} finally {
|
||||
IoUtils.closeQuietly(infile);
|
||||
mZenModeHelper.readXml(parser);
|
||||
}
|
||||
} catch (FileNotFoundException e) {
|
||||
// No data yet
|
||||
} catch (IOException e) {
|
||||
Log.wtf(TAG, "Unable to read notification policy", e);
|
||||
} catch (NumberFormatException e) {
|
||||
Log.wtf(TAG, "Unable to parse notification policy", e);
|
||||
} catch (XmlPullParserException e) {
|
||||
Log.wtf(TAG, "Unable to parse notification policy", e);
|
||||
} finally {
|
||||
IoUtils.closeQuietly(infile);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void savePolicyFile() {
|
||||
mHandler.removeMessages(MESSAGE_SAVE_POLICY_FILE);
|
||||
mHandler.sendEmptyMessage(MESSAGE_SAVE_POLICY_FILE);
|
||||
}
|
||||
|
||||
private void handleSavePolicyFile() {
|
||||
Slog.d(TAG, "handleSavePolicyFile");
|
||||
synchronized (mPolicyFile) {
|
||||
final FileOutputStream stream;
|
||||
try {
|
||||
stream = mPolicyFile.startWrite();
|
||||
} catch (IOException e) {
|
||||
Slog.w(TAG, "Failed to save policy file", e);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
final XmlSerializer out = new FastXmlSerializer();
|
||||
out.setOutput(stream, "utf-8");
|
||||
out.startDocument(null, true);
|
||||
out.startTag(null, TAG_BODY);
|
||||
out.attribute(null, ATTR_VERSION, Integer.toString(DB_VERSION));
|
||||
mZenModeHelper.writeXml(out);
|
||||
out.endTag(null, TAG_BODY);
|
||||
out.endDocument();
|
||||
mPolicyFile.finishWrite(stream);
|
||||
} catch (IOException e) {
|
||||
Slog.w(TAG, "Failed to save policy file, restoring backup", e);
|
||||
mPolicyFile.failWrite(stream);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1066,10 +1092,7 @@ public class NotificationManagerService extends SystemService {
|
||||
|
||||
@Override
|
||||
public boolean allowDisable(int what, IBinder token, String pkg) {
|
||||
if (isCall(pkg, null)) {
|
||||
return mZenMode == Settings.Global.ZEN_MODE_OFF;
|
||||
}
|
||||
return true;
|
||||
return mZenModeHelper.allowDisable(what, token, pkg);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -1194,9 +1217,6 @@ public class NotificationManagerService extends SystemService {
|
||||
private final Uri ENABLED_NOTIFICATION_LISTENERS_URI
|
||||
= Settings.Secure.getUriFor(Settings.Secure.ENABLED_NOTIFICATION_LISTENERS);
|
||||
|
||||
private final Uri ZEN_MODE
|
||||
= Settings.Global.getUriFor(Settings.Global.ZEN_MODE);
|
||||
|
||||
SettingsObserver(Handler handler) {
|
||||
super(handler);
|
||||
}
|
||||
@@ -1207,8 +1227,6 @@ public class NotificationManagerService extends SystemService {
|
||||
false, this, UserHandle.USER_ALL);
|
||||
resolver.registerContentObserver(ENABLED_NOTIFICATION_LISTENERS_URI,
|
||||
false, this, UserHandle.USER_ALL);
|
||||
resolver.registerContentObserver(ZEN_MODE,
|
||||
false, this);
|
||||
update(null);
|
||||
}
|
||||
|
||||
@@ -1229,13 +1247,11 @@ public class NotificationManagerService extends SystemService {
|
||||
if (uri == null || ENABLED_NOTIFICATION_LISTENERS_URI.equals(uri)) {
|
||||
rebindListenerServices();
|
||||
}
|
||||
if (ZEN_MODE.equals(uri)) {
|
||||
updateZenMode();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private SettingsObserver mSettingsObserver;
|
||||
private ZenModeHelper mZenModeHelper;
|
||||
|
||||
static long[] getLongArray(Resources r, int resid, int maxlen, long[] def) {
|
||||
int[] ar = r.getIntArray(resid);
|
||||
@@ -1261,6 +1277,15 @@ public class NotificationManagerService extends SystemService {
|
||||
mVibrator = (Vibrator) getContext().getSystemService(Context.VIBRATOR_SERVICE);
|
||||
|
||||
mHandler = new WorkerHandler();
|
||||
mZenModeHelper = new ZenModeHelper(getContext(), mHandler);
|
||||
mZenModeHelper.setCallback(new ZenModeHelper.Callback() {
|
||||
@Override
|
||||
public void onConfigChanged() {
|
||||
savePolicyFile();
|
||||
}
|
||||
});
|
||||
final File systemDir = new File(Environment.getDataDirectory(), "system");
|
||||
mPolicyFile = new AtomicFile(new File(systemDir, "notification_policy.xml"));
|
||||
|
||||
importOldBlockDb();
|
||||
|
||||
@@ -1297,7 +1322,7 @@ public class NotificationManagerService extends SystemService {
|
||||
Settings.Global.DEVICE_PROVISIONED, 0)) {
|
||||
mDisableNotificationAlerts = true;
|
||||
}
|
||||
updateZenMode();
|
||||
mZenModeHelper.updateZenMode();
|
||||
|
||||
updateCurrentProfilesCache(getContext());
|
||||
|
||||
@@ -1350,7 +1375,7 @@ public class NotificationManagerService extends SystemService {
|
||||
* Read the old XML-based app block database and import those blockages into the AppOps system.
|
||||
*/
|
||||
private void importOldBlockDb() {
|
||||
loadBlockDb();
|
||||
loadPolicyFile();
|
||||
|
||||
PackageManager pm = getContext().getPackageManager();
|
||||
for (String pkg : mBlockedPackages) {
|
||||
@@ -1363,9 +1388,6 @@ public class NotificationManagerService extends SystemService {
|
||||
}
|
||||
}
|
||||
mBlockedPackages.clear();
|
||||
if (mPolicyFile != null) {
|
||||
mPolicyFile.delete();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -1744,6 +1766,18 @@ public class NotificationManagerService extends SystemService {
|
||||
return NotificationManagerService.this.getActiveNotificationKeysFromListener(token);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ZenModeConfig getZenModeConfig() {
|
||||
checkCallerIsSystem();
|
||||
return mZenModeHelper.getConfig();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean setZenModeConfig(ZenModeConfig config) {
|
||||
checkCallerIsSystem();
|
||||
return mZenModeHelper.setConfig(config);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
|
||||
if (getContext().checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
|
||||
@@ -1825,7 +1859,6 @@ public class NotificationManagerService extends SystemService {
|
||||
pw.println(" mSoundNotification=" + mSoundNotification);
|
||||
pw.println(" mVibrateNotification=" + mVibrateNotification);
|
||||
pw.println(" mDisableNotificationAlerts=" + mDisableNotificationAlerts);
|
||||
pw.println(" mZenMode=" + Settings.Global.zenModeToString(mZenMode));
|
||||
pw.println(" mSystemReady=" + mSystemReady);
|
||||
pw.println(" mArchive=" + mArchive.toString());
|
||||
Iterator<StatusBarNotification> iter = mArchive.descendingIterator();
|
||||
@@ -1841,6 +1874,8 @@ public class NotificationManagerService extends SystemService {
|
||||
pw.println("\n Usage Stats:");
|
||||
mUsageStats.dump(pw, " ");
|
||||
|
||||
pw.println("\n Zen Mode:");
|
||||
mZenModeHelper.dump(pw, " ");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1973,7 +2008,7 @@ public class NotificationManagerService extends SystemService {
|
||||
}
|
||||
|
||||
// Is this notification intercepted by zen mode?
|
||||
final boolean intercept = shouldIntercept(pkg, notification);
|
||||
final boolean intercept = mZenModeHelper.shouldIntercept(pkg, notification);
|
||||
notification.extras.putBoolean(EXTRA_INTERCEPT, intercept);
|
||||
|
||||
// Should this notification make noise, vibe, or use the LED?
|
||||
@@ -2358,6 +2393,9 @@ public class NotificationManagerService extends SystemService {
|
||||
case MESSAGE_TIMEOUT:
|
||||
handleTimeout((ToastRecord)msg.obj);
|
||||
break;
|
||||
case MESSAGE_SAVE_POLICY_FILE:
|
||||
handleSavePolicyFile();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2722,42 +2760,6 @@ public class NotificationManagerService extends SystemService {
|
||||
}
|
||||
}
|
||||
|
||||
private void updateZenMode() {
|
||||
final int mode = Settings.Global.getInt(getContext().getContentResolver(),
|
||||
Settings.Global.ZEN_MODE, Settings.Global.ZEN_MODE_OFF);
|
||||
if (mode != mZenMode) {
|
||||
Slog.d(TAG, String.format("updateZenMode: %s -> %s",
|
||||
Settings.Global.zenModeToString(mZenMode),
|
||||
Settings.Global.zenModeToString(mode)));
|
||||
}
|
||||
mZenMode = mode;
|
||||
|
||||
final String[] exceptionPackages = null; // none (for now)
|
||||
|
||||
// call restrictions
|
||||
final boolean muteCalls = mZenMode != Settings.Global.ZEN_MODE_OFF;
|
||||
mAppOps.setRestriction(AppOpsManager.OP_VIBRATE, AudioManager.STREAM_RING,
|
||||
muteCalls ? AppOpsManager.MODE_IGNORED : AppOpsManager.MODE_ALLOWED,
|
||||
exceptionPackages);
|
||||
mAppOps.setRestriction(AppOpsManager.OP_PLAY_AUDIO, AudioManager.STREAM_RING,
|
||||
muteCalls ? AppOpsManager.MODE_IGNORED : AppOpsManager.MODE_ALLOWED,
|
||||
exceptionPackages);
|
||||
|
||||
// alarm restrictions
|
||||
final boolean muteAlarms = false; // TODO until we save user config
|
||||
mAppOps.setRestriction(AppOpsManager.OP_VIBRATE, AudioManager.STREAM_ALARM,
|
||||
muteAlarms ? AppOpsManager.MODE_IGNORED : AppOpsManager.MODE_ALLOWED,
|
||||
exceptionPackages);
|
||||
mAppOps.setRestriction(AppOpsManager.OP_PLAY_AUDIO, AudioManager.STREAM_ALARM,
|
||||
muteAlarms ? AppOpsManager.MODE_IGNORED : AppOpsManager.MODE_ALLOWED,
|
||||
exceptionPackages);
|
||||
|
||||
// restrict vibrations with no hints
|
||||
mAppOps.setRestriction(AppOpsManager.OP_VIBRATE, AudioManager.USE_DEFAULT_STREAM_TYPE,
|
||||
(muteAlarms || muteCalls) ? AppOpsManager.MODE_IGNORED : AppOpsManager.MODE_ALLOWED,
|
||||
exceptionPackages);
|
||||
}
|
||||
|
||||
private void updateCurrentProfilesCache(Context context) {
|
||||
UserManager userManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
|
||||
if (userManager != null) {
|
||||
@@ -2788,19 +2790,4 @@ public class NotificationManagerService extends SystemService {
|
||||
return mCurrentProfiles.get(userId) != null;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isCall(String pkg, Notification n) {
|
||||
return CALL_PACKAGES.contains(pkg);
|
||||
}
|
||||
|
||||
private boolean isAlarm(String pkg, Notification n) {
|
||||
return ALARM_PACKAGES.contains(pkg);
|
||||
}
|
||||
|
||||
private boolean shouldIntercept(String pkg, Notification n) {
|
||||
if (mZenMode != Settings.Global.ZEN_MODE_OFF) {
|
||||
return !isAlarm(pkg, n);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,312 @@
|
||||
/**
|
||||
* 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.app.AlarmManager;
|
||||
import android.app.AppOpsManager;
|
||||
import android.app.Notification;
|
||||
import android.app.PendingIntent;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.ContentResolver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.content.res.Resources;
|
||||
import android.content.res.XmlResourceParser;
|
||||
import android.database.ContentObserver;
|
||||
import android.media.AudioManager;
|
||||
import android.net.Uri;
|
||||
import android.os.Handler;
|
||||
import android.os.IBinder;
|
||||
import android.provider.Settings.Global;
|
||||
import android.service.notification.ZenModeConfig;
|
||||
import android.util.Slog;
|
||||
|
||||
import com.android.internal.R;
|
||||
|
||||
import libcore.io.IoUtils;
|
||||
|
||||
import org.xmlpull.v1.XmlPullParser;
|
||||
import org.xmlpull.v1.XmlPullParserException;
|
||||
import org.xmlpull.v1.XmlSerializer;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
import java.util.Arrays;
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* NotificationManagerService helper for functionality related to zen mode.
|
||||
*/
|
||||
public class ZenModeHelper {
|
||||
private static final String TAG = "ZenModeHelper";
|
||||
|
||||
private static final String ACTION_ENTER_ZEN = "enter_zen";
|
||||
private static final int REQUEST_CODE_ENTER = 100;
|
||||
private static final String ACTION_EXIT_ZEN = "exit_zen";
|
||||
private static final int REQUEST_CODE_EXIT = 101;
|
||||
private static final String EXTRA_TIME = "time";
|
||||
|
||||
private final Context mContext;
|
||||
private final Handler mHandler;
|
||||
private final SettingsObserver mSettingsObserver;
|
||||
private final AppOpsManager mAppOps;
|
||||
private final ZenModeConfig mDefaultConfig;
|
||||
|
||||
private Callback mCallback;
|
||||
private int mZenMode;
|
||||
private ZenModeConfig mConfig;
|
||||
|
||||
// temporary, until we update apps to provide metadata
|
||||
private static final Set<String> CALL_PACKAGES = new HashSet<String>(Arrays.asList(
|
||||
"com.google.android.dialer",
|
||||
"com.android.phone"
|
||||
));
|
||||
private static final Set<String> MESSAGE_PACKAGES = new HashSet<String>(Arrays.asList(
|
||||
"com.google.android.talk",
|
||||
"com.android.mms"
|
||||
));
|
||||
|
||||
public ZenModeHelper(Context context, Handler handler) {
|
||||
mContext = context;
|
||||
mHandler = handler;
|
||||
mAppOps = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
|
||||
mDefaultConfig = readDefaultConfig(context.getResources());
|
||||
mConfig = mDefaultConfig;
|
||||
mSettingsObserver = new SettingsObserver(mHandler);
|
||||
mSettingsObserver.observe();
|
||||
|
||||
final IntentFilter filter = new IntentFilter();
|
||||
filter.addAction(ACTION_ENTER_ZEN);
|
||||
filter.addAction(ACTION_EXIT_ZEN);
|
||||
mContext.registerReceiver(new ZenBroadcastReceiver(), filter);
|
||||
}
|
||||
|
||||
public static ZenModeConfig readDefaultConfig(Resources resources) {
|
||||
XmlResourceParser parser = null;
|
||||
try {
|
||||
parser = resources.getXml(R.xml.default_zen_mode_config);
|
||||
while (parser.next() != XmlPullParser.END_DOCUMENT) {
|
||||
final ZenModeConfig config = ZenModeConfig.readXml(parser);
|
||||
if (config != null) return config;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Slog.w(TAG, "Error reading default zen mode config from resource", e);
|
||||
} finally {
|
||||
IoUtils.closeQuietly(parser);
|
||||
}
|
||||
return new ZenModeConfig();
|
||||
}
|
||||
|
||||
public void setCallback(Callback callback) {
|
||||
mCallback = callback;
|
||||
}
|
||||
|
||||
public boolean shouldIntercept(String pkg, Notification n) {
|
||||
if (mZenMode != Global.ZEN_MODE_OFF) {
|
||||
if (isCall(pkg, n)) {
|
||||
return !mConfig.allowCalls;
|
||||
}
|
||||
if (isMessage(pkg, n)) {
|
||||
return !mConfig.allowMessages;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public void updateZenMode() {
|
||||
final int mode = Global.getInt(mContext.getContentResolver(),
|
||||
Global.ZEN_MODE, Global.ZEN_MODE_OFF);
|
||||
if (mode != mZenMode) {
|
||||
Slog.d(TAG, String.format("updateZenMode: %s -> %s",
|
||||
Global.zenModeToString(mZenMode),
|
||||
Global.zenModeToString(mode)));
|
||||
}
|
||||
mZenMode = mode;
|
||||
final boolean zen = mZenMode != Global.ZEN_MODE_OFF;
|
||||
final String[] exceptionPackages = null; // none (for now)
|
||||
|
||||
// call restrictions
|
||||
final boolean muteCalls = zen && !mConfig.allowCalls;
|
||||
mAppOps.setRestriction(AppOpsManager.OP_VIBRATE, AudioManager.STREAM_RING,
|
||||
muteCalls ? AppOpsManager.MODE_IGNORED : AppOpsManager.MODE_ALLOWED,
|
||||
exceptionPackages);
|
||||
mAppOps.setRestriction(AppOpsManager.OP_PLAY_AUDIO, AudioManager.STREAM_RING,
|
||||
muteCalls ? AppOpsManager.MODE_IGNORED : AppOpsManager.MODE_ALLOWED,
|
||||
exceptionPackages);
|
||||
|
||||
// restrict vibrations with no hints
|
||||
mAppOps.setRestriction(AppOpsManager.OP_VIBRATE, AudioManager.USE_DEFAULT_STREAM_TYPE,
|
||||
zen ? AppOpsManager.MODE_IGNORED : AppOpsManager.MODE_ALLOWED,
|
||||
exceptionPackages);
|
||||
}
|
||||
|
||||
public boolean allowDisable(int what, IBinder token, String pkg) {
|
||||
if (isCall(pkg, null)) {
|
||||
return mZenMode == Global.ZEN_MODE_OFF || mConfig.allowCalls;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public void dump(PrintWriter pw, String prefix) {
|
||||
pw.print(prefix); pw.print("mZenMode=");
|
||||
pw.println(Global.zenModeToString(mZenMode));
|
||||
pw.print(prefix); pw.print("mConfig="); pw.println(mConfig);
|
||||
pw.print(prefix); pw.print("mDefaultConfig="); pw.println(mDefaultConfig);
|
||||
}
|
||||
|
||||
public void readXml(XmlPullParser parser) throws XmlPullParserException, IOException {
|
||||
final ZenModeConfig config = ZenModeConfig.readXml(parser);
|
||||
if (config != null) {
|
||||
setConfig(config);
|
||||
}
|
||||
}
|
||||
|
||||
public void writeXml(XmlSerializer out) throws IOException {
|
||||
mConfig.writeXml(out);
|
||||
}
|
||||
|
||||
public ZenModeConfig getConfig() {
|
||||
return mConfig;
|
||||
}
|
||||
|
||||
public boolean setConfig(ZenModeConfig config) {
|
||||
if (config == null || !config.isValid()) return false;
|
||||
if (config.equals(mConfig)) return true;
|
||||
mConfig = config;
|
||||
Slog.d(TAG, "mConfig=" + mConfig);
|
||||
if (mCallback != null) mCallback.onConfigChanged();
|
||||
final String val = Integer.toString(mConfig.hashCode());
|
||||
Global.putString(mContext.getContentResolver(), Global.ZEN_MODE_CONFIG_ETAG, val);
|
||||
updateAlarms();
|
||||
updateZenMode();
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean isCall(String pkg, Notification n) {
|
||||
return CALL_PACKAGES.contains(pkg);
|
||||
}
|
||||
|
||||
private boolean isMessage(String pkg, Notification n) {
|
||||
return MESSAGE_PACKAGES.contains(pkg);
|
||||
}
|
||||
|
||||
private void updateAlarms() {
|
||||
updateAlarm(ACTION_ENTER_ZEN, REQUEST_CODE_ENTER,
|
||||
mConfig.sleepStartHour, mConfig.sleepStartMinute);
|
||||
updateAlarm(ACTION_EXIT_ZEN, REQUEST_CODE_EXIT,
|
||||
mConfig.sleepEndHour, mConfig.sleepEndMinute);
|
||||
}
|
||||
|
||||
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();
|
||||
final Calendar c = Calendar.getInstance();
|
||||
c.setTimeInMillis(now);
|
||||
c.set(Calendar.HOUR_OF_DAY, hr);
|
||||
c.set(Calendar.MINUTE, min);
|
||||
c.set(Calendar.SECOND, 0);
|
||||
c.set(Calendar.MILLISECOND, 0);
|
||||
if (c.getTimeInMillis() <= now) {
|
||||
c.add(Calendar.DATE, 1);
|
||||
}
|
||||
final long time = c.getTimeInMillis();
|
||||
final PendingIntent pendingIntent = PendingIntent.getBroadcast(mContext, requestCode,
|
||||
new Intent(action).putExtra(EXTRA_TIME, time), PendingIntent.FLAG_UPDATE_CURRENT);
|
||||
alarms.cancel(pendingIntent);
|
||||
if (mConfig.sleepMode != null) {
|
||||
Slog.d(TAG, String.format("Scheduling %s for %s, %s in the future, now=%s",
|
||||
action, ts(time), time - now, ts(now)));
|
||||
alarms.setExact(AlarmManager.RTC_WAKEUP, time, pendingIntent);
|
||||
}
|
||||
}
|
||||
|
||||
private static String ts(long time) {
|
||||
return new Date(time) + " (" + time + ")";
|
||||
}
|
||||
|
||||
public static boolean isWeekend(long time, int offsetDays) {
|
||||
final Calendar c = Calendar.getInstance();
|
||||
c.setTimeInMillis(time);
|
||||
if (offsetDays != 0) {
|
||||
c.add(Calendar.DATE, offsetDays);
|
||||
}
|
||||
final int day = c.get(Calendar.DAY_OF_WEEK);
|
||||
return day == Calendar.SATURDAY || day == Calendar.SUNDAY;
|
||||
}
|
||||
|
||||
private class SettingsObserver extends ContentObserver {
|
||||
private final Uri ZEN_MODE = Global.getUriFor(Global.ZEN_MODE);
|
||||
|
||||
public SettingsObserver(Handler handler) {
|
||||
super(handler);
|
||||
}
|
||||
|
||||
public void observe() {
|
||||
final ContentResolver resolver = mContext.getContentResolver();
|
||||
resolver.registerContentObserver(ZEN_MODE, false /*notifyForDescendents*/, this);
|
||||
update(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onChange(boolean selfChange, Uri uri) {
|
||||
update(uri);
|
||||
}
|
||||
|
||||
public void update(Uri uri) {
|
||||
if (ZEN_MODE.equals(uri)) {
|
||||
updateZenMode();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class ZenBroadcastReceiver extends BroadcastReceiver {
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
if (ACTION_ENTER_ZEN.equals(intent.getAction())) {
|
||||
setZenMode(intent, 1, Global.ZEN_MODE_ON);
|
||||
} else if (ACTION_EXIT_ZEN.equals(intent.getAction())) {
|
||||
setZenMode(intent, 0, Global.ZEN_MODE_OFF);
|
||||
}
|
||||
}
|
||||
|
||||
private void setZenMode(Intent intent, int wkendOffsetDays, int zenModeValue) {
|
||||
final long schTime = intent.getLongExtra(EXTRA_TIME, 0);
|
||||
final long now = System.currentTimeMillis();
|
||||
Slog.d(TAG, String.format("%s scheduled for %s, fired at %s, delta=%s",
|
||||
intent.getAction(), ts(schTime), ts(now), now - schTime));
|
||||
|
||||
final boolean skip = ZenModeConfig.SLEEP_MODE_WEEKNIGHTS.equals(mConfig.sleepMode) &&
|
||||
isWeekend(schTime, wkendOffsetDays);
|
||||
|
||||
if (skip) {
|
||||
Slog.d(TAG, "Skipping zen mode update for the weekend");
|
||||
} else {
|
||||
Global.putInt(mContext.getContentResolver(), Global.ZEN_MODE, zenModeValue);
|
||||
}
|
||||
updateAlarms();
|
||||
}
|
||||
}
|
||||
|
||||
public interface Callback {
|
||||
void onConfigChanged();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user