1 /* 2 * Copyright (C) 2014 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 android.app.AlertDialog; 20 import android.app.AutomaticZenRule; 21 import android.app.NotificationManager; 22 import android.app.NotificationManager.Policy; 23 import android.content.ComponentName; 24 import android.content.Context; 25 import android.content.DialogInterface; 26 import android.content.Intent; 27 import android.content.pm.ApplicationInfo; 28 import android.content.pm.PackageManager; 29 import android.content.pm.ServiceInfo; 30 import android.content.res.Resources; 31 import android.graphics.drawable.Drawable; 32 import android.os.AsyncTask; 33 import android.os.Bundle; 34 import android.provider.Settings; 35 import android.service.notification.ConditionProviderService; 36 import android.service.notification.ZenModeConfig; 37 import com.android.settings.utils.ManagedServiceSettings; 38 import com.android.settings.utils.ZenServiceListing; 39 import android.support.annotation.VisibleForTesting; 40 import android.support.v7.preference.Preference; 41 import android.support.v7.preference.PreferenceCategory; 42 import android.support.v7.preference.PreferenceScreen; 43 import android.support.v7.preference.PreferenceViewHolder; 44 import android.view.View; 45 46 import com.android.internal.logging.nano.MetricsProto.MetricsEvent; 47 import com.android.settings.R; 48 49 import java.lang.ref.WeakReference; 50 import java.util.Arrays; 51 import java.util.Comparator; 52 import java.util.Map; 53 import java.util.Map.Entry; 54 55 public class ZenModeSettings extends ZenModeSettingsBase { 56 57 public static final String KEY_VISUAL_SETTINGS = "visual_interruptions_settings"; 58 59 private static final String KEY_PRIORITY_SETTINGS = "priority_settings"; 60 private static final String KEY_AUTOMATIC_RULES = "automatic_rules"; 61 62 static final ManagedServiceSettings.Config CONFIG = getConditionProviderConfig(); 63 64 private PreferenceCategory mAutomaticRules; 65 private Preference mPrioritySettings; 66 private Preference mVisualSettings; 67 private Policy mPolicy; 68 private SummaryBuilder mSummaryBuilder; 69 private PackageManager mPm; 70 private ZenServiceListing mServiceListing; 71 72 @Override onCreate(Bundle savedInstanceState)73 public void onCreate(Bundle savedInstanceState) { 74 super.onCreate(savedInstanceState); 75 76 addPreferencesFromResource(R.xml.zen_mode_settings); 77 final PreferenceScreen root = getPreferenceScreen(); 78 79 mAutomaticRules = (PreferenceCategory) root.findPreference(KEY_AUTOMATIC_RULES); 80 mPrioritySettings = root.findPreference(KEY_PRIORITY_SETTINGS); 81 mVisualSettings = root.findPreference(KEY_VISUAL_SETTINGS); 82 mPolicy = NotificationManager.from(mContext).getNotificationPolicy(); 83 mSummaryBuilder = new SummaryBuilder(getContext()); 84 mPm = mContext.getPackageManager(); 85 mServiceListing = new ZenServiceListing(mContext, CONFIG); 86 mServiceListing.reloadApprovedServices(); 87 } 88 89 @Override onResume()90 public void onResume() { 91 super.onResume(); 92 if (isUiRestricted()) { 93 return; 94 } 95 updateControls(); 96 } 97 98 @Override getMetricsCategory()99 public int getMetricsCategory() { 100 return MetricsEvent.NOTIFICATION_ZEN_MODE; 101 } 102 103 @Override onZenModeChanged()104 protected void onZenModeChanged() { 105 updateControls(); 106 } 107 108 @Override onZenModeConfigChanged()109 protected void onZenModeConfigChanged() { 110 mPolicy = NotificationManager.from(mContext).getNotificationPolicy(); 111 updateControls(); 112 } 113 updateControls()114 private void updateControls() { 115 updatePrioritySettingsSummary(); 116 updateVisualSettingsSummary(); 117 updateAutomaticRules(); 118 } 119 updatePrioritySettingsSummary()120 private void updatePrioritySettingsSummary() { 121 mPrioritySettings.setSummary(mSummaryBuilder.getPrioritySettingSummary(mPolicy)); 122 } 123 updateVisualSettingsSummary()124 private void updateVisualSettingsSummary() { 125 mVisualSettings.setSummary(mSummaryBuilder.getVisualSettingSummary(mPolicy)); 126 } 127 updateAutomaticRules()128 private void updateAutomaticRules() { 129 mAutomaticRules.removeAll(); 130 final Map.Entry<String,AutomaticZenRule>[] sortedRules = sortedRules(); 131 for (Map.Entry<String,AutomaticZenRule> sortedRule : sortedRules) { 132 ZenRulePreference pref = new ZenRulePreference(getPrefContext(), sortedRule); 133 if (pref.appExists) { 134 mAutomaticRules.addPreference(pref); 135 } 136 } 137 final Preference p = new Preference(getPrefContext()); 138 p.setIcon(R.drawable.ic_add); 139 p.setTitle(R.string.zen_mode_add_rule); 140 p.setPersistent(false); 141 p.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() { 142 @Override 143 public boolean onPreferenceClick(Preference preference) { 144 mMetricsFeatureProvider.action(mContext, MetricsEvent.ACTION_ZEN_ADD_RULE); 145 showAddRuleDialog(); 146 return true; 147 } 148 }); 149 mAutomaticRules.addPreference(p); 150 } 151 showAddRuleDialog()152 private void showAddRuleDialog() { 153 new ZenRuleSelectionDialog(mContext, mServiceListing) { 154 @Override 155 public void onSystemRuleSelected(ZenRuleInfo ri) { 156 showNameRuleDialog(ri); 157 } 158 159 @Override 160 public void onExternalRuleSelected(ZenRuleInfo ri) { 161 Intent intent = new Intent().setComponent(ri.configurationActivity); 162 startActivity(intent); 163 } 164 }.show(); 165 } 166 computeRuleSummary(AutomaticZenRule rule, boolean isSystemRule, CharSequence providerLabel)167 private String computeRuleSummary(AutomaticZenRule rule, boolean isSystemRule, 168 CharSequence providerLabel) { 169 final String mode = computeZenModeCaption(getResources(), rule.getInterruptionFilter()); 170 final String ruleState = (rule == null || !rule.isEnabled()) 171 ? getString(R.string.switch_off_text) 172 : getString(R.string.zen_mode_rule_summary_enabled_combination, mode); 173 174 return isSystemRule ? ruleState 175 : getString(R.string.zen_mode_rule_summary_provider_combination, 176 providerLabel, ruleState); 177 } 178 getConditionProviderConfig()179 private static ManagedServiceSettings.Config getConditionProviderConfig() { 180 final ManagedServiceSettings.Config c = new ManagedServiceSettings.Config(); 181 c.tag = TAG; 182 c.setting = Settings.Secure.ENABLED_NOTIFICATION_POLICY_ACCESS_PACKAGES; 183 c.secondarySetting = Settings.Secure.ENABLED_NOTIFICATION_LISTENERS; 184 c.intentAction = ConditionProviderService.SERVICE_INTERFACE; 185 c.permission = android.Manifest.permission.BIND_CONDITION_PROVIDER_SERVICE; 186 c.noun = "condition provider"; 187 return c; 188 } 189 computeZenModeCaption(Resources res, int zenMode)190 private static String computeZenModeCaption(Resources res, int zenMode) { 191 switch (zenMode) { 192 case NotificationManager.INTERRUPTION_FILTER_ALARMS: 193 return res.getString(R.string.zen_mode_option_alarms); 194 case NotificationManager.INTERRUPTION_FILTER_PRIORITY: 195 return res.getString(R.string.zen_mode_option_important_interruptions); 196 case NotificationManager.INTERRUPTION_FILTER_NONE: 197 return res.getString(R.string.zen_mode_option_no_interruptions); 198 default: 199 return null; 200 } 201 } 202 getRuleInfo(PackageManager pm, ServiceInfo si)203 public static ZenRuleInfo getRuleInfo(PackageManager pm, ServiceInfo si) { 204 if (si == null || si.metaData == null) return null; 205 final String ruleType = si.metaData.getString(ConditionProviderService.META_DATA_RULE_TYPE); 206 final ComponentName configurationActivity = getSettingsActivity(si); 207 if (ruleType != null && !ruleType.trim().isEmpty() && configurationActivity != null) { 208 final ZenRuleInfo ri = new ZenRuleInfo(); 209 ri.serviceComponent = new ComponentName(si.packageName, si.name); 210 ri.settingsAction = Settings.ACTION_ZEN_MODE_EXTERNAL_RULE_SETTINGS; 211 ri.title = ruleType; 212 ri.packageName = si.packageName; 213 ri.configurationActivity = getSettingsActivity(si); 214 ri.packageLabel = si.applicationInfo.loadLabel(pm); 215 ri.ruleInstanceLimit = 216 si.metaData.getInt(ConditionProviderService.META_DATA_RULE_INSTANCE_LIMIT, -1); 217 return ri; 218 } 219 return null; 220 } 221 getSettingsActivity(ServiceInfo si)222 private static ComponentName getSettingsActivity(ServiceInfo si) { 223 if (si == null || si.metaData == null) return null; 224 final String configurationActivity = 225 si.metaData.getString(ConditionProviderService.META_DATA_CONFIGURATION_ACTIVITY); 226 if (configurationActivity != null) { 227 return ComponentName.unflattenFromString(configurationActivity); 228 } 229 return null; 230 } 231 showNameRuleDialog(final ZenRuleInfo ri)232 private void showNameRuleDialog(final ZenRuleInfo ri) { 233 new ZenRuleNameDialog(mContext, null) { 234 @Override 235 public void onOk(String ruleName) { 236 mMetricsFeatureProvider.action(mContext, MetricsEvent.ACTION_ZEN_ADD_RULE_OK); 237 AutomaticZenRule rule = new AutomaticZenRule(ruleName, ri.serviceComponent, 238 ri.defaultConditionId, NotificationManager.INTERRUPTION_FILTER_PRIORITY, 239 true); 240 String savedRuleId = addZenRule(rule); 241 if (savedRuleId != null) { 242 startActivity(getRuleIntent(ri.settingsAction, null, savedRuleId)); 243 } 244 } 245 }.show(); 246 } 247 showDeleteRuleDialog(final String ruleId, final CharSequence ruleName)248 private void showDeleteRuleDialog(final String ruleId, final CharSequence ruleName) { 249 new AlertDialog.Builder(mContext) 250 .setMessage(getString(R.string.zen_mode_delete_rule_confirmation, ruleName)) 251 .setNegativeButton(R.string.cancel, null) 252 .setPositiveButton(R.string.zen_mode_delete_rule_button, 253 new DialogInterface.OnClickListener() { 254 @Override 255 public void onClick(DialogInterface dialog, int which) { 256 mMetricsFeatureProvider.action(mContext, 257 MetricsEvent.ACTION_ZEN_DELETE_RULE_OK); 258 removeZenRule(ruleId); 259 } 260 }) 261 .show(); 262 } 263 getRuleIntent(String settingsAction, ComponentName configurationActivity, String ruleId)264 private Intent getRuleIntent(String settingsAction, ComponentName configurationActivity, 265 String ruleId) { 266 Intent intent = new Intent() 267 .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP) 268 .putExtra(ConditionProviderService.EXTRA_RULE_ID, ruleId); 269 if (configurationActivity != null) { 270 intent.setComponent(configurationActivity); 271 } else { 272 intent.setAction(settingsAction); 273 } 274 return intent; 275 } 276 sortedRules()277 private Map.Entry<String,AutomaticZenRule>[] sortedRules() { 278 final Map.Entry<String,AutomaticZenRule>[] rt = 279 mRules.toArray(new Map.Entry[mRules.size()]); 280 Arrays.sort(rt, RULE_COMPARATOR); 281 return rt; 282 } 283 284 @Override getHelpResource()285 protected int getHelpResource() { 286 return R.string.help_uri_interruptions; 287 } 288 289 private class ZenRulePreference extends Preference { 290 final CharSequence mName; 291 final String mId; 292 final boolean appExists; 293 ZenRulePreference(Context context, final Map.Entry<String, AutomaticZenRule> ruleEntry)294 public ZenRulePreference(Context context, 295 final Map.Entry<String, AutomaticZenRule> ruleEntry) { 296 super(context); 297 298 final AutomaticZenRule rule = ruleEntry.getValue(); 299 mName = rule.getName(); 300 mId = ruleEntry.getKey(); 301 302 final boolean isSchedule = ZenModeConfig.isValidScheduleConditionId( 303 rule.getConditionId()); 304 final boolean isEvent = ZenModeConfig.isValidEventConditionId(rule.getConditionId()); 305 final boolean isSystemRule = isSchedule || isEvent; 306 307 try { 308 ApplicationInfo info = mPm.getApplicationInfo(rule.getOwner().getPackageName(), 0); 309 LoadIconTask task = new LoadIconTask(this); 310 task.execute(info); 311 setSummary(computeRuleSummary(rule, isSystemRule, info.loadLabel(mPm))); 312 } catch (PackageManager.NameNotFoundException e) { 313 setIcon(R.drawable.ic_label); 314 appExists = false; 315 return; 316 } 317 318 appExists = true; 319 setTitle(rule.getName()); 320 setPersistent(false); 321 322 final String action = isSchedule ? ZenModeScheduleRuleSettings.ACTION 323 : isEvent ? ZenModeEventRuleSettings.ACTION : ""; 324 ServiceInfo si = mServiceListing.findService(rule.getOwner()); 325 ComponentName settingsActivity = getSettingsActivity(si); 326 setIntent(getRuleIntent(action, settingsActivity, mId)); 327 setSelectable(settingsActivity != null || isSystemRule); 328 329 setWidgetLayoutResource(R.layout.zen_rule_widget); 330 } 331 332 @Override onBindViewHolder(PreferenceViewHolder view)333 public void onBindViewHolder(PreferenceViewHolder view) { 334 super.onBindViewHolder(view); 335 336 View v = view.findViewById(R.id.delete_zen_rule); 337 if (v != null) { 338 v.setOnClickListener(mDeleteListener); 339 } 340 } 341 342 private final View.OnClickListener mDeleteListener = new View.OnClickListener() { 343 @Override 344 public void onClick(View v) { 345 showDeleteRuleDialog(mId, mName); 346 } 347 }; 348 } 349 350 private class LoadIconTask extends AsyncTask<ApplicationInfo, Void, Drawable> { 351 private final WeakReference<Preference> prefReference; 352 LoadIconTask(Preference pref)353 public LoadIconTask(Preference pref) { 354 prefReference = new WeakReference<>(pref); 355 } 356 357 @Override doInBackground(ApplicationInfo... params)358 protected Drawable doInBackground(ApplicationInfo... params) { 359 return params[0].loadIcon(mPm); 360 } 361 362 @Override onPostExecute(Drawable icon)363 protected void onPostExecute(Drawable icon) { 364 if (icon != null) { 365 final Preference pref = prefReference.get(); 366 if (pref != null) { 367 pref.setIcon(icon); 368 } 369 } 370 } 371 } 372 373 public static class SummaryBuilder { 374 375 private Context mContext; 376 SummaryBuilder(Context context)377 public SummaryBuilder(Context context) { 378 mContext = context; 379 } 380 getPrioritySettingSummary(Policy policy)381 String getPrioritySettingSummary(Policy policy) { 382 String s = mContext.getString(R.string.zen_mode_alarms); 383 s = append(s, isCategoryEnabled(policy, Policy.PRIORITY_CATEGORY_REMINDERS), 384 R.string.zen_mode_reminders); 385 s = append(s, isCategoryEnabled(policy, Policy.PRIORITY_CATEGORY_EVENTS), 386 R.string.zen_mode_events); 387 if (isCategoryEnabled(policy, Policy.PRIORITY_CATEGORY_MESSAGES)) { 388 if (policy.priorityMessageSenders == Policy.PRIORITY_SENDERS_ANY) { 389 s = append(s, true, R.string.zen_mode_all_messages); 390 } else { 391 s = append(s, true, R.string.zen_mode_selected_messages); 392 } 393 } 394 if (isCategoryEnabled(policy, Policy.PRIORITY_CATEGORY_CALLS)) { 395 if (policy.priorityCallSenders == Policy.PRIORITY_SENDERS_ANY) { 396 s = append(s, true, R.string.zen_mode_all_callers); 397 } else { 398 s = append(s, true, R.string.zen_mode_selected_callers); 399 } 400 } else if (isCategoryEnabled(policy, Policy.PRIORITY_CATEGORY_REPEAT_CALLERS)) { 401 s = append(s, true, R.string.zen_mode_repeat_callers); 402 } 403 return s; 404 } 405 getVisualSettingSummary(Policy policy)406 String getVisualSettingSummary(Policy policy) { 407 String s = mContext.getString(R.string.zen_mode_all_visual_interruptions); 408 if (isEffectSuppressed(policy, Policy.SUPPRESSED_EFFECT_SCREEN_ON) 409 && isEffectSuppressed(policy, Policy.SUPPRESSED_EFFECT_SCREEN_OFF)) { 410 s = mContext.getString(R.string.zen_mode_no_visual_interruptions); 411 } else if (isEffectSuppressed(policy, Policy.SUPPRESSED_EFFECT_SCREEN_ON)) { 412 s = mContext.getString(R.string.zen_mode_screen_on_visual_interruptions); 413 } else if (isEffectSuppressed(policy, Policy.SUPPRESSED_EFFECT_SCREEN_OFF)) { 414 s = mContext.getString(R.string.zen_mode_screen_off_visual_interruptions); 415 } 416 return s; 417 } 418 getAutomaticRulesSummary()419 String getAutomaticRulesSummary() { 420 final int count = getEnabledAutomaticRulesCount(); 421 return count == 0 ? mContext.getString(R.string.zen_mode_settings_summary_off) 422 : mContext.getResources().getQuantityString( 423 R.plurals.zen_mode_settings_summary_on, count, count); 424 } 425 426 @VisibleForTesting getEnabledAutomaticRulesCount()427 int getEnabledAutomaticRulesCount() { 428 int count = 0; 429 final Map<String, AutomaticZenRule> ruleMap = 430 NotificationManager.from(mContext).getAutomaticZenRules(); 431 if (ruleMap != null) { 432 for (Entry<String, AutomaticZenRule> ruleEntry : ruleMap.entrySet()) { 433 final AutomaticZenRule rule = ruleEntry.getValue(); 434 if (rule != null && rule.isEnabled()) { 435 count++; 436 } 437 } 438 } 439 return count; 440 } 441 442 @VisibleForTesting append(String s, boolean condition, int resId)443 String append(String s, boolean condition, int resId) { 444 if (condition) { 445 return mContext.getString( 446 R.string.join_many_items_middle, s, mContext.getString(resId)); 447 } 448 return s; 449 } 450 isCategoryEnabled(Policy policy, int categoryType)451 private boolean isCategoryEnabled(Policy policy, int categoryType) { 452 return (policy.priorityCategories & categoryType) != 0; 453 } 454 isEffectSuppressed(Policy policy, int effect)455 private boolean isEffectSuppressed(Policy policy, int effect) { 456 return (policy.suppressedVisualEffects & effect) != 0; 457 } 458 } 459 460 private static final Comparator<Map.Entry<String,AutomaticZenRule>> RULE_COMPARATOR = 461 new Comparator<Map.Entry<String,AutomaticZenRule>>() { 462 @Override 463 public int compare(Map.Entry<String,AutomaticZenRule> lhs, 464 Map.Entry<String,AutomaticZenRule> rhs) { 465 int byDate = Long.compare(lhs.getValue().getCreationTime(), 466 rhs.getValue().getCreationTime()); 467 if (byDate != 0) { 468 return byDate; 469 } else { 470 return key(lhs.getValue()).compareTo(key(rhs.getValue())); 471 } 472 } 473 474 private String key(AutomaticZenRule rule) { 475 final int type = ZenModeConfig.isValidScheduleConditionId(rule.getConditionId()) 476 ? 1 477 : ZenModeConfig.isValidEventConditionId(rule.getConditionId()) 478 ? 2 479 : 3; 480 return type + rule.getName().toString(); 481 } 482 }; 483 } 484