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