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 getContactsCallsSummary(ZenPolicy policy)315 protected int getContactsCallsSummary(ZenPolicy policy) { 316 int peopleType = policy.getPriorityCallSenders(); 317 switch (peopleType) { 318 case ZenPolicy.PEOPLE_TYPE_ANYONE: 319 return R.string.zen_mode_from_anyone; 320 case ZenPolicy.PEOPLE_TYPE_CONTACTS: 321 return R.string.zen_mode_from_contacts; 322 case ZenPolicy.PEOPLE_TYPE_STARRED: 323 return R.string.zen_mode_from_starred; 324 case ZenPolicy.PEOPLE_TYPE_NONE: 325 default: 326 return R.string.zen_mode_none_calls; 327 } 328 } 329 getContactsMessagesSummary(ZenPolicy policy)330 protected int getContactsMessagesSummary(ZenPolicy policy) { 331 int peopleType = policy.getPriorityMessageSenders(); 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_messages; 342 } 343 } 344 getZenPolicySettingFromPrefKey(String key)345 protected static int getZenPolicySettingFromPrefKey(String key) { 346 switch (key) { 347 case ZEN_MODE_FROM_ANYONE: 348 return ZenPolicy.PEOPLE_TYPE_ANYONE; 349 case ZEN_MODE_FROM_CONTACTS: 350 return ZenPolicy.PEOPLE_TYPE_CONTACTS; 351 case ZEN_MODE_FROM_STARRED: 352 return ZenPolicy.PEOPLE_TYPE_STARRED; 353 case ZEN_MODE_FROM_NONE: 354 default: 355 return ZenPolicy.PEOPLE_TYPE_NONE; 356 } 357 } 358 removeZenRule(String ruleId)359 public boolean removeZenRule(String ruleId) { 360 return NotificationManager.from(mContext).removeAutomaticZenRule(ruleId); 361 } 362 getConsolidatedPolicy()363 public NotificationManager.Policy getConsolidatedPolicy() { 364 return NotificationManager.from(mContext).getConsolidatedNotificationPolicy(); 365 } 366 addZenRule(AutomaticZenRule rule)367 protected String addZenRule(AutomaticZenRule rule) { 368 try { 369 return NotificationManager.from(mContext).addAutomaticZenRule(rule); 370 } catch (Exception e) { 371 return null; 372 } 373 } 374 setDefaultZenPolicy(ZenPolicy zenPolicy)375 ZenPolicy setDefaultZenPolicy(ZenPolicy zenPolicy) { 376 int calls; 377 if (mPolicy.allowCalls()) { 378 calls = ZenModeConfig.getZenPolicySenders(mPolicy.allowCallsFrom()); 379 } else { 380 calls = ZenPolicy.PEOPLE_TYPE_NONE; 381 } 382 383 int messages; 384 if (mPolicy.allowMessages()) { 385 messages = ZenModeConfig.getZenPolicySenders(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 if (mDefaultRuleIds == null) { 433 mDefaultRuleIds = ZenModeConfig.DEFAULT_RULE_IDS; 434 } 435 return mDefaultRuleIds; 436 } 437 toNotificationPolicy(ZenPolicy policy)438 NotificationManager.Policy toNotificationPolicy(ZenPolicy policy) { 439 ZenModeConfig config = new ZenModeConfig(); 440 return config.toNotificationPolicy(policy); 441 } 442 443 @VisibleForTesting getStarredContacts(Cursor cursor)444 List<String> getStarredContacts(Cursor cursor) { 445 List<String> starredContacts = new ArrayList<>(); 446 if (cursor != null && cursor.moveToFirst()) { 447 do { 448 String contact = cursor.getString(0); 449 starredContacts.add(contact != null ? contact : 450 mContext.getString(R.string.zen_mode_starred_contacts_empty_name)); 451 452 } while (cursor.moveToNext()); 453 } 454 return starredContacts; 455 } 456 getStarredContacts()457 private List<String> getStarredContacts() { 458 Cursor cursor = null; 459 try { 460 cursor = queryStarredContactsData(); 461 return getStarredContacts(cursor); 462 } finally { 463 if (cursor != null) { 464 cursor.close(); 465 } 466 } 467 } 468 getStarredContactsSummary(Context context)469 String getStarredContactsSummary(Context context) { 470 List<String> starredContacts = getStarredContacts(); 471 int numStarredContacts = starredContacts.size(); 472 MessageFormat msgFormat = new MessageFormat( 473 mContext.getString(R.string.zen_mode_starred_contacts_summary_contacts), 474 Locale.getDefault()); 475 Map<String, Object> args = new HashMap<>(); 476 args.put("count", numStarredContacts); 477 if (numStarredContacts >= 1) { 478 args.put("contact_1", starredContacts.get(0)); 479 if (numStarredContacts >= 2) { 480 args.put("contact_2", starredContacts.get(1)); 481 if (numStarredContacts == 3) { 482 args.put("contact_3", starredContacts.get(2)); 483 } 484 } 485 } 486 return msgFormat.format(args); 487 } 488 getContactsNumberSummary(Context context)489 String getContactsNumberSummary(Context context) { 490 MessageFormat msgFormat = new MessageFormat( 491 mContext.getString(R.string.zen_mode_contacts_count), 492 Locale.getDefault()); 493 Map<String, Object> args = new HashMap<>(); 494 args.put("count", queryAllContactsData().getCount()); 495 return msgFormat.format(args); 496 } 497 queryStarredContactsData()498 private Cursor queryStarredContactsData() { 499 return mContext.getContentResolver().query(ContactsContract.Contacts.CONTENT_URI, 500 new String[]{ContactsContract.Contacts.DISPLAY_NAME_PRIMARY}, 501 ContactsContract.Data.STARRED + "=1", null, 502 ContactsContract.Data.TIMES_CONTACTED); 503 } 504 queryAllContactsData()505 private Cursor queryAllContactsData() { 506 return mContext.getContentResolver().query(ContactsContract.Contacts.CONTENT_URI, 507 new String[]{ContactsContract.Contacts.DISPLAY_NAME_PRIMARY}, 508 null, null, null); 509 } 510 511 @VisibleForTesting 512 public static final Comparator<Map.Entry<String, AutomaticZenRule>> RULE_COMPARATOR = 513 new Comparator<Map.Entry<String, AutomaticZenRule>>() { 514 @Override 515 public int compare(Map.Entry<String, AutomaticZenRule> lhs, 516 Map.Entry<String, AutomaticZenRule> rhs) { 517 // if it's a default rule, should be at the top of automatic rules 518 boolean lhsIsDefaultRule = getDefaultRuleIds().contains(lhs.getKey()); 519 boolean rhsIsDefaultRule = getDefaultRuleIds().contains(rhs.getKey()); 520 if (lhsIsDefaultRule != rhsIsDefaultRule) { 521 return lhsIsDefaultRule ? -1 : 1; 522 } 523 524 int byDate = Long.compare(lhs.getValue().getCreationTime(), 525 rhs.getValue().getCreationTime()); 526 if (byDate != 0) { 527 return byDate; 528 } else { 529 return key(lhs.getValue()).compareTo(key(rhs.getValue())); 530 } 531 } 532 533 private String key(AutomaticZenRule rule) { 534 final int type = ZenModeConfig.isValidScheduleConditionId(rule.getConditionId()) 535 ? 1 : ZenModeConfig.isValidEventConditionId(rule.getConditionId()) 536 ? 2 : 3; 537 return type + rule.getName().toString(); 538 } 539 }; 540 } 541