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