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.NotificationManager;
21 import android.content.Context;
22 import android.content.DialogInterface;
23 import android.content.DialogInterface.OnDismissListener;
24 import android.content.pm.ApplicationInfo;
25 import android.content.pm.PackageManager;
26 import android.content.pm.ServiceInfo;
27 import android.graphics.drawable.Drawable;
28 import android.os.AsyncTask;
29 import android.service.notification.ZenModeConfig;
30 import android.util.ArraySet;
31 import android.util.Log;
32 import android.view.LayoutInflater;
33 import android.view.View;
34 import android.widget.ImageView;
35 import android.widget.LinearLayout;
36 import android.widget.TextView;
37 
38 import com.android.settings.R;
39 import com.android.settings.utils.ServiceListing;
40 import com.android.settings.utils.ZenServiceListing;
41 
42 import java.lang.ref.WeakReference;
43 import java.text.Collator;
44 import java.util.ArrayList;
45 import java.util.Collections;
46 import java.util.Comparator;
47 import java.util.List;
48 import java.util.Set;
49 import java.util.TreeSet;
50 
51 public abstract class ZenRuleSelectionDialog {
52     private static final String TAG = "ZenRuleSelectionDialog";
53     private static final boolean DEBUG = ZenModeSettings.DEBUG;
54 
55     private final Context mContext;
56     private final PackageManager mPm;
57     private NotificationManager mNm;
58     private final AlertDialog mDialog;
59     private final LinearLayout mRuleContainer;
60     private final ZenServiceListing mServiceListing;
61 
ZenRuleSelectionDialog(Context context, ZenServiceListing serviceListing)62     public ZenRuleSelectionDialog(Context context, ZenServiceListing serviceListing) {
63         mContext = context;
64         mPm = context.getPackageManager();
65         mNm = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
66         mServiceListing = serviceListing;
67         final View v =
68                 LayoutInflater.from(context).inflate(R.layout.zen_rule_type_selection, null, false);
69 
70         mRuleContainer = (LinearLayout) v.findViewById(R.id.rule_container);
71         if (mServiceListing != null) {
72             bindType(defaultNewEvent());
73             bindType(defaultNewSchedule());
74             mServiceListing.addZenCallback(mServiceListingCallback);
75             mServiceListing.reloadApprovedServices();
76         }
77         mDialog = new AlertDialog.Builder(context)
78                 .setTitle(R.string.zen_mode_choose_rule_type)
79                 .setView(v)
80                 .setOnDismissListener(new OnDismissListener() {
81                     @Override
82                     public void onDismiss(DialogInterface dialog) {
83                         if (mServiceListing != null) {
84                             mServiceListing.removeZenCallback(mServiceListingCallback);
85                         }
86                     }
87                 })
88                 .setNegativeButton(R.string.cancel, null)
89                 .create();
90     }
91 
show()92     public void show() {
93         mDialog.show();
94     }
95 
onSystemRuleSelected(ZenRuleInfo ruleInfo)96     abstract public void onSystemRuleSelected(ZenRuleInfo ruleInfo);
onExternalRuleSelected(ZenRuleInfo ruleInfo)97     abstract public void onExternalRuleSelected(ZenRuleInfo ruleInfo);
98 
bindType(final ZenRuleInfo ri)99     private void bindType(final ZenRuleInfo ri) {
100         try {
101             ApplicationInfo info = mPm.getApplicationInfo(ri.packageName, 0);
102             final LinearLayout v = (LinearLayout) LayoutInflater.from(mContext).inflate(
103                     R.layout.zen_rule_type, null, false);
104 
105             LoadIconTask task = new LoadIconTask((ImageView) v.findViewById(R.id.icon));
106             task.execute(info);
107             ((TextView) v.findViewById(R.id.title)).setText(ri.title);
108             if (!ri.isSystem) {
109                 TextView subtitle = (TextView) v.findViewById(R.id.subtitle);
110                 subtitle.setText(info.loadLabel(mPm));
111                 subtitle.setVisibility(View.VISIBLE);
112             }
113             v.setOnClickListener(new View.OnClickListener() {
114                 @Override
115                 public void onClick(View v) {
116                     mDialog.dismiss();
117                     if (ri.isSystem) {
118                         onSystemRuleSelected(ri);
119                     } else {
120                         onExternalRuleSelected(ri);
121                     }
122                 }
123             });
124             mRuleContainer.addView(v);
125         } catch (PackageManager.NameNotFoundException e) {
126             // Omit rule.
127         }
128     }
129 
defaultNewSchedule()130     private ZenRuleInfo defaultNewSchedule() {
131         final ZenModeConfig.ScheduleInfo schedule = new ZenModeConfig.ScheduleInfo();
132         schedule.days = ZenModeConfig.ALL_DAYS;
133         schedule.startHour = 22;
134         schedule.endHour = 7;
135         final ZenRuleInfo rt = new ZenRuleInfo();
136         rt.settingsAction = ZenModeScheduleRuleSettings.ACTION;
137         rt.title = mContext.getString(R.string.zen_schedule_rule_type_name);
138         rt.packageName = ZenModeConfig.getEventConditionProvider().getPackageName();
139         rt.defaultConditionId = ZenModeConfig.toScheduleConditionId(schedule);
140         rt.serviceComponent = ZenModeConfig.getScheduleConditionProvider();
141         rt.isSystem = true;
142         return rt;
143     }
144 
defaultNewEvent()145     private ZenRuleInfo defaultNewEvent() {
146         final ZenModeConfig.EventInfo event = new ZenModeConfig.EventInfo();
147         event.calendar = null; // any calendar
148         event.reply = ZenModeConfig.EventInfo.REPLY_ANY_EXCEPT_NO;
149         final ZenRuleInfo rt = new ZenRuleInfo();
150         rt.settingsAction = ZenModeEventRuleSettings.ACTION;
151         rt.title = mContext.getString(R.string.zen_event_rule_type_name);
152         rt.packageName = ZenModeConfig.getScheduleConditionProvider().getPackageName();
153         rt.defaultConditionId = ZenModeConfig.toEventConditionId(event);
154         rt.serviceComponent = ZenModeConfig.getEventConditionProvider();
155         rt.isSystem = true;
156         return rt;
157     }
158 
bindExternalRules(Set<ZenRuleInfo> externalRuleTypes)159     private void bindExternalRules(Set<ZenRuleInfo> externalRuleTypes) {
160         for (ZenRuleInfo ri : externalRuleTypes) {
161             bindType(ri);
162         }
163     }
164 
165     private final ZenServiceListing.Callback mServiceListingCallback = new
166             ZenServiceListing.Callback() {
167         @Override
168         public void onServicesReloaded(Set<ServiceInfo> services) {
169             if (DEBUG) Log.d(TAG, "Services reloaded: count=" + services.size());
170             Set<ZenRuleInfo> externalRuleTypes = new TreeSet<>(RULE_TYPE_COMPARATOR);
171             for (ServiceInfo serviceInfo : services) {
172                 final ZenRuleInfo ri = ZenModeAutomationSettings.getRuleInfo(mPm, serviceInfo);
173                 if (ri != null && ri.configurationActivity != null
174                         && mNm.isNotificationPolicyAccessGrantedForPackage(ri.packageName)
175                         && (ri.ruleInstanceLimit <= 0 || ri.ruleInstanceLimit
176                         >= (mNm.getRuleInstanceCount(serviceInfo.getComponentName()) + 1))) {
177                     externalRuleTypes.add(ri);
178                 }
179             }
180             bindExternalRules(externalRuleTypes);
181         }
182     };
183 
184     private static final Comparator<ZenRuleInfo> RULE_TYPE_COMPARATOR =
185             new Comparator<ZenRuleInfo>() {
186                 private final Collator mCollator = Collator.getInstance();
187 
188                 @Override
189                 public int compare(ZenRuleInfo lhs, ZenRuleInfo rhs) {
190                     int byAppName = mCollator.compare(lhs.packageLabel, rhs.packageLabel);
191                     if (byAppName != 0) {
192                         return byAppName;
193                     } else {
194                         return mCollator.compare(lhs.title, rhs.title);
195                     }
196                 }
197             };
198 
199     private class LoadIconTask extends AsyncTask<ApplicationInfo, Void, Drawable> {
200         private final WeakReference<ImageView> viewReference;
201 
LoadIconTask(ImageView view)202         public LoadIconTask(ImageView view) {
203             viewReference = new WeakReference<>(view);
204         }
205 
206         @Override
doInBackground(ApplicationInfo... params)207         protected Drawable doInBackground(ApplicationInfo... params) {
208             return params[0].loadIcon(mPm);
209         }
210 
211         @Override
onPostExecute(Drawable icon)212         protected void onPostExecute(Drawable icon) {
213             if (icon != null) {
214                 final ImageView view = viewReference.get();
215                 if (view != null) {
216                     view.setImageDrawable(icon);
217                 }
218             }
219         }
220     }
221 }