diff options
9 files changed, 270 insertions, 36 deletions
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 00c851b..293cf6f 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -919,6 +919,15 @@ public final class Settings { = "android.settings.ZEN_MODE_SCHEDULE_RULE_SETTINGS"; /** + * Activity Action: Show Zen Mode event rule configuration settings. + * + * @hide + */ + @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) + public static final String ACTION_ZEN_MODE_EVENT_RULE_SETTINGS + = "android.settings.ZEN_MODE_EVENT_RULE_SETTINGS"; + + /** * Activity Action: Show Zen Mode external rule configuration settings. * * @hide diff --git a/core/java/android/service/notification/ZenModeConfig.java b/core/java/android/service/notification/ZenModeConfig.java index dc8f3ea..f09f4d2 100644 --- a/core/java/android/service/notification/ZenModeConfig.java +++ b/core/java/android/service/notification/ZenModeConfig.java @@ -95,7 +95,7 @@ public class ZenModeConfig implements Parcelable { private static final String MANUAL_TAG = "manual"; private static final String AUTOMATIC_TAG = "automatic"; - private static final String RULE_ATT_ID = "id"; + private static final String RULE_ATT_ID = "ruleId"; private static final String RULE_ATT_ENABLED = "enabled"; private static final String RULE_ATT_SNOOZING = "snoozing"; private static final String RULE_ATT_NAME = "name"; @@ -279,6 +279,15 @@ public class ZenModeConfig implements Parcelable { } } + private static long tryParseLong(String value, long defValue) { + if (TextUtils.isEmpty(value)) return defValue; + try { + return Long.valueOf(value); + } catch (NumberFormatException e) { + return defValue; + } + } + public static ZenModeConfig readXml(XmlPullParser parser, Migration migration) throws XmlPullParserException, IOException { int type = parser.getEventType(); @@ -367,7 +376,7 @@ public class ZenModeConfig implements Parcelable { rt.conditionId = safeUri(parser, RULE_ATT_CONDITION_ID); rt.component = safeComponentName(parser, RULE_ATT_COMPONENT); rt.condition = readConditionXml(parser); - return rt.condition != null || !conditionRequired ? rt : null; + return rt; } public static void writeRuleXml(ZenRule rule, XmlSerializer out) throws IOException { @@ -568,10 +577,12 @@ public class ZenModeConfig implements Parcelable { Condition.FLAG_RELEVANT_NOW); } - // For built-in conditions + // ==== Built-in system conditions ==== + public static final String SYSTEM_AUTHORITY = "android"; - // Built-in countdown conditions, e.g. condition://android/countdown/1399917958951 + // ==== Built-in system condition: countdown ==== + public static final String COUNTDOWN_PATH = "countdown"; public static Uri toCountdownConditionId(long time) { @@ -598,9 +609,43 @@ public class ZenModeConfig implements Parcelable { return tryParseCountdownConditionId(conditionId) != 0; } - // built-in schedule conditions + // ==== Built-in system condition: schedule ==== + public static final String SCHEDULE_PATH = "schedule"; + public static Uri toScheduleConditionId(ScheduleInfo schedule) { + return new Uri.Builder().scheme(Condition.SCHEME) + .authority(SYSTEM_AUTHORITY) + .appendPath(SCHEDULE_PATH) + .appendQueryParameter("days", toDayList(schedule.days)) + .appendQueryParameter("start", schedule.startHour + "." + schedule.startMinute) + .appendQueryParameter("end", schedule.endHour + "." + schedule.endMinute) + .build(); + } + + public static boolean isValidScheduleConditionId(Uri conditionId) { + return tryParseScheduleConditionId(conditionId) != null; + } + + public static ScheduleInfo tryParseScheduleConditionId(Uri conditionId) { + final boolean isSchedule = conditionId != null + && conditionId.getScheme().equals(Condition.SCHEME) + && conditionId.getAuthority().equals(ZenModeConfig.SYSTEM_AUTHORITY) + && conditionId.getPathSegments().size() == 1 + && conditionId.getPathSegments().get(0).equals(ZenModeConfig.SCHEDULE_PATH); + if (!isSchedule) return null; + final int[] start = tryParseHourAndMinute(conditionId.getQueryParameter("start")); + final int[] end = tryParseHourAndMinute(conditionId.getQueryParameter("end")); + if (start == null || end == null) return null; + final ScheduleInfo rt = new ScheduleInfo(); + rt.days = tryParseDayList(conditionId.getQueryParameter("days"), "\\."); + rt.startHour = start[0]; + rt.startMinute = start[1]; + rt.endHour = end[0]; + rt.endMinute = end[1]; + return rt; + } + public static class ScheduleInfo { public int[] days; public int startHour; @@ -638,39 +683,76 @@ public class ZenModeConfig implements Parcelable { } } - public static Uri toScheduleConditionId(ScheduleInfo schedule) { + // ==== Built-in system condition: event ==== + + public static final String EVENT_PATH = "event"; + + public static Uri toEventConditionId(EventInfo event) { return new Uri.Builder().scheme(Condition.SCHEME) .authority(SYSTEM_AUTHORITY) - .appendPath(SCHEDULE_PATH) - .appendQueryParameter("days", toDayList(schedule.days)) - .appendQueryParameter("start", schedule.startHour + "." + schedule.startMinute) - .appendQueryParameter("end", schedule.endHour + "." + schedule.endMinute) + .appendPath(EVENT_PATH) + .appendQueryParameter("calendar", Long.toString(event.calendar)) + .appendQueryParameter("attendance", Integer.toString(event.attendance)) + .appendQueryParameter("reply", Integer.toString(event.reply)) .build(); } - public static boolean isValidScheduleConditionId(Uri conditionId) { - return tryParseScheduleConditionId(conditionId) != null; + public static boolean isValidEventConditionId(Uri conditionId) { + return tryParseEventConditionId(conditionId) != null; } - public static ScheduleInfo tryParseScheduleConditionId(Uri conditionId) { - final boolean isSchedule = conditionId != null + public static EventInfo tryParseEventConditionId(Uri conditionId) { + final boolean isEvent = conditionId != null && conditionId.getScheme().equals(Condition.SCHEME) && conditionId.getAuthority().equals(ZenModeConfig.SYSTEM_AUTHORITY) && conditionId.getPathSegments().size() == 1 - && conditionId.getPathSegments().get(0).equals(ZenModeConfig.SCHEDULE_PATH); - if (!isSchedule) return null; - final int[] start = tryParseHourAndMinute(conditionId.getQueryParameter("start")); - final int[] end = tryParseHourAndMinute(conditionId.getQueryParameter("end")); - if (start == null || end == null) return null; - final ScheduleInfo rt = new ScheduleInfo(); - rt.days = tryParseDayList(conditionId.getQueryParameter("days"), "\\."); - rt.startHour = start[0]; - rt.startMinute = start[1]; - rt.endHour = end[0]; - rt.endMinute = end[1]; + && conditionId.getPathSegments().get(0).equals(EVENT_PATH); + if (!isEvent) return null; + final EventInfo rt = new EventInfo(); + rt.calendar = tryParseLong(conditionId.getQueryParameter("calendar"), 0L); + rt.attendance = tryParseInt(conditionId.getQueryParameter("attendance"), 0); + rt.reply = tryParseInt(conditionId.getQueryParameter("reply"), 0); return rt; } + public static class EventInfo { + public static final int ATTENDANCE_REQUIRED_OR_OPTIONAL = 0; + public static final int ATTENDANCE_REQUIRED = 1; + public static final int ATTENDANCE_OPTIONAL = 2; + + public static final int REPLY_ANY = 0; + public static final int REPLY_ANY_EXCEPT_NO = 1; + public static final int REPLY_YES = 2; + + public long calendar; // CalendarContract.Calendars._ID, or 0 for any + public int attendance; + public int reply; + + @Override + public int hashCode() { + return 0; + } + + @Override + public boolean equals(Object o) { + if (!(o instanceof EventInfo)) return false; + final EventInfo other = (EventInfo) o; + return calendar == other.calendar + && attendance == other.attendance + && reply == other.reply; + } + + public EventInfo copy() { + final EventInfo rt = new EventInfo(); + rt.calendar = calendar; + rt.attendance = attendance; + rt.reply = reply; + return rt; + } + } + + // ==== End built-in system conditions ==== + private static int[] tryParseHourAndMinute(String value) { if (TextUtils.isEmpty(value)) return null; final int i = value.indexOf('.'); diff --git a/core/java/com/android/internal/logging/MetricsLogger.java b/core/java/com/android/internal/logging/MetricsLogger.java index 092c148..3f96174 100644 --- a/core/java/com/android/internal/logging/MetricsLogger.java +++ b/core/java/com/android/internal/logging/MetricsLogger.java @@ -26,11 +26,12 @@ import android.os.Build; */ public class MetricsLogger implements MetricsConstants { // These constants are temporary, they should migrate to MetricsConstants. - // next value is 146; + // next value is 148; public static final int NOTIFICATION_ZEN_MODE_SCHEDULE_RULE = 144; public static final int NOTIFICATION_ZEN_MODE_EXTERNAL_RULE = 145; public static final int ACTION_BAN_APP_NOTES = 146; + public static final int NOTIFICATION_ZEN_MODE_EVENT_RULE = 147; public static void visible(Context context, int category) throws IllegalArgumentException { if (Build.IS_DEBUGGABLE && category == VIEW_UNKNOWN) { diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index 56eab2c..6f60188 100755 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -2054,6 +2054,7 @@ <string-array translatable="false" name="config_system_condition_providers"> <item>countdown</item> <item>schedule</item> + <item>event</item> </string-array> <!-- Priority repeat caller threshold, in minutes --> diff --git a/services/core/java/com/android/server/notification/CountdownConditionProvider.java b/services/core/java/com/android/server/notification/CountdownConditionProvider.java index 6a04688..b5b97d6 100644 --- a/services/core/java/com/android/server/notification/CountdownConditionProvider.java +++ b/services/core/java/com/android/server/notification/CountdownConditionProvider.java @@ -64,7 +64,7 @@ public class CountdownConditionProvider extends SystemConditionProviderService { } @Override - public boolean isValidConditionid(Uri id) { + public boolean isValidConditionId(Uri id) { return ZenModeConfig.isValidCountdownConditionId(id); } diff --git a/services/core/java/com/android/server/notification/EventConditionProvider.java b/services/core/java/com/android/server/notification/EventConditionProvider.java new file mode 100644 index 0000000..425e222 --- /dev/null +++ b/services/core/java/com/android/server/notification/EventConditionProvider.java @@ -0,0 +1,142 @@ +/* + * Copyright (C) 2015 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.ComponentName; +import android.content.Context; +import android.net.Uri; +import android.service.notification.Condition; +import android.service.notification.IConditionProvider; +import android.service.notification.ZenModeConfig; +import android.util.ArraySet; +import android.util.Log; +import android.util.Slog; + +import com.android.server.notification.NotificationManagerService.DumpFilter; + +import java.io.PrintWriter; + +/** + * Built-in zen condition provider for calendar event-based conditions. + */ +public class EventConditionProvider extends SystemConditionProviderService { + private static final String TAG = "ConditionProviders"; + private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); + + public static final ComponentName COMPONENT = + new ComponentName("android", EventConditionProvider.class.getName()); + private static final String NOT_SHOWN = "..."; + + private final ArraySet<Uri> mSubscriptions = new ArraySet<Uri>(); + + private boolean mConnected; + private boolean mRegistered; + + public EventConditionProvider() { + if (DEBUG) Slog.d(TAG, "new EventConditionProvider()"); + } + + @Override + public ComponentName getComponent() { + return COMPONENT; + } + + @Override + public boolean isValidConditionId(Uri id) { + return ZenModeConfig.isValidEventConditionId(id); + } + + @Override + public void dump(PrintWriter pw, DumpFilter filter) { + pw.println(" EventConditionProvider:"); + pw.print(" mConnected="); pw.println(mConnected); + pw.print(" mRegistered="); pw.println(mRegistered); + pw.println(" mSubscriptions="); + for (Uri conditionId : mSubscriptions) { + pw.print(" "); + pw.println(conditionId); + } + } + + @Override + public void onConnected() { + if (DEBUG) Slog.d(TAG, "onConnected"); + mConnected = true; + } + + @Override + public void onDestroy() { + super.onDestroy(); + if (DEBUG) Slog.d(TAG, "onDestroy"); + mConnected = false; + } + + @Override + public void onRequestConditions(int relevance) { + if (DEBUG) Slog.d(TAG, "onRequestConditions relevance=" + relevance); + // does not advertise conditions + } + + @Override + public void onSubscribe(Uri conditionId) { + if (DEBUG) Slog.d(TAG, "onSubscribe " + conditionId); + if (!ZenModeConfig.isValidEventConditionId(conditionId)) { + notifyCondition(conditionId, Condition.STATE_FALSE, "badCondition"); + return; + } + mSubscriptions.add(conditionId); + evaluateSubscriptions(); + } + + @Override + public void onUnsubscribe(Uri conditionId) { + if (DEBUG) Slog.d(TAG, "onUnsubscribe " + conditionId); + if (mSubscriptions.remove(conditionId)) { + evaluateSubscriptions(); + } + } + + @Override + public void attachBase(Context base) { + attachBaseContext(base); + } + + @Override + public IConditionProvider asInterface() { + return (IConditionProvider) onBind(null); + } + + private void evaluateSubscriptions() { + for (Uri conditionId : mSubscriptions) { + notifyCondition(conditionId, Condition.STATE_FALSE, "notImplemented"); + } + } + + private void notifyCondition(Uri conditionId, int state, String reason) { + if (DEBUG) Slog.d(TAG, "notifyCondition " + Condition.stateToString(state) + + " reason=" + reason); + notifyCondition(createCondition(conditionId, state)); + } + + private Condition createCondition(Uri id, int state) { + final String summary = NOT_SHOWN; + final String line1 = NOT_SHOWN; + final String line2 = NOT_SHOWN; + return new Condition(id, summary, line1, line2, 0, state, Condition.FLAG_RELEVANT_ALWAYS); + } + +} diff --git a/services/core/java/com/android/server/notification/ScheduleConditionProvider.java b/services/core/java/com/android/server/notification/ScheduleConditionProvider.java index 383d56c..0912e97 100644 --- a/services/core/java/com/android/server/notification/ScheduleConditionProvider.java +++ b/services/core/java/com/android/server/notification/ScheduleConditionProvider.java @@ -69,7 +69,7 @@ public class ScheduleConditionProvider extends SystemConditionProviderService { } @Override - public boolean isValidConditionid(Uri id) { + public boolean isValidConditionId(Uri id) { return ZenModeConfig.isValidScheduleConditionId(id); } diff --git a/services/core/java/com/android/server/notification/SystemConditionProviderService.java b/services/core/java/com/android/server/notification/SystemConditionProviderService.java index a217623..3282a69a 100644 --- a/services/core/java/com/android/server/notification/SystemConditionProviderService.java +++ b/services/core/java/com/android/server/notification/SystemConditionProviderService.java @@ -32,5 +32,5 @@ public abstract class SystemConditionProviderService extends ConditionProviderSe abstract public void attachBase(Context context); abstract public IConditionProvider asInterface(); abstract public ComponentName getComponent(); - abstract public boolean isValidConditionid(Uri id); + abstract public boolean isValidConditionId(Uri id); } diff --git a/services/core/java/com/android/server/notification/ZenModeConditions.java b/services/core/java/com/android/server/notification/ZenModeConditions.java index 766d6c5..fa314de 100644 --- a/services/core/java/com/android/server/notification/ZenModeConditions.java +++ b/services/core/java/com/android/server/notification/ZenModeConditions.java @@ -38,20 +38,19 @@ public class ZenModeConditions implements ConditionProviders.Callback { private final ConditionProviders mConditionProviders; private final ArrayMap<Uri, ComponentName> mSubscriptions = new ArrayMap<>(); - private CountdownConditionProvider mCountdown; - private ScheduleConditionProvider mSchedule; private boolean mFirstEvaluation = true; public ZenModeConditions(ZenModeHelper helper, ConditionProviders conditionProviders) { mHelper = helper; mConditionProviders = conditionProviders; if (mConditionProviders.isSystemProviderEnabled(ZenModeConfig.COUNTDOWN_PATH)) { - mCountdown = new CountdownConditionProvider(); - mConditionProviders.addSystemProvider(mCountdown); + mConditionProviders.addSystemProvider(new CountdownConditionProvider()); } if (mConditionProviders.isSystemProviderEnabled(ZenModeConfig.SCHEDULE_PATH)) { - mSchedule = new ScheduleConditionProvider(); - mConditionProviders.addSystemProvider(mSchedule); + mConditionProviders.addSystemProvider(new ScheduleConditionProvider()); + } + if (mConditionProviders.isSystemProviderEnabled(ZenModeConfig.EVENT_PATH)) { + mConditionProviders.addSystemProvider(new EventConditionProvider()); } mConditionProviders.setCallback(this); } @@ -128,7 +127,7 @@ public class ZenModeConditions implements ConditionProviders.Callback { final Uri id = rule.conditionId; boolean isSystemCondition = false; for (SystemConditionProviderService sp : mConditionProviders.getSystemProviders()) { - if (sp.isValidConditionid(id)) { + if (sp.isValidConditionId(id)) { mConditionProviders.ensureRecordExists(sp.getComponent(), id, sp.asInterface()); rule.component = sp.getComponent(); isSystemCondition = true; |