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.ZenAdapters; 34 import android.service.notification.ZenModeConfig; 35 import android.service.notification.ZenPolicy; 36 import android.util.Log; 37 38 import androidx.annotation.VisibleForTesting; 39 40 import com.android.settings.R; 41 42 import java.util.ArrayList; 43 import java.util.Arrays; 44 import java.util.Comparator; 45 import java.util.HashMap; 46 import java.util.List; 47 import java.util.Locale; 48 import java.util.Map; 49 50 public class ZenModeBackend { 51 @VisibleForTesting 52 protected static final String ZEN_MODE_FROM_ANYONE = "zen_mode_from_anyone"; 53 @VisibleForTesting 54 protected static final String ZEN_MODE_FROM_CONTACTS = "zen_mode_from_contacts"; 55 @VisibleForTesting 56 protected static final String ZEN_MODE_FROM_STARRED = "zen_mode_from_starred"; 57 @VisibleForTesting 58 protected static final String ZEN_MODE_FROM_NONE = "zen_mode_from_none"; 59 protected static final int SOURCE_NONE = -1; 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 static final 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 mNotificationManager.updateAutomaticZenRule(id, rule, /* fromUser= */ true); 99 } 100 setZenMode(int zenMode)101 protected void setZenMode(int zenMode) { 102 mNotificationManager.setZenMode(zenMode, null, TAG, /* fromUser= */ true); 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, /* fromUser= */ true); 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 int suppressedEffects = getNewSuppressedEffects(suppress, category); 163 savePolicy(mPolicy.priorityCategories, mPolicy.priorityCallSenders, 164 mPolicy.priorityMessageSenders, suppressedEffects, 165 mPolicy.priorityConversationSenders); 166 } 167 saveSoundPolicy(int category, boolean allow)168 protected void saveSoundPolicy(int category, boolean allow) { 169 int priorityCategories = getNewDefaultPriorityCategories(allow, category); 170 savePolicy(priorityCategories, mPolicy.priorityCallSenders, 171 mPolicy.priorityMessageSenders, mPolicy.suppressedVisualEffects, 172 mPolicy.priorityConversationSenders); 173 } 174 savePolicy(int priorityCategories, int priorityCallSenders, int priorityMessageSenders, int suppressedVisualEffects, int priorityConversationSenders)175 protected void savePolicy(int priorityCategories, int priorityCallSenders, 176 int priorityMessageSenders, int suppressedVisualEffects, 177 int priorityConversationSenders) { 178 mPolicy = new NotificationManager.Policy(priorityCategories, priorityCallSenders, 179 priorityMessageSenders, suppressedVisualEffects, priorityConversationSenders); 180 mNotificationManager.setNotificationPolicy(mPolicy, /* fromUser= */ true); 181 } 182 getNewSuppressedEffects(boolean suppress, int effectType)183 private int getNewSuppressedEffects(boolean suppress, int effectType) { 184 int effects = mPolicy.suppressedVisualEffects; 185 186 if (suppress) { 187 effects |= effectType; 188 } else { 189 effects &= ~effectType; 190 } 191 192 return clearDeprecatedEffects(effects); 193 } 194 clearDeprecatedEffects(int effects)195 private int clearDeprecatedEffects(int effects) { 196 return effects & ~(SUPPRESSED_EFFECT_SCREEN_ON | SUPPRESSED_EFFECT_SCREEN_OFF); 197 } 198 isEffectAllowed(int effect)199 protected boolean isEffectAllowed(int effect) { 200 return (mPolicy.suppressedVisualEffects & effect) == 0; 201 } 202 saveSenders(int category, int val)203 protected void saveSenders(int category, int val) { 204 int priorityCallSenders = getPriorityCallSenders(); 205 int priorityMessagesSenders = getPriorityMessageSenders(); 206 int categorySenders = getPrioritySenders(category); 207 208 final boolean allowSenders = val != SOURCE_NONE; 209 final int allowSendersFrom = val == SOURCE_NONE ? categorySenders : val; 210 211 String stringCategory = ""; 212 if (category == NotificationManager.Policy.PRIORITY_CATEGORY_CALLS) { 213 stringCategory = "Calls"; 214 priorityCallSenders = allowSendersFrom; 215 } 216 217 if (category == NotificationManager.Policy.PRIORITY_CATEGORY_MESSAGES) { 218 stringCategory = "Messages"; 219 priorityMessagesSenders = allowSendersFrom; 220 } 221 222 savePolicy(getNewDefaultPriorityCategories(allowSenders, category), 223 priorityCallSenders, priorityMessagesSenders, mPolicy.suppressedVisualEffects, 224 mPolicy.priorityConversationSenders); 225 226 if (ZenModeSettingsBase.DEBUG) { 227 Log.d(TAG, "onPrefChange allow" 228 + stringCategory + "=" + allowSenders + " allow" + stringCategory + "From=" 229 + ZenModeConfig.sourceToString(allowSendersFrom)); 230 } 231 } 232 saveConversationSenders(int val)233 protected void saveConversationSenders(int val) { 234 final boolean allowSenders = val != CONVERSATION_SENDERS_NONE; 235 236 savePolicy(getNewDefaultPriorityCategories(allowSenders, PRIORITY_CATEGORY_CONVERSATIONS), 237 mPolicy.priorityCallSenders, mPolicy.priorityMessageSenders, 238 mPolicy.suppressedVisualEffects, val); 239 240 } 241 getPrioritySenders(int category)242 private int getPrioritySenders(int category) { 243 int categorySenders = -1; 244 245 if (category == NotificationManager.Policy.PRIORITY_CATEGORY_CALLS) { 246 return getPriorityCallSenders(); 247 } 248 249 if (category == NotificationManager.Policy.PRIORITY_CATEGORY_MESSAGES) { 250 return getPriorityMessageSenders(); 251 } 252 253 if (category == NotificationManager.Policy.PRIORITY_CATEGORY_CONVERSATIONS) { 254 return getPriorityConversationSenders(); 255 } 256 257 return categorySenders; 258 } 259 getKeyFromZenPolicySetting(int contactType)260 protected static String getKeyFromZenPolicySetting(int contactType) { 261 switch (contactType) { 262 case ZenPolicy.PEOPLE_TYPE_ANYONE: 263 return ZEN_MODE_FROM_ANYONE; 264 case ZenPolicy.PEOPLE_TYPE_CONTACTS: 265 return ZEN_MODE_FROM_CONTACTS; 266 case ZenPolicy.PEOPLE_TYPE_STARRED: 267 return ZEN_MODE_FROM_STARRED; 268 case ZenPolicy.PEOPLE_TYPE_NONE: 269 default: 270 return ZEN_MODE_FROM_NONE; 271 } 272 } 273 getKeyFromSetting(int contactType)274 protected static String getKeyFromSetting(int contactType) { 275 switch (contactType) { 276 case NotificationManager.Policy.PRIORITY_SENDERS_ANY: 277 return ZEN_MODE_FROM_ANYONE; 278 case NotificationManager.Policy.PRIORITY_SENDERS_CONTACTS: 279 return ZEN_MODE_FROM_CONTACTS; 280 case NotificationManager.Policy.PRIORITY_SENDERS_STARRED: 281 return ZEN_MODE_FROM_STARRED; 282 case SOURCE_NONE: 283 default: 284 return ZEN_MODE_FROM_NONE; 285 } 286 } 287 getContactSettingFromZenPolicySetting(int setting)288 protected static int getContactSettingFromZenPolicySetting(int setting) { 289 switch (setting) { 290 case ZenPolicy.PEOPLE_TYPE_ANYONE: 291 return NotificationManager.Policy.PRIORITY_SENDERS_ANY; 292 case ZenPolicy.PEOPLE_TYPE_CONTACTS: 293 return NotificationManager.Policy.PRIORITY_SENDERS_CONTACTS; 294 case ZenPolicy.PEOPLE_TYPE_STARRED: 295 return NotificationManager.Policy.PRIORITY_SENDERS_STARRED; 296 case ZenPolicy.PEOPLE_TYPE_NONE: 297 default: 298 return SOURCE_NONE; 299 } 300 } 301 getAlarmsTotalSilencePeopleSummary(int category)302 protected int getAlarmsTotalSilencePeopleSummary(int category) { 303 if (category == NotificationManager.Policy.PRIORITY_CATEGORY_MESSAGES) { 304 return R.string.zen_mode_none_messages; 305 } else if (category == NotificationManager.Policy.PRIORITY_CATEGORY_CALLS) { 306 return R.string.zen_mode_none_calls; 307 } else if (category == NotificationManager.Policy.PRIORITY_CATEGORY_CONVERSATIONS) { 308 return R.string.zen_mode_from_no_conversations; 309 } 310 return R.string.zen_mode_from_no_conversations; 311 } 312 getContactsCallsSummary(ZenPolicy policy)313 protected int getContactsCallsSummary(ZenPolicy policy) { 314 int peopleType = policy.getPriorityCallSenders(); 315 switch (peopleType) { 316 case ZenPolicy.PEOPLE_TYPE_ANYONE: 317 return R.string.zen_mode_from_anyone; 318 case ZenPolicy.PEOPLE_TYPE_CONTACTS: 319 return R.string.zen_mode_from_contacts; 320 case ZenPolicy.PEOPLE_TYPE_STARRED: 321 return R.string.zen_mode_from_starred; 322 case ZenPolicy.PEOPLE_TYPE_NONE: 323 default: 324 return R.string.zen_mode_none_calls; 325 } 326 } 327 getContactsMessagesSummary(ZenPolicy policy)328 protected int getContactsMessagesSummary(ZenPolicy policy) { 329 int peopleType = policy.getPriorityMessageSenders(); 330 switch (peopleType) { 331 case ZenPolicy.PEOPLE_TYPE_ANYONE: 332 return R.string.zen_mode_from_anyone; 333 case ZenPolicy.PEOPLE_TYPE_CONTACTS: 334 return R.string.zen_mode_from_contacts; 335 case ZenPolicy.PEOPLE_TYPE_STARRED: 336 return R.string.zen_mode_from_starred; 337 case ZenPolicy.PEOPLE_TYPE_NONE: 338 default: 339 return R.string.zen_mode_none_messages; 340 } 341 } 342 getZenPolicySettingFromPrefKey(String key)343 protected static int getZenPolicySettingFromPrefKey(String key) { 344 switch (key) { 345 case ZEN_MODE_FROM_ANYONE: 346 return ZenPolicy.PEOPLE_TYPE_ANYONE; 347 case ZEN_MODE_FROM_CONTACTS: 348 return ZenPolicy.PEOPLE_TYPE_CONTACTS; 349 case ZEN_MODE_FROM_STARRED: 350 return ZenPolicy.PEOPLE_TYPE_STARRED; 351 case ZEN_MODE_FROM_NONE: 352 default: 353 return ZenPolicy.PEOPLE_TYPE_NONE; 354 } 355 } 356 removeZenRule(String ruleId)357 public boolean removeZenRule(String ruleId) { 358 return mNotificationManager.removeAutomaticZenRule(ruleId, /* fromUser= */ true); 359 } 360 getConsolidatedPolicy()361 public NotificationManager.Policy getConsolidatedPolicy() { 362 return NotificationManager.from(mContext).getConsolidatedNotificationPolicy(); 363 } 364 addZenRule(AutomaticZenRule rule)365 protected String addZenRule(AutomaticZenRule rule) { 366 try { 367 return mNotificationManager.addAutomaticZenRule(rule, /* fromUser= */ true); 368 } catch (Exception e) { 369 return null; 370 } 371 } 372 setDefaultZenPolicy(ZenPolicy zenPolicy)373 ZenPolicy setDefaultZenPolicy(ZenPolicy zenPolicy) { 374 int calls; 375 if (mPolicy.allowCalls()) { 376 calls = ZenAdapters.prioritySendersToPeopleType( 377 mPolicy.allowCallsFrom()); 378 } else { 379 calls = ZenPolicy.PEOPLE_TYPE_NONE; 380 } 381 382 int messages; 383 if (mPolicy.allowMessages()) { 384 messages = ZenAdapters.prioritySendersToPeopleType( 385 mPolicy.allowMessagesFrom()); 386 } else { 387 messages = ZenPolicy.PEOPLE_TYPE_NONE; 388 } 389 390 int conversations; 391 if (mPolicy.allowConversations()) { 392 // unlike the above, no mapping is needed because the values are the same 393 conversations = mPolicy.allowConversationsFrom(); 394 } else { 395 conversations = CONVERSATION_SENDERS_NONE; 396 } 397 398 return new ZenPolicy.Builder(zenPolicy) 399 .allowAlarms(mPolicy.allowAlarms()) 400 .allowCalls(calls) 401 .allowEvents(mPolicy.allowEvents()) 402 .allowMedia(mPolicy.allowMedia()) 403 .allowMessages(messages) 404 .allowConversations(conversations) 405 .allowReminders(mPolicy.allowReminders()) 406 .allowRepeatCallers(mPolicy.allowRepeatCallers()) 407 .allowSystem(mPolicy.allowSystem()) 408 .showFullScreenIntent(mPolicy.showFullScreenIntents()) 409 .showLights(mPolicy.showLights()) 410 .showInAmbientDisplay(mPolicy.showAmbient()) 411 .showInNotificationList(mPolicy.showInNotificationList()) 412 .showBadges(mPolicy.showBadges()) 413 .showPeeking(mPolicy.showPeeking()) 414 .showStatusBarIcons(mPolicy.showStatusBarIcons()) 415 .build(); 416 } 417 getAutomaticZenRules()418 protected Map.Entry<String, AutomaticZenRule>[] getAutomaticZenRules() { 419 Map<String, AutomaticZenRule> ruleMap = 420 NotificationManager.from(mContext).getAutomaticZenRules(); 421 final Map.Entry<String, AutomaticZenRule>[] rt = ruleMap.entrySet().toArray( 422 new Map.Entry[ruleMap.size()]); 423 Arrays.sort(rt, RULE_COMPARATOR); 424 return rt; 425 } 426 getAutomaticZenRule(String id)427 protected AutomaticZenRule getAutomaticZenRule(String id) { 428 return NotificationManager.from(mContext).getAutomaticZenRule(id); 429 } 430 getDefaultRuleIds()431 private static List<String> getDefaultRuleIds() { 432 return ZenModeConfig.getDefaultRuleIds(); 433 } 434 toNotificationPolicy(ZenPolicy policy)435 NotificationManager.Policy toNotificationPolicy(ZenPolicy policy) { 436 ZenModeConfig config = new ZenModeConfig(); 437 return config.toNotificationPolicy(policy); 438 } 439 440 @VisibleForTesting getStarredContacts(Cursor cursor)441 List<String> getStarredContacts(Cursor cursor) { 442 List<String> starredContacts = new ArrayList<>(); 443 if (cursor != null && cursor.moveToFirst()) { 444 do { 445 String contact = cursor.getString(0); 446 int emptyNameId = R.string.zen_mode_starred_contacts_empty_name; 447 starredContacts.add(contact != null ? contact : mContext.getString(emptyNameId)); 448 449 } while (cursor.moveToNext()); 450 } 451 return starredContacts; 452 } 453 getStarredContacts()454 private List<String> getStarredContacts() { 455 Cursor cursor = null; 456 try { 457 cursor = queryStarredContactsData(); 458 return getStarredContacts(cursor); 459 } finally { 460 if (cursor != null) { 461 cursor.close(); 462 } 463 } 464 } 465 getStarredContactsSummary(Context context)466 String getStarredContactsSummary(Context context) { 467 List<String> starredContacts = getStarredContacts(); 468 int numStarredContacts = starredContacts.size(); 469 MessageFormat msgFormat = new MessageFormat( 470 mContext.getString(R.string.zen_mode_starred_contacts_summary_contacts), 471 Locale.getDefault()); 472 Map<String, Object> args = new HashMap<>(); 473 args.put("count", numStarredContacts); 474 if (numStarredContacts >= 1) { 475 args.put("contact_1", starredContacts.get(0)); 476 if (numStarredContacts >= 2) { 477 args.put("contact_2", starredContacts.get(1)); 478 if (numStarredContacts == 3) { 479 args.put("contact_3", starredContacts.get(2)); 480 } 481 } 482 } 483 return msgFormat.format(args); 484 } 485 getContactsNumberSummary(Context context)486 String getContactsNumberSummary(Context context) { 487 MessageFormat msgFormat = new MessageFormat( 488 mContext.getString(R.string.zen_mode_contacts_count), 489 Locale.getDefault()); 490 Map<String, Object> args = new HashMap<>(); 491 args.put("count", queryAllContactsData().getCount()); 492 return msgFormat.format(args); 493 } 494 queryStarredContactsData()495 private Cursor queryStarredContactsData() { 496 return mContext.getContentResolver().query(ContactsContract.Contacts.CONTENT_URI, 497 new String[]{ContactsContract.Contacts.DISPLAY_NAME_PRIMARY}, 498 ContactsContract.Data.STARRED + "=1", null, 499 ContactsContract.Data.TIMES_CONTACTED); 500 } 501 queryAllContactsData()502 private Cursor queryAllContactsData() { 503 return mContext.getContentResolver().query(ContactsContract.Contacts.CONTENT_URI, 504 new String[]{ContactsContract.Contacts.DISPLAY_NAME_PRIMARY}, 505 null, null, null); 506 } 507 508 @VisibleForTesting 509 public static final Comparator<Map.Entry<String, AutomaticZenRule>> RULE_COMPARATOR = 510 new Comparator<Map.Entry<String, AutomaticZenRule>>() { 511 @Override 512 public int compare(Map.Entry<String, AutomaticZenRule> lhs, 513 Map.Entry<String, AutomaticZenRule> rhs) { 514 // if it's a default rule, should be at the top of automatic rules 515 boolean lhsIsDefaultRule = getDefaultRuleIds().contains(lhs.getKey()); 516 boolean rhsIsDefaultRule = getDefaultRuleIds().contains(rhs.getKey()); 517 if (lhsIsDefaultRule != rhsIsDefaultRule) { 518 return lhsIsDefaultRule ? -1 : 1; 519 } 520 521 int byDate = Long.compare(lhs.getValue().getCreationTime(), 522 rhs.getValue().getCreationTime()); 523 if (byDate != 0) { 524 return byDate; 525 } else { 526 return key(lhs.getValue()).compareTo(key(rhs.getValue())); 527 } 528 } 529 530 private String key(AutomaticZenRule rule) { 531 final int type = ZenModeConfig.isValidScheduleConditionId(rule.getConditionId()) 532 ? 1 : ZenModeConfig.isValidEventConditionId(rule.getConditionId()) 533 ? 2 : 3; 534 return type + rule.getName().toString(); 535 } 536 }; 537 } 538