1 /* 2 * Copyright (C) 2015 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.content.ComponentName; 23 import android.content.Context; 24 import android.content.DialogInterface; 25 import android.content.Intent; 26 import android.content.pm.ApplicationInfo; 27 import android.content.pm.PackageManager; 28 import android.content.pm.ServiceInfo; 29 import android.content.res.Resources; 30 import android.graphics.drawable.Drawable; 31 import android.os.AsyncTask; 32 import android.os.Bundle; 33 import android.provider.Settings; 34 import android.service.notification.ConditionProviderService; 35 import android.service.notification.ZenModeConfig; 36 import android.support.v7.preference.Preference; 37 import android.support.v7.preference.Preference.OnPreferenceClickListener; 38 import android.support.v7.preference.PreferenceScreen; 39 import android.support.v7.preference.PreferenceViewHolder; 40 import android.view.View; 41 42 import com.android.internal.logging.MetricsLogger; 43 import com.android.internal.logging.MetricsProto.MetricsEvent; 44 import com.android.settings.R; 45 import com.android.settings.utils.ManagedServiceSettings.Config; 46 import com.android.settings.utils.ZenServiceListing; 47 48 import java.lang.ref.WeakReference; 49 import java.util.Arrays; 50 import java.util.Comparator; 51 import java.util.Map; 52 53 public class ZenModeAutomationSettings extends ZenModeSettingsBase { 54 55 static final Config CONFIG = getConditionProviderConfig(); 56 57 private PackageManager mPm; 58 private ZenServiceListing mServiceListing; 59 60 @Override onCreate(Bundle icicle)61 public void onCreate(Bundle icicle) { 62 super.onCreate(icicle); 63 addPreferencesFromResource(R.xml.zen_mode_automation_settings); 64 mPm = mContext.getPackageManager(); 65 mServiceListing = new ZenServiceListing(mContext, CONFIG); 66 mServiceListing.reloadApprovedServices(); 67 } 68 69 @Override onDestroy()70 public void onDestroy() { 71 super.onDestroy(); 72 } 73 74 @Override onZenModeChanged()75 protected void onZenModeChanged() { 76 // don't care 77 } 78 79 @Override onZenModeConfigChanged()80 protected void onZenModeConfigChanged() { 81 updateControls(); 82 } 83 84 @Override onResume()85 public void onResume() { 86 super.onResume(); 87 if (isUiRestricted()) { 88 return; 89 } 90 updateControls(); 91 } 92 showAddRuleDialog()93 private void showAddRuleDialog() { 94 new ZenRuleSelectionDialog(mContext, mServiceListing) { 95 @Override 96 public void onSystemRuleSelected(ZenRuleInfo ri) { 97 showNameRuleDialog(ri); 98 } 99 100 @Override 101 public void onExternalRuleSelected(ZenRuleInfo ri) { 102 Intent intent = new Intent().setComponent(ri.configurationActivity); 103 startActivity(intent); 104 } 105 }.show(); 106 } 107 showNameRuleDialog(final ZenRuleInfo ri)108 private void showNameRuleDialog(final ZenRuleInfo ri) { 109 new ZenRuleNameDialog(mContext, null) { 110 @Override 111 public void onOk(String ruleName) { 112 MetricsLogger.action(mContext, MetricsEvent.ACTION_ZEN_ADD_RULE_OK); 113 AutomaticZenRule rule = new AutomaticZenRule(ruleName, ri.serviceComponent, 114 ri.defaultConditionId, NotificationManager.INTERRUPTION_FILTER_PRIORITY, 115 true); 116 String savedRuleId = addZenRule(rule); 117 if (savedRuleId != null) { 118 startActivity(getRuleIntent(ri.settingsAction, null, savedRuleId)); 119 } 120 } 121 }.show(); 122 } 123 showDeleteRuleDialog(final String ruleId, final CharSequence ruleName)124 private void showDeleteRuleDialog(final String ruleId, final CharSequence ruleName) { 125 new AlertDialog.Builder(mContext) 126 .setMessage(getString(R.string.zen_mode_delete_rule_confirmation, ruleName)) 127 .setNegativeButton(R.string.cancel, null) 128 .setPositiveButton(R.string.zen_mode_delete_rule_button, 129 new DialogInterface.OnClickListener() { 130 @Override 131 public void onClick(DialogInterface dialog, int which) { 132 MetricsLogger.action(mContext, MetricsEvent.ACTION_ZEN_DELETE_RULE_OK); 133 removeZenRule(ruleId); 134 } 135 }) 136 .show(); 137 } 138 getRuleIntent(String settingsAction, ComponentName configurationActivity, String ruleId)139 private Intent getRuleIntent(String settingsAction, ComponentName configurationActivity, 140 String ruleId) { 141 Intent intent = new Intent() 142 .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP) 143 .putExtra(ConditionProviderService.EXTRA_RULE_ID, ruleId); 144 if (configurationActivity != null) { 145 intent.setComponent(configurationActivity); 146 } else { 147 intent.setAction(settingsAction); 148 } 149 return intent; 150 } 151 sortedRules()152 private Map.Entry<String,AutomaticZenRule>[] sortedRules() { 153 final Map.Entry<String,AutomaticZenRule>[] rt = 154 mRules.toArray(new Map.Entry[mRules.size()]); 155 Arrays.sort(rt, RULE_COMPARATOR); 156 return rt; 157 } 158 updateControls()159 private void updateControls() { 160 final PreferenceScreen root = getPreferenceScreen(); 161 root.removeAll(); 162 final Map.Entry<String,AutomaticZenRule>[] sortedRules = sortedRules(); 163 for (Map.Entry<String,AutomaticZenRule> sortedRule : sortedRules) { 164 ZenRulePreference pref = new ZenRulePreference(getPrefContext(), sortedRule); 165 if (pref.appExists) { 166 root.addPreference(pref); 167 } 168 } 169 final Preference p = new Preference(getPrefContext()); 170 p.setIcon(R.drawable.ic_add); 171 p.setTitle(R.string.zen_mode_add_rule); 172 p.setPersistent(false); 173 p.setOnPreferenceClickListener(new OnPreferenceClickListener() { 174 @Override 175 public boolean onPreferenceClick(Preference preference) { 176 MetricsLogger.action(mContext, MetricsEvent.ACTION_ZEN_ADD_RULE); 177 showAddRuleDialog(); 178 return true; 179 } 180 }); 181 root.addPreference(p); 182 } 183 184 @Override getMetricsCategory()185 protected int getMetricsCategory() { 186 return MetricsEvent.NOTIFICATION_ZEN_MODE_AUTOMATION; 187 } 188 computeRuleSummary(AutomaticZenRule rule, boolean isSystemRule, CharSequence providerLabel)189 private String computeRuleSummary(AutomaticZenRule rule, boolean isSystemRule, 190 CharSequence providerLabel) { 191 final String mode = computeZenModeCaption(getResources(), rule.getInterruptionFilter()); 192 final String ruleState = (rule == null || !rule.isEnabled()) 193 ? getString(R.string.switch_off_text) 194 : getString(R.string.zen_mode_rule_summary_enabled_combination, mode); 195 196 return isSystemRule ? ruleState 197 : getString(R.string.zen_mode_rule_summary_provider_combination, 198 providerLabel, ruleState); 199 } 200 getConditionProviderConfig()201 private static Config getConditionProviderConfig() { 202 final Config c = new Config(); 203 c.tag = TAG; 204 c.setting = Settings.Secure.ENABLED_NOTIFICATION_POLICY_ACCESS_PACKAGES; 205 c.secondarySetting = Settings.Secure.ENABLED_NOTIFICATION_LISTENERS; 206 c.intentAction = ConditionProviderService.SERVICE_INTERFACE; 207 c.permission = android.Manifest.permission.BIND_CONDITION_PROVIDER_SERVICE; 208 c.noun = "condition provider"; 209 return c; 210 } 211 computeZenModeCaption(Resources res, int zenMode)212 private static String computeZenModeCaption(Resources res, int zenMode) { 213 switch (zenMode) { 214 case NotificationManager.INTERRUPTION_FILTER_ALARMS: 215 return res.getString(R.string.zen_mode_option_alarms); 216 case NotificationManager.INTERRUPTION_FILTER_PRIORITY: 217 return res.getString(R.string.zen_mode_option_important_interruptions); 218 case NotificationManager.INTERRUPTION_FILTER_NONE: 219 return res.getString(R.string.zen_mode_option_no_interruptions); 220 default: 221 return null; 222 } 223 } 224 getRuleInfo(PackageManager pm, ServiceInfo si)225 public static ZenRuleInfo getRuleInfo(PackageManager pm, ServiceInfo si) { 226 if (si == null || si.metaData == null) return null; 227 final String ruleType = si.metaData.getString(ConditionProviderService.META_DATA_RULE_TYPE); 228 final ComponentName configurationActivity = getSettingsActivity(si); 229 if (ruleType != null && !ruleType.trim().isEmpty() && configurationActivity != null) { 230 final ZenRuleInfo ri = new ZenRuleInfo(); 231 ri.serviceComponent = new ComponentName(si.packageName, si.name); 232 ri.settingsAction = Settings.ACTION_ZEN_MODE_EXTERNAL_RULE_SETTINGS; 233 ri.title = ruleType; 234 ri.packageName = si.packageName; 235 ri.configurationActivity = getSettingsActivity(si); 236 ri.packageLabel = si.applicationInfo.loadLabel(pm); 237 ri.ruleInstanceLimit = 238 si.metaData.getInt(ConditionProviderService.META_DATA_RULE_INSTANCE_LIMIT, -1); 239 return ri; 240 } 241 return null; 242 } 243 getSettingsActivity(ServiceInfo si)244 private static ComponentName getSettingsActivity(ServiceInfo si) { 245 if (si == null || si.metaData == null) return null; 246 final String configurationActivity = 247 si.metaData.getString(ConditionProviderService.META_DATA_CONFIGURATION_ACTIVITY); 248 if (configurationActivity != null) { 249 return ComponentName.unflattenFromString(configurationActivity); 250 } 251 return null; 252 } 253 254 private static final Comparator<Map.Entry<String,AutomaticZenRule>> RULE_COMPARATOR = 255 new Comparator<Map.Entry<String,AutomaticZenRule>>() { 256 @Override 257 public int compare(Map.Entry<String,AutomaticZenRule> lhs, 258 Map.Entry<String,AutomaticZenRule> rhs) { 259 int byDate = Long.compare(lhs.getValue().getCreationTime(), 260 rhs.getValue().getCreationTime()); 261 if (byDate != 0) { 262 return byDate; 263 } else { 264 return key(lhs.getValue()).compareTo(key(rhs.getValue())); 265 } 266 } 267 268 private String key(AutomaticZenRule rule) { 269 final int type = ZenModeConfig.isValidScheduleConditionId(rule.getConditionId()) ? 1 270 : ZenModeConfig.isValidEventConditionId(rule.getConditionId()) ? 2 271 : 3; 272 return type + rule.getName().toString(); 273 } 274 }; 275 276 private class ZenRulePreference extends Preference { 277 final CharSequence mName; 278 final String mId; 279 final boolean appExists; 280 ZenRulePreference(Context context, final Map.Entry<String, AutomaticZenRule> ruleEntry)281 public ZenRulePreference(Context context, 282 final Map.Entry<String, AutomaticZenRule> ruleEntry) { 283 super(context); 284 285 final AutomaticZenRule rule = ruleEntry.getValue(); 286 mName = rule.getName(); 287 mId = ruleEntry.getKey(); 288 289 final boolean isSchedule = ZenModeConfig.isValidScheduleConditionId( 290 rule.getConditionId()); 291 final boolean isEvent = ZenModeConfig.isValidEventConditionId(rule.getConditionId()); 292 final boolean isSystemRule = isSchedule || isEvent; 293 294 try { 295 ApplicationInfo info = mPm.getApplicationInfo(rule.getOwner().getPackageName(), 0); 296 LoadIconTask task = new LoadIconTask(this); 297 task.execute(info); 298 setSummary(computeRuleSummary(rule, isSystemRule, info.loadLabel(mPm))); 299 } catch (PackageManager.NameNotFoundException e) { 300 setIcon(R.drawable.ic_label); 301 appExists = false; 302 return; 303 } 304 305 appExists = true; 306 setTitle(rule.getName()); 307 setPersistent(false); 308 309 final String action = isSchedule ? ZenModeScheduleRuleSettings.ACTION 310 : isEvent ? ZenModeEventRuleSettings.ACTION : ""; 311 ServiceInfo si = mServiceListing.findService(rule.getOwner()); 312 ComponentName settingsActivity = getSettingsActivity(si); 313 setIntent(getRuleIntent(action, settingsActivity, mId)); 314 setSelectable(settingsActivity != null || isSystemRule); 315 316 setWidgetLayoutResource(R.layout.zen_rule_widget); 317 } 318 319 @Override onBindViewHolder(PreferenceViewHolder view)320 public void onBindViewHolder(PreferenceViewHolder view) { 321 super.onBindViewHolder(view); 322 323 View v = view.findViewById(R.id.delete_zen_rule); 324 if (v != null) { 325 v.setOnClickListener(mDeleteListener); 326 } 327 view.setDividerAllowedAbove(true); 328 view.setDividerAllowedBelow(true); 329 } 330 331 private final View.OnClickListener mDeleteListener = new View.OnClickListener() { 332 @Override 333 public void onClick(View v) { 334 showDeleteRuleDialog(mId, mName); 335 } 336 }; 337 } 338 339 private class LoadIconTask extends AsyncTask<ApplicationInfo, Void, Drawable> { 340 private final WeakReference<Preference> prefReference; 341 LoadIconTask(Preference pref)342 public LoadIconTask(Preference pref) { 343 prefReference = new WeakReference<>(pref); 344 } 345 346 @Override doInBackground(ApplicationInfo... params)347 protected Drawable doInBackground(ApplicationInfo... params) { 348 return params[0].loadIcon(mPm); 349 } 350 351 @Override onPostExecute(Drawable icon)352 protected void onPostExecute(Drawable icon) { 353 if (icon != null) { 354 final Preference pref = prefReference.get(); 355 if (pref != null) { 356 pref.setIcon(icon); 357 } 358 } 359 } 360 } 361 362 } 363