• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2014 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 android.app.AlertDialog;
20 import android.app.AutomaticZenRule;
21 import android.app.NotificationManager;
22 import android.app.NotificationManager.Policy;
23 import android.content.ComponentName;
24 import android.content.Context;
25 import android.content.DialogInterface;
26 import android.content.Intent;
27 import android.content.pm.ApplicationInfo;
28 import android.content.pm.PackageManager;
29 import android.content.pm.ServiceInfo;
30 import android.content.res.Resources;
31 import android.graphics.drawable.Drawable;
32 import android.os.AsyncTask;
33 import android.os.Bundle;
34 import android.provider.Settings;
35 import android.service.notification.ConditionProviderService;
36 import android.service.notification.ZenModeConfig;
37 import com.android.settings.utils.ManagedServiceSettings;
38 import com.android.settings.utils.ZenServiceListing;
39 import android.support.annotation.VisibleForTesting;
40 import android.support.v7.preference.Preference;
41 import android.support.v7.preference.PreferenceCategory;
42 import android.support.v7.preference.PreferenceScreen;
43 import android.support.v7.preference.PreferenceViewHolder;
44 import android.view.View;
45 
46 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
47 import com.android.settings.R;
48 import com.android.settingslib.TwoTargetPreference;
49 
50 import java.lang.ref.WeakReference;
51 import java.util.Arrays;
52 import java.util.Comparator;
53 import java.util.Map;
54 import java.util.Map.Entry;
55 
56 public class ZenModeSettings extends ZenModeSettingsBase {
57 
58     public static final String KEY_VISUAL_SETTINGS = "visual_interruptions_settings";
59 
60     private static final String KEY_PRIORITY_SETTINGS = "priority_settings";
61     private static final String KEY_AUTOMATIC_RULES = "automatic_rules";
62 
63     static final ManagedServiceSettings.Config CONFIG = getConditionProviderConfig();
64 
65     private PreferenceCategory mAutomaticRules;
66     private Preference mPrioritySettings;
67     private Preference mVisualSettings;
68     private Policy mPolicy;
69     private SummaryBuilder mSummaryBuilder;
70     private PackageManager mPm;
71     private ZenServiceListing mServiceListing;
72 
73     @Override
onCreate(Bundle savedInstanceState)74     public void onCreate(Bundle savedInstanceState) {
75         super.onCreate(savedInstanceState);
76 
77         addPreferencesFromResource(R.xml.zen_mode_settings);
78         final PreferenceScreen root = getPreferenceScreen();
79 
80         mAutomaticRules = (PreferenceCategory) root.findPreference(KEY_AUTOMATIC_RULES);
81         mPrioritySettings = root.findPreference(KEY_PRIORITY_SETTINGS);
82         mVisualSettings = root.findPreference(KEY_VISUAL_SETTINGS);
83         mPolicy = NotificationManager.from(mContext).getNotificationPolicy();
84         mSummaryBuilder = new SummaryBuilder(getContext());
85         mPm = mContext.getPackageManager();
86         mServiceListing = new ZenServiceListing(mContext, CONFIG);
87         mServiceListing.reloadApprovedServices();
88     }
89 
90     @Override
onResume()91     public void onResume() {
92         super.onResume();
93         if (isUiRestricted()) {
94             return;
95         }
96         updateControls();
97     }
98 
99     @Override
getMetricsCategory()100     public int getMetricsCategory() {
101         return MetricsEvent.NOTIFICATION_ZEN_MODE;
102     }
103 
104     @Override
onZenModeChanged()105     protected void onZenModeChanged() {
106         updateControls();
107     }
108 
109     @Override
onZenModeConfigChanged()110     protected void onZenModeConfigChanged() {
111         mPolicy = NotificationManager.from(mContext).getNotificationPolicy();
112         updateControls();
113     }
114 
updateControls()115     private void updateControls() {
116         updatePrioritySettingsSummary();
117         updateVisualSettingsSummary();
118         updateAutomaticRules();
119     }
120 
updatePrioritySettingsSummary()121     private void updatePrioritySettingsSummary() {
122         mPrioritySettings.setSummary(mSummaryBuilder.getPrioritySettingSummary(mPolicy));
123     }
124 
updateVisualSettingsSummary()125     private void updateVisualSettingsSummary() {
126         mVisualSettings.setSummary(mSummaryBuilder.getVisualSettingSummary(mPolicy));
127     }
128 
updateAutomaticRules()129     private void updateAutomaticRules() {
130         mAutomaticRules.removeAll();
131         final Map.Entry<String,AutomaticZenRule>[] sortedRules = sortedRules();
132         for (Map.Entry<String,AutomaticZenRule> sortedRule : sortedRules) {
133             ZenRulePreference pref = new ZenRulePreference(getPrefContext(), sortedRule);
134             if (pref.appExists) {
135                 mAutomaticRules.addPreference(pref);
136             }
137         }
138         final Preference p = new Preference(getPrefContext());
139         p.setIcon(R.drawable.ic_menu_add);
140         p.setTitle(R.string.zen_mode_add_rule);
141         p.setPersistent(false);
142         p.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
143             @Override
144             public boolean onPreferenceClick(Preference preference) {
145                 mMetricsFeatureProvider.action(mContext, MetricsEvent.ACTION_ZEN_ADD_RULE);
146                 showAddRuleDialog();
147                 return true;
148             }
149         });
150         mAutomaticRules.addPreference(p);
151     }
152 
showAddRuleDialog()153     private void showAddRuleDialog() {
154         new ZenRuleSelectionDialog(mContext, mServiceListing) {
155             @Override
156             public void onSystemRuleSelected(ZenRuleInfo ri) {
157                 showNameRuleDialog(ri);
158             }
159 
160             @Override
161             public void onExternalRuleSelected(ZenRuleInfo ri) {
162                 Intent intent = new Intent().setComponent(ri.configurationActivity);
163                 startActivity(intent);
164             }
165         }.show();
166     }
167 
computeRuleSummary(AutomaticZenRule rule, boolean isSystemRule, CharSequence providerLabel)168     private String computeRuleSummary(AutomaticZenRule rule, boolean isSystemRule,
169             CharSequence providerLabel) {
170         final String mode = computeZenModeCaption(getResources(), rule.getInterruptionFilter());
171         final String ruleState = (rule == null || !rule.isEnabled())
172                 ? getString(R.string.switch_off_text)
173                 : getString(R.string.zen_mode_rule_summary_enabled_combination, mode);
174 
175         return ruleState;
176     }
177 
getConditionProviderConfig()178     private static ManagedServiceSettings.Config getConditionProviderConfig() {
179         final ManagedServiceSettings.Config c = new ManagedServiceSettings.Config();
180         c.tag = TAG;
181         c.intentAction = ConditionProviderService.SERVICE_INTERFACE;
182         c.permission = android.Manifest.permission.BIND_CONDITION_PROVIDER_SERVICE;
183         c.noun = "condition provider";
184         return c;
185     }
186 
computeZenModeCaption(Resources res, int zenMode)187     private static String computeZenModeCaption(Resources res, int zenMode) {
188         switch (zenMode) {
189             case NotificationManager.INTERRUPTION_FILTER_ALARMS:
190                 return res.getString(R.string.zen_mode_option_alarms);
191             case NotificationManager.INTERRUPTION_FILTER_PRIORITY:
192                 return res.getString(R.string.zen_mode_option_important_interruptions);
193             case NotificationManager.INTERRUPTION_FILTER_NONE:
194                 return res.getString(R.string.zen_mode_option_no_interruptions);
195             default:
196                 return null;
197         }
198     }
199 
getRuleInfo(PackageManager pm, ServiceInfo si)200     public static ZenRuleInfo getRuleInfo(PackageManager pm, ServiceInfo si) {
201         if (si == null || si.metaData == null) return null;
202         final String ruleType = si.metaData.getString(ConditionProviderService.META_DATA_RULE_TYPE);
203         final ComponentName configurationActivity = getSettingsActivity(si);
204         if (ruleType != null && !ruleType.trim().isEmpty() && configurationActivity != null) {
205             final ZenRuleInfo ri = new ZenRuleInfo();
206             ri.serviceComponent = new ComponentName(si.packageName, si.name);
207             ri.settingsAction = Settings.ACTION_ZEN_MODE_EXTERNAL_RULE_SETTINGS;
208             ri.title = ruleType;
209             ri.packageName = si.packageName;
210             ri.configurationActivity = getSettingsActivity(si);
211             ri.packageLabel = si.applicationInfo.loadLabel(pm);
212             ri.ruleInstanceLimit =
213                     si.metaData.getInt(ConditionProviderService.META_DATA_RULE_INSTANCE_LIMIT, -1);
214             return ri;
215         }
216         return null;
217     }
218 
getSettingsActivity(ServiceInfo si)219     private static ComponentName getSettingsActivity(ServiceInfo si) {
220         if (si == null || si.metaData == null) return null;
221         final String configurationActivity =
222                 si.metaData.getString(ConditionProviderService.META_DATA_CONFIGURATION_ACTIVITY);
223         if (configurationActivity != null) {
224             return ComponentName.unflattenFromString(configurationActivity);
225         }
226         return null;
227     }
228 
showNameRuleDialog(final ZenRuleInfo ri)229     private void showNameRuleDialog(final ZenRuleInfo ri) {
230         new ZenRuleNameDialog(mContext, null) {
231             @Override
232             public void onOk(String ruleName) {
233                 mMetricsFeatureProvider.action(mContext, MetricsEvent.ACTION_ZEN_ADD_RULE_OK);
234                 AutomaticZenRule rule = new AutomaticZenRule(ruleName, ri.serviceComponent,
235                         ri.defaultConditionId, NotificationManager.INTERRUPTION_FILTER_PRIORITY,
236                         true);
237                 String savedRuleId = addZenRule(rule);
238                 if (savedRuleId != null) {
239                     startActivity(getRuleIntent(ri.settingsAction, null, savedRuleId));
240                 }
241             }
242         }.show();
243     }
244 
showDeleteRuleDialog(final String ruleId, final CharSequence ruleName)245     private void showDeleteRuleDialog(final String ruleId, final CharSequence ruleName) {
246         new AlertDialog.Builder(mContext)
247                 .setMessage(getString(R.string.zen_mode_delete_rule_confirmation, ruleName))
248                 .setNegativeButton(R.string.cancel, null)
249                 .setPositiveButton(R.string.zen_mode_delete_rule_button,
250                         new DialogInterface.OnClickListener() {
251                             @Override
252                             public void onClick(DialogInterface dialog, int which) {
253                                 mMetricsFeatureProvider.action(mContext,
254                                         MetricsEvent.ACTION_ZEN_DELETE_RULE_OK);
255                                 removeZenRule(ruleId);
256                             }
257                         })
258                 .show();
259     }
260 
getRuleIntent(String settingsAction, ComponentName configurationActivity, String ruleId)261     private Intent getRuleIntent(String settingsAction, ComponentName configurationActivity,
262             String ruleId) {
263         Intent intent = new Intent()
264                 .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
265                 .putExtra(ConditionProviderService.EXTRA_RULE_ID, ruleId);
266         if (configurationActivity != null) {
267             intent.setComponent(configurationActivity);
268         } else {
269             intent.setAction(settingsAction);
270         }
271         return intent;
272     }
273 
sortedRules()274     private Map.Entry<String,AutomaticZenRule>[] sortedRules() {
275         final Map.Entry<String,AutomaticZenRule>[] rt =
276                 mRules.toArray(new Map.Entry[mRules.size()]);
277         Arrays.sort(rt, RULE_COMPARATOR);
278         return rt;
279     }
280 
281     @Override
getHelpResource()282     protected int getHelpResource() {
283         return R.string.help_uri_interruptions;
284     }
285 
286     private class ZenRulePreference extends TwoTargetPreference {
287         final CharSequence mName;
288         final String mId;
289         final boolean appExists;
290 
ZenRulePreference(Context context, final Map.Entry<String, AutomaticZenRule> ruleEntry)291         public ZenRulePreference(Context context,
292                 final Map.Entry<String, AutomaticZenRule> ruleEntry) {
293             super(context);
294 
295             final AutomaticZenRule rule = ruleEntry.getValue();
296             mName = rule.getName();
297             mId = ruleEntry.getKey();
298 
299             final boolean isSchedule = ZenModeConfig.isValidScheduleConditionId(
300                     rule.getConditionId());
301             final boolean isEvent = ZenModeConfig.isValidEventConditionId(rule.getConditionId());
302             final boolean isSystemRule = isSchedule || isEvent;
303 
304             try {
305                 ApplicationInfo info = mPm.getApplicationInfo(rule.getOwner().getPackageName(), 0);
306                 setSummary(computeRuleSummary(rule, isSystemRule, info.loadLabel(mPm)));
307             } catch (PackageManager.NameNotFoundException e) {
308                 appExists = false;
309                 return;
310             }
311 
312             appExists = true;
313             setTitle(rule.getName());
314             setPersistent(false);
315 
316             final String action = isSchedule ? ZenModeScheduleRuleSettings.ACTION
317                     : isEvent ? ZenModeEventRuleSettings.ACTION : "";
318             ServiceInfo si = mServiceListing.findService(rule.getOwner());
319             ComponentName settingsActivity = getSettingsActivity(si);
320             setIntent(getRuleIntent(action, settingsActivity, mId));
321             setSelectable(settingsActivity != null || isSystemRule);
322         }
323 
324         @Override
getSecondTargetResId()325         protected int getSecondTargetResId() {
326             return R.layout.zen_rule_widget;
327         }
328 
329         @Override
onBindViewHolder(PreferenceViewHolder view)330         public void onBindViewHolder(PreferenceViewHolder view) {
331             super.onBindViewHolder(view);
332 
333             View v = view.findViewById(R.id.delete_zen_rule);
334             if (v != null) {
335                 v.setOnClickListener(mDeleteListener);
336             }
337         }
338 
339         private final View.OnClickListener mDeleteListener = new View.OnClickListener() {
340             @Override
341             public void onClick(View v) {
342                 showDeleteRuleDialog(mId, mName);
343             }
344         };
345     }
346 
347     public static class SummaryBuilder {
348 
349         private Context mContext;
350 
SummaryBuilder(Context context)351         public SummaryBuilder(Context context) {
352             mContext = context;
353         }
354 
getPrioritySettingSummary(Policy policy)355         String getPrioritySettingSummary(Policy policy) {
356             String s = mContext.getString(R.string.zen_mode_alarms);
357             s = prepend(s, isCategoryEnabled(policy, Policy.PRIORITY_CATEGORY_EVENTS),
358                     R.string.zen_mode_events);
359             s = prepend(s, isCategoryEnabled(policy, Policy.PRIORITY_CATEGORY_REMINDERS),
360                 R.string.zen_mode_reminders);
361             if (isCategoryEnabled(policy, Policy.PRIORITY_CATEGORY_MESSAGES)) {
362                 if (policy.priorityMessageSenders == Policy.PRIORITY_SENDERS_ANY) {
363                     s = append(s, true, R.string.zen_mode_all_messages);
364                 } else {
365                     s = append(s, true, R.string.zen_mode_selected_messages);
366                 }
367             }
368             if (isCategoryEnabled(policy, Policy.PRIORITY_CATEGORY_CALLS)) {
369                 if (policy.priorityCallSenders == Policy.PRIORITY_SENDERS_ANY) {
370                     s = append(s, true, R.string.zen_mode_all_callers);
371                 } else {
372                     s = append(s, true, R.string.zen_mode_selected_callers);
373                 }
374             } else if (isCategoryEnabled(policy, Policy.PRIORITY_CATEGORY_REPEAT_CALLERS)) {
375                 s = append(s, true, R.string.zen_mode_repeat_callers);
376             }
377             return s;
378         }
379 
getVisualSettingSummary(Policy policy)380         String getVisualSettingSummary(Policy policy) {
381             String s = mContext.getString(R.string.zen_mode_all_visual_interruptions);
382             if (isEffectSuppressed(policy, Policy.SUPPRESSED_EFFECT_SCREEN_ON)
383                 && isEffectSuppressed(policy, Policy.SUPPRESSED_EFFECT_SCREEN_OFF)) {
384                 s = mContext.getString(R.string.zen_mode_no_visual_interruptions);
385             } else if (isEffectSuppressed(policy, Policy.SUPPRESSED_EFFECT_SCREEN_ON)) {
386                 s = mContext.getString(R.string.zen_mode_screen_on_visual_interruptions);
387             } else if (isEffectSuppressed(policy, Policy.SUPPRESSED_EFFECT_SCREEN_OFF)) {
388                 s = mContext.getString(R.string.zen_mode_screen_off_visual_interruptions);
389             }
390             return s;
391         }
392 
getAutomaticRulesSummary()393         String getAutomaticRulesSummary() {
394             final int count = getEnabledAutomaticRulesCount();
395             return count == 0 ? mContext.getString(R.string.zen_mode_settings_summary_off)
396                 : mContext.getResources().getQuantityString(
397                     R.plurals.zen_mode_settings_summary_on, count, count);
398         }
399 
400         @VisibleForTesting
getEnabledAutomaticRulesCount()401         int getEnabledAutomaticRulesCount() {
402             int count = 0;
403             final Map<String, AutomaticZenRule> ruleMap =
404                 NotificationManager.from(mContext).getAutomaticZenRules();
405             if (ruleMap != null) {
406                 for (Entry<String, AutomaticZenRule> ruleEntry : ruleMap.entrySet()) {
407                     final AutomaticZenRule rule = ruleEntry.getValue();
408                     if (rule != null && rule.isEnabled()) {
409                         count++;
410                     }
411                 }
412             }
413             return count;
414         }
415 
416         @VisibleForTesting
append(String s, boolean condition, int resId)417         String append(String s, boolean condition, int resId) {
418             if (condition) {
419                 return mContext.getString(
420                     R.string.join_many_items_middle, s, mContext.getString(resId));
421             }
422             return s;
423         }
424 
425         @VisibleForTesting
prepend(String s, boolean condition, int resId)426         String prepend(String s, boolean condition, int resId) {
427             if (condition) {
428                 return mContext.getString(
429                         R.string.join_many_items_middle, mContext.getString(resId), s);
430             }
431             return s;
432         }
433 
isCategoryEnabled(Policy policy, int categoryType)434         private boolean isCategoryEnabled(Policy policy, int categoryType) {
435             return (policy.priorityCategories & categoryType) != 0;
436         }
437 
isEffectSuppressed(Policy policy, int effect)438         private boolean isEffectSuppressed(Policy policy, int effect) {
439             return (policy.suppressedVisualEffects & effect) != 0;
440         }
441     }
442 
443     private static final Comparator<Map.Entry<String,AutomaticZenRule>> RULE_COMPARATOR =
444             new Comparator<Map.Entry<String,AutomaticZenRule>>() {
445                 @Override
446                 public int compare(Map.Entry<String,AutomaticZenRule> lhs,
447                         Map.Entry<String,AutomaticZenRule> rhs) {
448                     int byDate = Long.compare(lhs.getValue().getCreationTime(),
449                             rhs.getValue().getCreationTime());
450                     if (byDate != 0) {
451                         return byDate;
452                     } else {
453                         return key(lhs.getValue()).compareTo(key(rhs.getValue()));
454                     }
455                 }
456 
457                 private String key(AutomaticZenRule rule) {
458                     final int type = ZenModeConfig.isValidScheduleConditionId(rule.getConditionId())
459                             ? 1
460                             : ZenModeConfig.isValidEventConditionId(rule.getConditionId())
461                                     ? 2
462                                     : 3;
463                     return type + rule.getName().toString();
464                 }
465             };
466 }
467