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