• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2017 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.app.NotificationManager.Policy.SUPPRESSED_EFFECT_SCREEN_OFF;
20 import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_SCREEN_ON;
21 
22 import android.app.ActivityManager;
23 import android.app.AutomaticZenRule;
24 import android.app.NotificationManager;
25 import android.content.Context;
26 import android.database.Cursor;
27 import android.icu.text.ListFormatter;
28 import android.net.Uri;
29 import android.provider.ContactsContract;
30 import android.provider.Settings;
31 import android.service.notification.ZenModeConfig;
32 import android.service.notification.ZenPolicy;
33 import android.util.Log;
34 
35 import androidx.annotation.VisibleForTesting;
36 
37 import com.android.settings.R;
38 
39 import java.util.ArrayList;
40 import java.util.Arrays;
41 import java.util.Comparator;
42 import java.util.List;
43 import java.util.Map;
44 
45 public class ZenModeBackend {
46     @VisibleForTesting
47     protected static final String ZEN_MODE_FROM_ANYONE = "zen_mode_from_anyone";
48     @VisibleForTesting
49     protected static final String ZEN_MODE_FROM_CONTACTS = "zen_mode_from_contacts";
50     @VisibleForTesting
51     protected static final String ZEN_MODE_FROM_STARRED = "zen_mode_from_starred";
52     @VisibleForTesting
53     protected static final String ZEN_MODE_FROM_NONE = "zen_mode_from_none";
54     protected static final int SOURCE_NONE = -1;
55     private static List<String> mDefaultRuleIds;
56 
57     private static ZenModeBackend sInstance;
58 
59     protected int mZenMode;
60     /** gets policy last set by updatePolicy **/
61     protected NotificationManager.Policy mPolicy;
62     private final NotificationManager mNotificationManager;
63 
64     private String TAG = "ZenModeSettingsBackend";
65     private final Context mContext;
66 
getInstance(Context context)67     public static ZenModeBackend getInstance(Context context) {
68         if (sInstance == null) {
69             sInstance = new ZenModeBackend(context);
70         }
71         return sInstance;
72     }
73 
ZenModeBackend(Context context)74     public ZenModeBackend(Context context) {
75         mContext = context;
76         mNotificationManager = (NotificationManager) context.getSystemService(
77                 Context.NOTIFICATION_SERVICE);
78         updateZenMode();
79         updatePolicy();
80     }
81 
updatePolicy()82     protected void updatePolicy() {
83         if (mNotificationManager != null) {
84             mPolicy = mNotificationManager.getNotificationPolicy();
85         }
86     }
87 
updateZenMode()88     protected void updateZenMode() {
89         mZenMode = Settings.Global.getInt(mContext.getContentResolver(),
90                 Settings.Global.ZEN_MODE, mZenMode);
91     }
92 
updateZenRule(String id, AutomaticZenRule rule)93     protected boolean updateZenRule(String id, AutomaticZenRule rule) {
94         return NotificationManager.from(mContext).updateAutomaticZenRule(id, rule);
95     }
96 
setZenMode(int zenMode)97     protected void setZenMode(int zenMode) {
98         NotificationManager.from(mContext).setZenMode(zenMode, null, TAG);
99         mZenMode = getZenMode();
100     }
101 
setZenModeForDuration(int minutes)102     protected void setZenModeForDuration(int minutes) {
103         Uri conditionId = ZenModeConfig.toTimeCondition(mContext, minutes,
104                 ActivityManager.getCurrentUser(), true).id;
105         mNotificationManager.setZenMode(Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS,
106                 conditionId, TAG);
107         mZenMode = getZenMode();
108     }
109 
getZenMode()110     protected int getZenMode() {
111         mZenMode = Settings.Global.getInt(mContext.getContentResolver(),
112                 Settings.Global.ZEN_MODE, mZenMode);
113         return mZenMode;
114     }
115 
isVisualEffectSuppressed(int visualEffect)116     protected boolean isVisualEffectSuppressed(int visualEffect) {
117         return (mPolicy.suppressedVisualEffects & visualEffect) != 0;
118     }
119 
isPriorityCategoryEnabled(int categoryType)120     protected boolean isPriorityCategoryEnabled(int categoryType) {
121         return (mPolicy.priorityCategories & categoryType) != 0;
122     }
123 
getNewDefaultPriorityCategories(boolean allow, int categoryType)124     protected int getNewDefaultPriorityCategories(boolean allow, int categoryType) {
125         int priorityCategories = mPolicy.priorityCategories;
126         if (allow) {
127             priorityCategories |= categoryType;
128         } else {
129             priorityCategories &= ~categoryType;
130         }
131         return priorityCategories;
132     }
133 
getPriorityCallSenders()134     protected int getPriorityCallSenders() {
135         if (isPriorityCategoryEnabled(NotificationManager.Policy.PRIORITY_CATEGORY_CALLS)) {
136             return mPolicy.priorityCallSenders;
137         }
138 
139         return SOURCE_NONE;
140     }
141 
getPriorityMessageSenders()142     protected int getPriorityMessageSenders() {
143         if (isPriorityCategoryEnabled(
144                 NotificationManager.Policy.PRIORITY_CATEGORY_MESSAGES)) {
145             return mPolicy.priorityMessageSenders;
146         }
147         return SOURCE_NONE;
148     }
149 
saveVisualEffectsPolicy(int category, boolean suppress)150     protected void saveVisualEffectsPolicy(int category, boolean suppress) {
151         Settings.Secure.putInt(mContext.getContentResolver(),
152                 Settings.Secure.ZEN_SETTINGS_UPDATED, 1);
153 
154         int suppressedEffects = getNewSuppressedEffects(suppress, category);
155         savePolicy(mPolicy.priorityCategories, mPolicy.priorityCallSenders,
156                 mPolicy.priorityMessageSenders, suppressedEffects);
157     }
158 
saveSoundPolicy(int category, boolean allow)159     protected void saveSoundPolicy(int category, boolean allow) {
160         int priorityCategories = getNewDefaultPriorityCategories(allow, category);
161         savePolicy(priorityCategories, mPolicy.priorityCallSenders,
162                 mPolicy.priorityMessageSenders, mPolicy.suppressedVisualEffects);
163     }
164 
savePolicy(int priorityCategories, int priorityCallSenders, int priorityMessageSenders, int suppressedVisualEffects)165     protected void savePolicy(int priorityCategories, int priorityCallSenders,
166             int priorityMessageSenders, int suppressedVisualEffects) {
167         mPolicy = new NotificationManager.Policy(priorityCategories, priorityCallSenders,
168                 priorityMessageSenders, suppressedVisualEffects);
169         mNotificationManager.setNotificationPolicy(mPolicy);
170     }
171 
172 
getNewSuppressedEffects(boolean suppress, int effectType)173     private int getNewSuppressedEffects(boolean suppress, int effectType) {
174         int effects = mPolicy.suppressedVisualEffects;
175 
176         if (suppress) {
177             effects |= effectType;
178         } else {
179             effects &= ~effectType;
180         }
181 
182         return clearDeprecatedEffects(effects);
183     }
184 
clearDeprecatedEffects(int effects)185     private int clearDeprecatedEffects(int effects) {
186         return effects & ~(SUPPRESSED_EFFECT_SCREEN_ON | SUPPRESSED_EFFECT_SCREEN_OFF);
187     }
188 
isEffectAllowed(int effect)189     protected boolean isEffectAllowed(int effect) {
190         return (mPolicy.suppressedVisualEffects & effect) == 0;
191     }
192 
saveSenders(int category, int val)193     protected void saveSenders(int category, int val) {
194         int priorityCallSenders = getPriorityCallSenders();
195         int priorityMessagesSenders = getPriorityMessageSenders();
196         int categorySenders = getPrioritySenders(category);
197 
198         final boolean allowSenders = val != SOURCE_NONE;
199         final int allowSendersFrom = val == SOURCE_NONE ? categorySenders : val;
200 
201         String stringCategory = "";
202         if (category == NotificationManager.Policy.PRIORITY_CATEGORY_CALLS) {
203             stringCategory = "Calls";
204             priorityCallSenders = allowSendersFrom;
205         }
206 
207         if (category == NotificationManager.Policy.PRIORITY_CATEGORY_MESSAGES) {
208             stringCategory = "Messages";
209             priorityMessagesSenders = allowSendersFrom;
210         }
211 
212         savePolicy(getNewDefaultPriorityCategories(allowSenders, category),
213             priorityCallSenders, priorityMessagesSenders, mPolicy.suppressedVisualEffects);
214 
215         if (ZenModeSettingsBase.DEBUG) Log.d(TAG, "onPrefChange allow" +
216                 stringCategory + "=" + allowSenders + " allow" + stringCategory + "From="
217                 + ZenModeConfig.sourceToString(allowSendersFrom));
218     }
219 
getSendersKey(int category)220     protected String getSendersKey(int category) {
221         switch (getZenMode()) {
222             case Settings.Global.ZEN_MODE_NO_INTERRUPTIONS:
223             case Settings.Global.ZEN_MODE_ALARMS:
224                 return getKeyFromSetting(SOURCE_NONE);
225             default:
226                 int prioritySenders = getPrioritySenders(category);
227                 return getKeyFromSetting(isPriorityCategoryEnabled(category)
228                         ? prioritySenders : SOURCE_NONE);
229             }
230     }
231 
getPrioritySenders(int category)232     private int getPrioritySenders(int category) {
233         int categorySenders = -1;
234 
235         if (category == NotificationManager.Policy.PRIORITY_CATEGORY_CALLS) {
236             return getPriorityCallSenders();
237         }
238 
239         if (category == NotificationManager.Policy.PRIORITY_CATEGORY_MESSAGES) {
240             return getPriorityMessageSenders();
241         }
242 
243         return categorySenders;
244     }
245 
getKeyFromZenPolicySetting(int contactType)246     protected static String getKeyFromZenPolicySetting(int contactType) {
247         switch (contactType) {
248             case ZenPolicy.PEOPLE_TYPE_ANYONE:
249                 return ZEN_MODE_FROM_ANYONE;
250             case  ZenPolicy.PEOPLE_TYPE_CONTACTS:
251                 return ZEN_MODE_FROM_CONTACTS;
252             case ZenPolicy.PEOPLE_TYPE_STARRED:
253                 return ZEN_MODE_FROM_STARRED;
254             case ZenPolicy.PEOPLE_TYPE_NONE:
255             default:
256                 return ZEN_MODE_FROM_NONE;
257         }
258     }
259 
getKeyFromSetting(int contactType)260     protected static String getKeyFromSetting(int contactType) {
261         switch (contactType) {
262             case NotificationManager.Policy.PRIORITY_SENDERS_ANY:
263                 return ZEN_MODE_FROM_ANYONE;
264             case NotificationManager.Policy.PRIORITY_SENDERS_CONTACTS:
265                 return ZEN_MODE_FROM_CONTACTS;
266             case NotificationManager.Policy.PRIORITY_SENDERS_STARRED:
267                 return ZEN_MODE_FROM_STARRED;
268             case SOURCE_NONE:
269             default:
270                 return ZEN_MODE_FROM_NONE;
271         }
272     }
273 
getAlarmsTotalSilenceCallsMessagesSummary(int category)274     protected int getAlarmsTotalSilenceCallsMessagesSummary(int category) {
275         if (category == NotificationManager.Policy.PRIORITY_CATEGORY_MESSAGES) {
276             return R.string.zen_mode_from_none_messages;
277         } else if (category == NotificationManager.Policy.PRIORITY_CATEGORY_CALLS){
278             return R.string.zen_mode_from_none_calls;
279         }
280         return 0;
281     }
282 
getContactsSummary(int category)283     protected int getContactsSummary(int category) {
284         int contactType = -1;
285         if (category == NotificationManager.Policy.PRIORITY_CATEGORY_MESSAGES) {
286             if (isPriorityCategoryEnabled(category)) {
287                 contactType = getPriorityMessageSenders();
288             }
289         } else if (category == NotificationManager.Policy.PRIORITY_CATEGORY_CALLS) {
290             if (isPriorityCategoryEnabled(category)) {
291                 contactType = getPriorityCallSenders();
292             }
293         }
294 
295         switch (contactType) {
296             case NotificationManager.Policy.PRIORITY_SENDERS_ANY:
297                 return R.string.zen_mode_from_anyone;
298             case NotificationManager.Policy.PRIORITY_SENDERS_CONTACTS:
299                 return R.string.zen_mode_from_contacts;
300             case NotificationManager.Policy.PRIORITY_SENDERS_STARRED:
301                 return R.string.zen_mode_from_starred;
302             case SOURCE_NONE:
303             default:
304                 if (category == NotificationManager.Policy.PRIORITY_CATEGORY_MESSAGES) {
305                     return R.string.zen_mode_from_none_messages;
306                 } else {
307                     return R.string.zen_mode_from_none_calls;
308                 }
309         }
310     }
311 
getContactsCallsSummary(ZenPolicy policy)312     protected int getContactsCallsSummary(ZenPolicy policy) {
313         int peopleType = policy.getPriorityCallSenders();
314         switch (peopleType) {
315             case ZenPolicy.PEOPLE_TYPE_ANYONE:
316                 return R.string.zen_mode_from_anyone;
317             case ZenPolicy.PEOPLE_TYPE_CONTACTS:
318                 return R.string.zen_mode_from_contacts;
319             case ZenPolicy.PEOPLE_TYPE_STARRED:
320                 return R.string.zen_mode_from_starred;
321             case ZenPolicy.PEOPLE_TYPE_NONE:
322             default:
323                 return R.string.zen_mode_from_none_calls;
324         }
325     }
326 
getContactsMessagesSummary(ZenPolicy policy)327     protected int getContactsMessagesSummary(ZenPolicy policy) {
328         int peopleType = policy.getPriorityMessageSenders();
329         switch (peopleType) {
330             case ZenPolicy.PEOPLE_TYPE_ANYONE:
331                 return R.string.zen_mode_from_anyone;
332             case ZenPolicy.PEOPLE_TYPE_CONTACTS:
333                 return R.string.zen_mode_from_contacts;
334             case ZenPolicy.PEOPLE_TYPE_STARRED:
335                 return R.string.zen_mode_from_starred;
336             case ZenPolicy.PEOPLE_TYPE_NONE:
337             default:
338                 return R.string.zen_mode_from_none_messages;
339         }
340     }
341 
getZenPolicySettingFromPrefKey(String key)342     protected static int getZenPolicySettingFromPrefKey(String key) {
343         switch (key) {
344             case ZEN_MODE_FROM_ANYONE:
345                 return ZenPolicy.PEOPLE_TYPE_ANYONE;
346             case ZEN_MODE_FROM_CONTACTS:
347                 return ZenPolicy.PEOPLE_TYPE_CONTACTS;
348             case ZEN_MODE_FROM_STARRED:
349                 return ZenPolicy.PEOPLE_TYPE_STARRED;
350             case ZEN_MODE_FROM_NONE:
351             default:
352                 return ZenPolicy.PEOPLE_TYPE_NONE;
353         }
354     }
355 
getSettingFromPrefKey(String key)356     protected static int getSettingFromPrefKey(String key) {
357         switch (key) {
358             case ZEN_MODE_FROM_ANYONE:
359                 return NotificationManager.Policy.PRIORITY_SENDERS_ANY;
360             case ZEN_MODE_FROM_CONTACTS:
361                 return NotificationManager.Policy.PRIORITY_SENDERS_CONTACTS;
362             case ZEN_MODE_FROM_STARRED:
363                 return NotificationManager.Policy.PRIORITY_SENDERS_STARRED;
364             case ZEN_MODE_FROM_NONE:
365             default:
366                 return SOURCE_NONE;
367         }
368     }
369 
removeZenRule(String ruleId)370     public boolean removeZenRule(String ruleId) {
371         return NotificationManager.from(mContext).removeAutomaticZenRule(ruleId);
372     }
373 
getConsolidatedPolicy()374     public NotificationManager.Policy getConsolidatedPolicy() {
375         return NotificationManager.from(mContext).getConsolidatedNotificationPolicy();
376     }
377 
addZenRule(AutomaticZenRule rule)378     protected String addZenRule(AutomaticZenRule rule) {
379         try {
380             return NotificationManager.from(mContext).addAutomaticZenRule(rule);
381         } catch (Exception e) {
382             return null;
383         }
384     }
385 
setDefaultZenPolicy(ZenPolicy zenPolicy)386     ZenPolicy setDefaultZenPolicy(ZenPolicy zenPolicy) {
387         int calls;
388         if (mPolicy.allowCalls()) {
389             calls = ZenModeConfig.getZenPolicySenders(mPolicy.allowCallsFrom());
390         } else {
391             calls = ZenPolicy.PEOPLE_TYPE_NONE;
392         }
393 
394         int messages;
395         if (mPolicy.allowMessages()) {
396             messages = ZenModeConfig.getZenPolicySenders(mPolicy.allowMessagesFrom());
397         } else {
398             messages = ZenPolicy.PEOPLE_TYPE_NONE;
399         }
400 
401         return new ZenPolicy.Builder(zenPolicy)
402                 .allowAlarms(mPolicy.allowAlarms())
403                 .allowCalls(calls)
404                 .allowEvents(mPolicy.allowEvents())
405                 .allowMedia(mPolicy.allowMedia())
406                 .allowMessages(messages)
407                 .allowReminders(mPolicy.allowReminders())
408                 .allowRepeatCallers(mPolicy.allowRepeatCallers())
409                 .allowSystem(mPolicy.allowSystem())
410                 .showFullScreenIntent(mPolicy.showFullScreenIntents())
411                 .showLights(mPolicy.showLights())
412                 .showInAmbientDisplay(mPolicy.showAmbient())
413                 .showInNotificationList(mPolicy.showInNotificationList())
414                 .showBadges(mPolicy.showBadges())
415                 .showPeeking(mPolicy.showPeeking())
416                 .showStatusBarIcons(mPolicy.showStatusBarIcons())
417                 .build();
418     }
419 
getAutomaticZenRules()420     protected Map.Entry<String, AutomaticZenRule>[] getAutomaticZenRules() {
421         Map<String, AutomaticZenRule> ruleMap =
422                 NotificationManager.from(mContext).getAutomaticZenRules();
423         final Map.Entry<String, AutomaticZenRule>[] rt = ruleMap.entrySet().toArray(
424                 new Map.Entry[ruleMap.size()]);
425         Arrays.sort(rt, RULE_COMPARATOR);
426         return rt;
427     }
428 
getAutomaticZenRule(String id)429     protected AutomaticZenRule getAutomaticZenRule(String id) {
430         return NotificationManager.from(mContext).getAutomaticZenRule(id);
431     }
432 
getDefaultRuleIds()433     private static List<String> getDefaultRuleIds() {
434         if (mDefaultRuleIds == null) {
435             mDefaultRuleIds = ZenModeConfig.DEFAULT_RULE_IDS;
436         }
437         return mDefaultRuleIds;
438     }
439 
toNotificationPolicy(ZenPolicy policy)440     NotificationManager.Policy toNotificationPolicy(ZenPolicy policy) {
441         ZenModeConfig config = new ZenModeConfig();
442         return config.toNotificationPolicy(policy);
443     }
444 
445     @VisibleForTesting
getStarredContacts(Cursor cursor)446     List<String> getStarredContacts(Cursor cursor) {
447         List<String> starredContacts = new ArrayList<>();
448         if (cursor != null && cursor.moveToFirst()) {
449             do {
450                 String contact = cursor.getString(0);
451                 if (contact != null) {
452                     starredContacts.add(contact);
453                 }
454             } while (cursor.moveToNext());
455         }
456         return starredContacts;
457     }
458 
getStarredContacts()459     private List<String> getStarredContacts() {
460         Cursor cursor = null;
461         try {
462             cursor = queryData();
463             return getStarredContacts(cursor);
464         } finally {
465             if (cursor != null) {
466                 cursor.close();
467             }
468         }
469     }
470 
getStarredContactsSummary()471     public String getStarredContactsSummary() {
472         List<String> starredContacts = getStarredContacts();
473         int numStarredContacts = starredContacts.size();
474 
475         List<String> displayContacts = new ArrayList<>();
476 
477         if (numStarredContacts == 0) {
478             displayContacts.add(mContext.getString(R.string.zen_mode_from_none));
479         } else {
480             for (int i = 0; i < 2 && i < numStarredContacts; i++) {
481                 displayContacts.add(starredContacts.get(i));
482             }
483 
484             if (numStarredContacts == 3) {
485                 displayContacts.add(starredContacts.get(2));
486             } else if (numStarredContacts > 2) {
487                 displayContacts.add(mContext.getResources().getQuantityString(
488                         R.plurals.zen_mode_starred_contacts_summary_additional_contacts,
489                         numStarredContacts - 2, numStarredContacts - 2));
490             }
491         }
492 
493         // values in displayContacts must not be null
494         return ListFormatter.getInstance().format(displayContacts);
495     }
496 
queryData()497     private Cursor queryData() {
498         return mContext.getContentResolver().query(ContactsContract.Contacts.CONTENT_URI,
499                 new String[]{ContactsContract.Contacts.DISPLAY_NAME_PRIMARY},
500                 ContactsContract.Data.STARRED + "=1", null,
501                 ContactsContract.Data.TIMES_CONTACTED);
502     }
503 
504     @VisibleForTesting
505     public static final Comparator<Map.Entry<String, AutomaticZenRule>> RULE_COMPARATOR =
506             new Comparator<Map.Entry<String, AutomaticZenRule>>() {
507                 @Override
508                 public int compare(Map.Entry<String, AutomaticZenRule> lhs,
509                         Map.Entry<String, AutomaticZenRule> rhs) {
510                     // if it's a default rule, should be at the top of automatic rules
511                     boolean lhsIsDefaultRule = getDefaultRuleIds().contains(lhs.getKey());
512                     boolean rhsIsDefaultRule = getDefaultRuleIds().contains(rhs.getKey());
513                     if (lhsIsDefaultRule != rhsIsDefaultRule) {
514                         return lhsIsDefaultRule ? -1 : 1;
515                     }
516 
517                     int byDate = Long.compare(lhs.getValue().getCreationTime(),
518                             rhs.getValue().getCreationTime());
519                     if (byDate != 0) {
520                         return byDate;
521                     } else {
522                         return key(lhs.getValue()).compareTo(key(rhs.getValue()));
523                     }
524                 }
525 
526                 private String key(AutomaticZenRule rule) {
527                     final int type = ZenModeConfig.isValidScheduleConditionId(rule.getConditionId())
528                             ? 1 : ZenModeConfig.isValidEventConditionId(rule.getConditionId())
529                             ? 2 : 3;
530                     return type + rule.getName().toString();
531                 }
532             };
533 }
534