Home | History | Annotate | Download | only in notification
      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
     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
     70     public void onDestroy() {
     71         super.onDestroy();
     72     }
     73 
     74     @Override
     75     protected void onZenModeChanged() {
     76         // don't care
     77     }
     78 
     79     @Override
     80     protected void onZenModeConfigChanged() {
     81         updateControls();
     82     }
     83 
     84     @Override
     85     public void onResume() {
     86         super.onResume();
     87         if (isUiRestricted()) {
     88             return;
     89         }
     90         updateControls();
     91     }
     92 
     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 
    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 
    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 
    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 
    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 
    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
    185     protected int getMetricsCategory() {
    186         return MetricsEvent.NOTIFICATION_ZEN_MODE_AUTOMATION;
    187     }
    188 
    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 
    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 
    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 
    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 
    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 
    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
    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 
    342         public LoadIconTask(Preference pref) {
    343             prefReference = new WeakReference<>(pref);
    344         }
    345 
    346         @Override
    347         protected Drawable doInBackground(ApplicationInfo... params) {
    348             return params[0].loadIcon(mPm);
    349         }
    350 
    351         @Override
    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