• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2015 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.settings.notification;
18 
19 import static android.service.notification.ZenModeConfig.ALL_DAYS;
20 
21 import android.content.ComponentName;
22 import android.content.Intent;
23 import android.content.pm.ServiceInfo;
24 import android.content.res.Resources;
25 import android.os.Bundle;
26 import android.os.UserHandle;
27 import android.preference.Preference;
28 import android.preference.Preference.OnPreferenceClickListener;
29 import android.preference.PreferenceScreen;
30 import android.provider.Settings;
31 import android.provider.Settings.Global;
32 import android.service.notification.ConditionProviderService;
33 import android.service.notification.ZenModeConfig;
34 import android.service.notification.ZenModeConfig.EventInfo;
35 import android.service.notification.ZenModeConfig.ScheduleInfo;
36 import android.service.notification.ZenModeConfig.ZenRule;
37 import android.text.format.DateFormat;
38 import android.util.Log;
39 
40 import com.android.internal.logging.MetricsLogger;
41 import com.android.settings.R;
42 import com.android.settings.notification.ManagedServiceSettings.Config;
43 import com.android.settings.notification.ZenModeEventRuleSettings.CalendarInfo;
44 import com.android.settings.notification.ZenRuleNameDialog.RuleInfo;
45 
46 import java.text.SimpleDateFormat;
47 import java.util.Arrays;
48 import java.util.Calendar;
49 import java.util.Comparator;
50 import java.util.List;
51 import java.util.TreeSet;
52 
53 public class ZenModeAutomationSettings extends ZenModeSettingsBase {
54 
55     static final Config CONFIG = getConditionProviderConfig();
56 
57     // per-instance to ensure we're always using the current locale
58     private final SimpleDateFormat mDayFormat = new SimpleDateFormat("EEE");
59     private final Calendar mCalendar = Calendar.getInstance();
60 
61     private ServiceListing mServiceListing;
62 
63     @Override
onCreate(Bundle icicle)64     public void onCreate(Bundle icicle) {
65         super.onCreate(icicle);
66         addPreferencesFromResource(R.xml.zen_mode_automation_settings);
67         mServiceListing = new ServiceListing(mContext, CONFIG);
68         mServiceListing.addCallback(mServiceListingCallback);
69         mServiceListing.reload();
70         mServiceListing.setListening(true);
71     }
72 
73     @Override
onDestroy()74     public void onDestroy() {
75         super.onDestroy();
76         mServiceListing.setListening(false);
77         mServiceListing.removeCallback(mServiceListingCallback);
78     }
79 
80     @Override
onZenModeChanged()81     protected void onZenModeChanged() {
82         // don't care
83     }
84 
85     @Override
onZenModeConfigChanged()86     protected void onZenModeConfigChanged() {
87         updateControls();
88     }
89 
90     @Override
onResume()91     public void onResume() {
92         super.onResume();
93         updateControls();
94     }
95 
showAddRuleDialog()96     private void showAddRuleDialog() {
97         new ZenRuleNameDialog(mContext, mServiceListing, null, mConfig.getAutomaticRuleNames()) {
98             @Override
99             public void onOk(String ruleName, RuleInfo ri) {
100                 MetricsLogger.action(mContext, MetricsLogger.ACTION_ZEN_ADD_RULE_OK);
101                 final ZenRule rule = new ZenRule();
102                 rule.name = ruleName;
103                 rule.enabled = true;
104                 rule.zenMode = Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
105                 rule.conditionId = ri.defaultConditionId;
106                 rule.component = ri.serviceComponent;
107                 final ZenModeConfig newConfig = mConfig.copy();
108                 final String ruleId = newConfig.newRuleId();
109                 newConfig.automaticRules.put(ruleId, rule);
110                 if (setZenModeConfig(newConfig)) {
111                     showRule(ri.settingsAction, ri.configurationActivity, ruleId, rule.name);
112                 }
113             }
114         }.show();
115     }
116 
showRule(String settingsAction, ComponentName configurationActivity, String ruleId, String ruleName)117     private void showRule(String settingsAction, ComponentName configurationActivity,
118             String ruleId, String ruleName) {
119         if (DEBUG) Log.d(TAG, "showRule " + ruleId + " name=" + ruleName);
120         mContext.startActivity(new Intent(settingsAction)
121                 .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
122                 .putExtra(ZenModeRuleSettingsBase.EXTRA_RULE_ID, ruleId));
123     }
124 
sortedRules()125     private ZenRuleInfo[] sortedRules() {
126         final ZenRuleInfo[] rt = new ZenRuleInfo[mConfig.automaticRules.size()];
127         for (int i = 0; i < rt.length; i++) {
128             final ZenRuleInfo zri = new ZenRuleInfo();
129             zri.id = mConfig.automaticRules.keyAt(i);
130             zri.rule = mConfig.automaticRules.valueAt(i);
131             rt[i] = zri;
132         }
133         Arrays.sort(rt, RULE_COMPARATOR);
134         return rt;
135     }
136 
updateControls()137     private void updateControls() {
138         final PreferenceScreen root = getPreferenceScreen();
139         root.removeAll();
140         if (mConfig == null) return;
141         final ZenRuleInfo[] sortedRules = sortedRules();
142         for (int i = 0; i < sortedRules.length; i++) {
143             final String id = sortedRules[i].id;
144             final ZenRule rule = sortedRules[i].rule;
145             final boolean isSchedule = ZenModeConfig.isValidScheduleConditionId(rule.conditionId);
146             final boolean isEvent = ZenModeConfig.isValidEventConditionId(rule.conditionId);
147             final Preference p = new Preference(mContext);
148             p.setIcon(isSchedule ? R.drawable.ic_schedule
149                     : isEvent ? R.drawable.ic_event
150                     : R.drawable.ic_label);
151             p.setTitle(rule.name);
152             p.setSummary(computeRuleSummary(rule));
153             p.setPersistent(false);
154             p.setOnPreferenceClickListener(new OnPreferenceClickListener() {
155                 @Override
156                 public boolean onPreferenceClick(Preference preference) {
157                     final String action = isSchedule ? ZenModeScheduleRuleSettings.ACTION
158                             : isEvent ? ZenModeEventRuleSettings.ACTION
159                             : ZenModeExternalRuleSettings.ACTION;
160                     showRule(action, null, id, rule.name);
161                     return true;
162                 }
163             });
164             root.addPreference(p);
165         }
166         final Preference p = new Preference(mContext);
167         p.setIcon(R.drawable.ic_add);
168         p.setTitle(R.string.zen_mode_add_rule);
169         p.setPersistent(false);
170         p.setOnPreferenceClickListener(new OnPreferenceClickListener() {
171             @Override
172             public boolean onPreferenceClick(Preference preference) {
173                 MetricsLogger.action(mContext, MetricsLogger.ACTION_ZEN_ADD_RULE);
174                 showAddRuleDialog();
175                 return true;
176             }
177         });
178         root.addPreference(p);
179     }
180 
181     @Override
getMetricsCategory()182     protected int getMetricsCategory() {
183         return MetricsLogger.NOTIFICATION_ZEN_MODE_AUTOMATION;
184     }
185 
computeRuleSummary(ZenRule rule)186     private String computeRuleSummary(ZenRule rule) {
187         if (rule == null || !rule.enabled) return getString(R.string.switch_off_text);
188         final String mode = computeZenModeCaption(getResources(), rule.zenMode);
189         String summary = getString(R.string.switch_on_text);
190         final ScheduleInfo schedule = ZenModeConfig.tryParseScheduleConditionId(rule.conditionId);
191         final EventInfo event = ZenModeConfig.tryParseEventConditionId(rule.conditionId);
192         if (schedule != null) {
193             summary = computeScheduleRuleSummary(schedule);
194         } else if (event != null) {
195             summary = computeEventRuleSummary(event);
196         }
197         return getString(R.string.zen_mode_rule_summary_combination, summary, mode);
198     }
199 
computeScheduleRuleSummary(ScheduleInfo schedule)200     private String computeScheduleRuleSummary(ScheduleInfo schedule) {
201         final String days = computeContiguousDayRanges(schedule.days);
202         final String start = getTime(schedule.startHour, schedule.startMinute);
203         final String end = getTime(schedule.endHour, schedule.endMinute);
204         final String time = getString(R.string.summary_range_verbal_combination, start, end);
205         return getString(R.string.zen_mode_rule_summary_combination, days, time);
206     }
207 
computeEventRuleSummary(EventInfo event)208     private String computeEventRuleSummary(EventInfo event) {
209         final String calendar = getString(R.string.zen_mode_event_rule_summary_calendar_template,
210                 computeCalendarName(event));
211         final String reply = getString(R.string.zen_mode_event_rule_summary_reply_template,
212                 getString(computeReply(event)));
213         return getString(R.string.zen_mode_rule_summary_combination, calendar, reply);
214     }
215 
computeCalendarName(EventInfo event)216     private String computeCalendarName(EventInfo event) {
217         return event.calendar != null ? event.calendar
218                 : getString(R.string.zen_mode_event_rule_summary_any_calendar);
219     }
220 
computeReply(EventInfo event)221     private int computeReply(EventInfo event) {
222         switch (event.reply) {
223             case EventInfo.REPLY_ANY_EXCEPT_NO:
224                 return R.string.zen_mode_event_rule_reply_any_except_no;
225             case EventInfo.REPLY_YES:
226                 return R.string.zen_mode_event_rule_reply_yes;
227             case EventInfo.REPLY_YES_OR_MAYBE:
228                 return R.string.zen_mode_event_rule_reply_yes_or_maybe;
229             default:
230                 throw new IllegalArgumentException("Bad reply: " + event.reply);
231         }
232     }
233 
getTime(int hour, int minute)234     private String getTime(int hour, int minute) {
235         mCalendar.set(Calendar.HOUR_OF_DAY, hour);
236         mCalendar.set(Calendar.MINUTE, minute);
237         return DateFormat.getTimeFormat(mContext).format(mCalendar.getTime());
238     }
239 
computeContiguousDayRanges(int[] days)240     private String computeContiguousDayRanges(int[] days) {
241         final TreeSet<Integer> daySet = new TreeSet<>();
242         for (int i = 0; days != null && i < days.length; i++) {
243             daySet.add(days[i]);
244         }
245         if (daySet.isEmpty()) {
246             return getString(R.string.zen_mode_schedule_rule_days_none);
247         }
248         final int N = ALL_DAYS.length;
249         if (daySet.size() == N) {
250             return getString(R.string.zen_mode_schedule_rule_days_all);
251         }
252         String rt = null;
253         for (int i = 0; i < N; i++) {
254             final int startDay = ALL_DAYS[i];
255             final boolean active = daySet.contains(startDay);
256             if (!active) continue;
257             int end = 0;
258             while (daySet.contains(ALL_DAYS[(i + end + 1) % N])) {
259                 end++;
260             }
261             if (!(i == 0 && daySet.contains(ALL_DAYS[N - 1]))) {
262                 final String v = end == 0 ? dayString(startDay)
263                         : getString(R.string.summary_range_symbol_combination,
264                                 dayString(startDay),
265                                 dayString(ALL_DAYS[(i + end) % N]));
266                 rt = rt == null ? v : getString(R.string.summary_divider_text, rt, v);
267             }
268             i += end;
269         }
270         return rt;
271     }
272 
dayString(int day)273     private String dayString(int day) {
274         mCalendar.set(Calendar.DAY_OF_WEEK, day);
275         return mDayFormat.format(mCalendar.getTime());
276     }
277 
getConditionProviderConfig()278     private static Config getConditionProviderConfig() {
279         final Config c = new Config();
280         c.tag = TAG;
281         c.setting = Settings.Secure.ENABLED_CONDITION_PROVIDERS;
282         c.intentAction = ConditionProviderService.SERVICE_INTERFACE;
283         c.permission = android.Manifest.permission.BIND_CONDITION_PROVIDER_SERVICE;
284         c.noun = "condition provider";
285         return c;
286     }
287 
computeZenModeCaption(Resources res, int zenMode)288     private static String computeZenModeCaption(Resources res, int zenMode) {
289         switch (zenMode) {
290             case Global.ZEN_MODE_ALARMS:
291                 return res.getString(R.string.zen_mode_option_alarms);
292             case Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS:
293                 return res.getString(R.string.zen_mode_option_important_interruptions);
294             case Global.ZEN_MODE_NO_INTERRUPTIONS:
295                 return res.getString(R.string.zen_mode_option_no_interruptions);
296             default:
297                 return null;
298         }
299     }
300 
301     private final ServiceListing.Callback mServiceListingCallback = new ServiceListing.Callback() {
302         @Override
303         public void onServicesReloaded(List<ServiceInfo> services) {
304             for (ServiceInfo service : services) {
305                 final RuleInfo ri = ZenModeExternalRuleSettings.getRuleInfo(service);
306                 if (ri != null && ri.serviceComponent != null
307                         && ri.settingsAction == ZenModeExternalRuleSettings.ACTION) {
308                     if (!mServiceListing.isEnabled(ri.serviceComponent)) {
309                         Log.i(TAG, "Enabling external condition provider: " + ri.serviceComponent);
310                         mServiceListing.setEnabled(ri.serviceComponent, true);
311                     }
312                 }
313             }
314         }
315     };
316 
317     private static final Comparator<ZenRuleInfo> RULE_COMPARATOR = new Comparator<ZenRuleInfo>() {
318         @Override
319         public int compare(ZenRuleInfo lhs, ZenRuleInfo rhs) {
320             return key(lhs).compareTo(key(rhs));
321         }
322 
323         private String key(ZenRuleInfo zri) {
324             final ZenRule rule = zri.rule;
325             final int type = ZenModeConfig.isValidScheduleConditionId(rule.conditionId) ? 1
326                     : ZenModeConfig.isValidEventConditionId(rule.conditionId) ? 2
327                     : 3;
328             return type + rule.name;
329         }
330     };
331 
332     private static class ZenRuleInfo {
333         String id;
334         ZenRule rule;
335     }
336 
337 }
338