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 getAlarmsTotalSilencePeopleSummary(int category)290 protected int getAlarmsTotalSilencePeopleSummary(int category) { 291 if (category == NotificationManager.Policy.PRIORITY_CATEGORY_MESSAGES) { 292 return R.string.zen_mode_none_messages; 293 } else if (category == NotificationManager.Policy.PRIORITY_CATEGORY_CALLS){ 294 return R.string.zen_mode_none_calls; 295 } else if (category == NotificationManager.Policy.PRIORITY_CATEGORY_CONVERSATIONS) { 296 return R.string.zen_mode_from_no_conversations; 297 } 298 return R.string.zen_mode_from_no_conversations; 299 } 300 getConversationSummary()301 protected int getConversationSummary() { 302 int conversationType = getPriorityConversationSenders(); 303 304 switch (conversationType) { 305 case NotificationManager.Policy.CONVERSATION_SENDERS_ANYONE: 306 return R.string.zen_mode_from_all_conversations; 307 case NotificationManager.Policy.CONVERSATION_SENDERS_IMPORTANT: 308 return R.string.zen_mode_from_important_conversations; 309 case NotificationManager.Policy.CONVERSATION_SENDERS_NONE: 310 return R.string.zen_mode_from_no_conversations; 311 default: 312 return R.string.zen_mode_from_no_conversations; 313 } 314 } 315 getContactsCallsSummary(ZenPolicy policy)316 protected int getContactsCallsSummary(ZenPolicy policy) { 317 int peopleType = policy.getPriorityCallSenders(); 318 switch (peopleType) { 319 case ZenPolicy.PEOPLE_TYPE_ANYONE: 320 return R.string.zen_mode_from_anyone; 321 case ZenPolicy.PEOPLE_TYPE_CONTACTS: 322 return R.string.zen_mode_from_contacts; 323 case ZenPolicy.PEOPLE_TYPE_STARRED: 324 return R.string.zen_mode_from_starred; 325 case ZenPolicy.PEOPLE_TYPE_NONE: 326 default: 327 return R.string.zen_mode_none_calls; 328 } 329 } 330 getContactsMessagesSummary(ZenPolicy policy)331 protected int getContactsMessagesSummary(ZenPolicy policy) { 332 int peopleType = policy.getPriorityMessageSenders(); 333 switch (peopleType) { 334 case ZenPolicy.PEOPLE_TYPE_ANYONE: 335 return R.string.zen_mode_from_anyone; 336 case ZenPolicy.PEOPLE_TYPE_CONTACTS: 337 return R.string.zen_mode_from_contacts; 338 case ZenPolicy.PEOPLE_TYPE_STARRED: 339 return R.string.zen_mode_from_starred; 340 case ZenPolicy.PEOPLE_TYPE_NONE: 341 default: 342 return R.string.zen_mode_none_messages; 343 } 344 } 345 getZenPolicySettingFromPrefKey(String key)346 protected static int getZenPolicySettingFromPrefKey(String key) { 347 switch (key) { 348 case ZEN_MODE_FROM_ANYONE: 349 return ZenPolicy.PEOPLE_TYPE_ANYONE; 350 case ZEN_MODE_FROM_CONTACTS: 351 return ZenPolicy.PEOPLE_TYPE_CONTACTS; 352 case ZEN_MODE_FROM_STARRED: 353 return ZenPolicy.PEOPLE_TYPE_STARRED; 354 case ZEN_MODE_FROM_NONE: 355 default: 356 return ZenPolicy.PEOPLE_TYPE_NONE; 357 } 358 } 359 removeZenRule(String ruleId)360 public boolean removeZenRule(String ruleId) { 361 return NotificationManager.from(mContext).removeAutomaticZenRule(ruleId); 362 } 363 getConsolidatedPolicy()364 public NotificationManager.Policy getConsolidatedPolicy() { 365 return NotificationManager.from(mContext).getConsolidatedNotificationPolicy(); 366 } 367 addZenRule(AutomaticZenRule rule)368 protected String addZenRule(AutomaticZenRule rule) { 369 try { 370 return NotificationManager.from(mContext).addAutomaticZenRule(rule); 371 } catch (Exception e) { 372 return null; 373 } 374 } 375 setDefaultZenPolicy(ZenPolicy zenPolicy)376 ZenPolicy setDefaultZenPolicy(ZenPolicy zenPolicy) { 377 int calls; 378 if (mPolicy.allowCalls()) { 379 calls = ZenModeConfig.getZenPolicySenders(mPolicy.allowCallsFrom()); 380 } else { 381 calls = ZenPolicy.PEOPLE_TYPE_NONE; 382 } 383 384 int messages; 385 if (mPolicy.allowMessages()) { 386 messages = ZenModeConfig.getZenPolicySenders(mPolicy.allowMessagesFrom()); 387 } else { 388 messages = ZenPolicy.PEOPLE_TYPE_NONE; 389 } 390 391 int conversations; 392 if (mPolicy.allowConversations()) { 393 // unlike the above, no mapping is needed because the values are the same 394 conversations = mPolicy.allowConversationsFrom(); 395 } else { 396 conversations = CONVERSATION_SENDERS_NONE; 397 } 398 399 return new ZenPolicy.Builder(zenPolicy) 400 .allowAlarms(mPolicy.allowAlarms()) 401 .allowCalls(calls) 402 .allowEvents(mPolicy.allowEvents()) 403 .allowMedia(mPolicy.allowMedia()) 404 .allowMessages(messages) 405 .allowConversations(conversations) 406 .allowReminders(mPolicy.allowReminders()) 407 .allowRepeatCallers(mPolicy.allowRepeatCallers()) 408 .allowSystem(mPolicy.allowSystem()) 409 .showFullScreenIntent(mPolicy.showFullScreenIntents()) 410 .showLights(mPolicy.showLights()) 411 .showInAmbientDisplay(mPolicy.showAmbient()) 412 .showInNotificationList(mPolicy.showInNotificationList()) 413 .showBadges(mPolicy.showBadges()) 414 .showPeeking(mPolicy.showPeeking()) 415 .showStatusBarIcons(mPolicy.showStatusBarIcons()) 416 .build(); 417 } 418 getAutomaticZenRules()419 protected Map.Entry<String, AutomaticZenRule>[] getAutomaticZenRules() { 420 Map<String, AutomaticZenRule> ruleMap = 421 NotificationManager.from(mContext).getAutomaticZenRules(); 422 final Map.Entry<String, AutomaticZenRule>[] rt = ruleMap.entrySet().toArray( 423 new Map.Entry[ruleMap.size()]); 424 Arrays.sort(rt, RULE_COMPARATOR); 425 return rt; 426 } 427 getAutomaticZenRule(String id)428 protected AutomaticZenRule getAutomaticZenRule(String id) { 429 return NotificationManager.from(mContext).getAutomaticZenRule(id); 430 } 431 getDefaultRuleIds()432 private static List<String> getDefaultRuleIds() { 433 if (mDefaultRuleIds == null) { 434 mDefaultRuleIds = ZenModeConfig.DEFAULT_RULE_IDS; 435 } 436 return mDefaultRuleIds; 437 } 438 toNotificationPolicy(ZenPolicy policy)439 NotificationManager.Policy toNotificationPolicy(ZenPolicy policy) { 440 ZenModeConfig config = new ZenModeConfig(); 441 return config.toNotificationPolicy(policy); 442 } 443 444 @VisibleForTesting getStarredContacts(Cursor cursor)445 List<String> getStarredContacts(Cursor cursor) { 446 List<String> starredContacts = new ArrayList<>(); 447 if (cursor != null && cursor.moveToFirst()) { 448 do { 449 String contact = cursor.getString(0); 450 starredContacts.add(contact != null ? contact : 451 mContext.getString(R.string.zen_mode_starred_contacts_empty_name)); 452 453 } while (cursor.moveToNext()); 454 } 455 return starredContacts; 456 } 457 getStarredContacts()458 private List<String> getStarredContacts() { 459 Cursor cursor = null; 460 try { 461 cursor = queryStarredContactsData(); 462 return getStarredContacts(cursor); 463 } finally { 464 if (cursor != null) { 465 cursor.close(); 466 } 467 } 468 } 469 getStarredContactsSummary(Context context)470 String getStarredContactsSummary(Context context) { 471 List<String> starredContacts = getStarredContacts(); 472 int numStarredContacts = starredContacts.size(); 473 MessageFormat msgFormat = new MessageFormat( 474 mContext.getString(R.string.zen_mode_starred_contacts_summary_contacts), 475 Locale.getDefault()); 476 Map<String, Object> args = new HashMap<>(); 477 args.put("count", numStarredContacts); 478 if (numStarredContacts >= 1) { 479 args.put("contact_1", starredContacts.get(0)); 480 if (numStarredContacts >= 2) { 481 args.put("contact_2", starredContacts.get(1)); 482 if (numStarredContacts == 3) { 483 args.put("contact_3", starredContacts.get(2)); 484 } 485 } 486 } 487 return msgFormat.format(args); 488 } 489 getContactsNumberSummary(Context context)490 String getContactsNumberSummary(Context context) { 491 MessageFormat msgFormat = new MessageFormat( 492 mContext.getString(R.string.zen_mode_contacts_count), 493 Locale.getDefault()); 494 Map<String, Object> args = new HashMap<>(); 495 args.put("count", queryAllContactsData().getCount()); 496 return msgFormat.format(args); 497 } 498 queryStarredContactsData()499 private Cursor queryStarredContactsData() { 500 return mContext.getContentResolver().query(ContactsContract.Contacts.CONTENT_URI, 501 new String[]{ContactsContract.Contacts.DISPLAY_NAME_PRIMARY}, 502 ContactsContract.Data.STARRED + "=1", null, 503 ContactsContract.Data.TIMES_CONTACTED); 504 } 505 queryAllContactsData()506 private Cursor queryAllContactsData() { 507 return mContext.getContentResolver().query(ContactsContract.Contacts.CONTENT_URI, 508 new String[]{ContactsContract.Contacts.DISPLAY_NAME_PRIMARY}, 509 null, null, null); 510 } 511 512 @VisibleForTesting 513 public static final Comparator<Map.Entry<String, AutomaticZenRule>> RULE_COMPARATOR = 514 new Comparator<Map.Entry<String, AutomaticZenRule>>() { 515 @Override 516 public int compare(Map.Entry<String, AutomaticZenRule> lhs, 517 Map.Entry<String, AutomaticZenRule> rhs) { 518 // if it's a default rule, should be at the top of automatic rules 519 boolean lhsIsDefaultRule = getDefaultRuleIds().contains(lhs.getKey()); 520 boolean rhsIsDefaultRule = getDefaultRuleIds().contains(rhs.getKey()); 521 if (lhsIsDefaultRule != rhsIsDefaultRule) { 522 return lhsIsDefaultRule ? -1 : 1; 523 } 524 525 int byDate = Long.compare(lhs.getValue().getCreationTime(), 526 rhs.getValue().getCreationTime()); 527 if (byDate != 0) { 528 return byDate; 529 } else { 530 return key(lhs.getValue()).compareTo(key(rhs.getValue())); 531 } 532 } 533 534 private String key(AutomaticZenRule rule) { 535 final int type = ZenModeConfig.isValidScheduleConditionId(rule.getConditionId()) 536 ? 1 : ZenModeConfig.isValidEventConditionId(rule.getConditionId()) 537 ? 2 : 3; 538 return type + rule.getName().toString(); 539 } 540 }; 541 } 542