• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.zen;
18 
19 import android.app.Dialog;
20 import android.app.NotificationManager;
21 import android.app.settings.SettingsEnums;
22 import android.content.Context;
23 import android.content.DialogInterface;
24 import android.content.Intent;
25 import android.content.pm.ApplicationInfo;
26 import android.content.pm.ComponentInfo;
27 import android.content.pm.PackageManager;
28 import android.content.pm.ResolveInfo;
29 import android.graphics.drawable.Drawable;
30 import android.os.AsyncTask;
31 import android.os.Bundle;
32 import android.service.notification.ZenModeConfig;
33 import android.util.Log;
34 import android.view.LayoutInflater;
35 import android.view.View;
36 import android.widget.ImageView;
37 import android.widget.LinearLayout;
38 import android.widget.TextView;
39 
40 import androidx.appcompat.app.AlertDialog;
41 import androidx.fragment.app.Fragment;
42 
43 import com.android.settings.R;
44 import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
45 import com.android.settings.utils.ZenServiceListing;
46 
47 import java.lang.ref.WeakReference;
48 import java.text.Collator;
49 import java.util.Comparator;
50 import java.util.List;
51 import java.util.Set;
52 import java.util.TreeSet;
53 
54 public class ZenRuleSelectionDialog extends InstrumentedDialogFragment {
55     private static final String TAG = "ZenRuleSelectionDialog";
56     private static final boolean DEBUG = ZenModeSettings.DEBUG;
57 
58     private static ZenServiceListing mServiceListing;
59     protected static PositiveClickListener mPositiveClickListener;
60 
61     private static Context mContext;
62     private static PackageManager mPm;
63     private static NotificationManager mNm;
64     private LinearLayout mRuleContainer;
65 
66     /**
67      * The interface we expect a listener to implement.
68      */
69     public interface PositiveClickListener {
onSystemRuleSelected(ZenRuleInfo ruleInfo, Fragment parent)70         void onSystemRuleSelected(ZenRuleInfo ruleInfo, Fragment parent);
onExternalRuleSelected(ZenRuleInfo ruleInfo, Fragment parent)71         void onExternalRuleSelected(ZenRuleInfo ruleInfo, Fragment parent);
72     }
73 
74     @Override
getMetricsCategory()75     public int getMetricsCategory() {
76         return SettingsEnums.NOTIFICATION_ZEN_MODE_RULE_SELECTION_DIALOG;
77     }
78 
show(Context context, Fragment parent, PositiveClickListener listener, ZenServiceListing serviceListing)79     public static void show(Context context, Fragment parent, PositiveClickListener
80             listener, ZenServiceListing serviceListing) {
81         mPositiveClickListener = listener;
82         mContext = context;
83         mPm = mContext.getPackageManager();
84         mNm = (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE);
85         mServiceListing = serviceListing;
86 
87         ZenRuleSelectionDialog dialog = new ZenRuleSelectionDialog();
88         dialog.setTargetFragment(parent, 0);
89         dialog.show(parent.getFragmentManager(), TAG);
90     }
91 
92     @Override
onCreateDialog(Bundle savedInstanceState)93     public Dialog onCreateDialog(Bundle savedInstanceState) {
94         final View v = LayoutInflater.from(getContext()).inflate(R.layout.zen_rule_type_selection,
95                 null, false);
96 
97         mRuleContainer = (LinearLayout) v.findViewById(R.id.rule_container);
98         if (mServiceListing != null) {
99             bindType(defaultNewEvent());
100             bindType(defaultNewSchedule());
101             mServiceListing.addZenCallback(mServiceListingCallback);
102             mServiceListing.reloadApprovedServices();
103         }
104         return new AlertDialog.Builder(getContext())
105                 .setTitle(R.string.zen_mode_choose_rule_type)
106                 .setView(v)
107                 .setNegativeButton(R.string.cancel, null)
108                 .create();
109     }
110 
111     @Override
onDismiss(DialogInterface dialog)112     public void onDismiss(DialogInterface dialog) {
113         super.onDismiss(dialog);
114         if (mServiceListing != null) {
115             mServiceListing.removeZenCallback(mServiceListingCallback);
116         }
117     }
118 
119     // Returns whether the rule's configuration activity exists and is valid.
isRuleActivityValid(final ZenRuleInfo ri)120     private boolean isRuleActivityValid(final ZenRuleInfo ri) {
121         Intent intent = new Intent().setComponent(ri.configurationActivity);
122         List<ResolveInfo> results = mPm.queryIntentActivities(
123                 intent, PackageManager.ResolveInfoFlags.of(0));
124         return intent.resolveActivity(mPm) != null && results.size() > 0;
125     }
126 
bindType(final ZenRuleInfo ri)127     private void bindType(final ZenRuleInfo ri) {
128         try {
129             ApplicationInfo info = mPm.getApplicationInfo(ri.packageName, 0);
130             final LinearLayout v = (LinearLayout) LayoutInflater.from(mContext).inflate(
131                     R.layout.zen_rule_type, null, false);
132 
133             ImageView iconView = v.findViewById(R.id.icon);
134             ((TextView) v.findViewById(R.id.title)).setText(ri.title);
135             if (!ri.isSystem) {
136                 // Omit rule if the externally provided rule activity is not valid.
137                 if (!isRuleActivityValid(ri)) {
138                     Log.w(TAG, "rule configuration activity invalid: " + ri.configurationActivity);
139                     return;
140                 }
141                 LoadIconTask task = new LoadIconTask(iconView);
142                 task.execute(info);
143 
144                 TextView subtitle = (TextView) v.findViewById(R.id.subtitle);
145                 subtitle.setText(info.loadLabel(mPm));
146                 subtitle.setVisibility(View.VISIBLE);
147             } else {
148                 if (ZenModeConfig.isValidScheduleConditionId(ri.defaultConditionId)) {
149                     iconView.setImageDrawable(mContext.getDrawable(R.drawable.ic_timelapse));
150                 } else if (ZenModeConfig.isValidEventConditionId(ri.defaultConditionId)) {
151                     iconView.setImageDrawable(mContext.getDrawable(R.drawable.ic_event));
152                 }
153             }
154             v.setOnClickListener(new View.OnClickListener() {
155                 @Override
156                 public void onClick(View v) {
157                     dismiss();
158                     if (ri.isSystem) {
159                         mPositiveClickListener.onSystemRuleSelected(ri, getTargetFragment());
160                     } else {
161                         mPositiveClickListener.onExternalRuleSelected(ri, getTargetFragment());
162                     }
163                 }
164             });
165             mRuleContainer.addView(v);
166         } catch (PackageManager.NameNotFoundException e) {
167             // Omit rule.
168         }
169     }
170 
defaultNewSchedule()171     private ZenRuleInfo defaultNewSchedule() {
172         final ZenModeConfig.ScheduleInfo schedule = new ZenModeConfig.ScheduleInfo();
173         schedule.days = ZenModeConfig.ALL_DAYS;
174         schedule.startHour = 22;
175         schedule.endHour = 7;
176         final ZenRuleInfo rt = new ZenRuleInfo();
177         rt.settingsAction = ZenModeScheduleRuleSettings.ACTION;
178         rt.title = mContext.getString(R.string.zen_schedule_rule_type_name);
179         rt.packageName = ZenModeConfig.getEventConditionProvider().getPackageName();
180         rt.defaultConditionId = ZenModeConfig.toScheduleConditionId(schedule);
181         rt.serviceComponent = ZenModeConfig.getScheduleConditionProvider();
182         rt.isSystem = true;
183         return rt;
184     }
185 
defaultNewEvent()186     private ZenRuleInfo defaultNewEvent() {
187         final ZenModeConfig.EventInfo event = new ZenModeConfig.EventInfo();
188         event.calName = null; // any calendar
189         event.calendarId = null;
190         event.reply = ZenModeConfig.EventInfo.REPLY_ANY_EXCEPT_NO;
191         final ZenRuleInfo rt = new ZenRuleInfo();
192         rt.settingsAction = ZenModeEventRuleSettings.ACTION;
193         rt.title = mContext.getString(R.string.zen_event_rule_type_name);
194         rt.packageName = ZenModeConfig.getScheduleConditionProvider().getPackageName();
195         rt.defaultConditionId = ZenModeConfig.toEventConditionId(event);
196         rt.serviceComponent = ZenModeConfig.getEventConditionProvider();
197         rt.isSystem = true;
198         return rt;
199     }
200 
bindExternalRules(Set<ZenRuleInfo> externalRuleTypes)201     private void bindExternalRules(Set<ZenRuleInfo> externalRuleTypes) {
202         for (ZenRuleInfo ri : externalRuleTypes) {
203             bindType(ri);
204         }
205     }
206 
207     private final ZenServiceListing.Callback mServiceListingCallback = new
208             ZenServiceListing.Callback() {
209         @Override
210         public void onComponentsReloaded(Set<ComponentInfo> componentInfos) {
211             if (DEBUG) Log.d(TAG, "Reloaded: count=" + componentInfos.size());
212 
213             Set<ZenRuleInfo> externalRuleTypes = new TreeSet<>(RULE_TYPE_COMPARATOR);
214             for (ComponentInfo ci : componentInfos) {
215                 final ZenRuleInfo ri = AbstractZenModeAutomaticRulePreferenceController.
216                         getRuleInfo(mPm, ci);
217                 if (ri != null && ri.configurationActivity != null
218                         && mNm.isNotificationPolicyAccessGrantedForPackage(ri.packageName)
219                         && (ri.ruleInstanceLimit <= 0 || ri.ruleInstanceLimit
220                         >= (mNm.getRuleInstanceCount(ci.getComponentName()) + 1))) {
221                     externalRuleTypes.add(ri);
222                 }
223             }
224             bindExternalRules(externalRuleTypes);
225         }
226     };
227 
228     private static final Comparator<ZenRuleInfo> RULE_TYPE_COMPARATOR =
229             new Comparator<ZenRuleInfo>() {
230                 private final Collator mCollator = Collator.getInstance();
231 
232                 @Override
233                 public int compare(ZenRuleInfo lhs, ZenRuleInfo rhs) {
234                     int byAppName = mCollator.compare(lhs.packageLabel, rhs.packageLabel);
235                     if (byAppName != 0) {
236                         return byAppName;
237                     } else {
238                         return mCollator.compare(lhs.title, rhs.title);
239                     }
240                 }
241             };
242 
243     private class LoadIconTask extends AsyncTask<ApplicationInfo, Void, Drawable> {
244         private final WeakReference<ImageView> viewReference;
245 
LoadIconTask(ImageView view)246         public LoadIconTask(ImageView view) {
247             viewReference = new WeakReference<>(view);
248         }
249 
250         @Override
doInBackground(ApplicationInfo... params)251         protected Drawable doInBackground(ApplicationInfo... params) {
252             return params[0].loadIcon(mPm);
253         }
254 
255         @Override
onPostExecute(Drawable icon)256         protected void onPostExecute(Drawable icon) {
257             if (icon != null) {
258                 final ImageView view = viewReference.get();
259                 if (view != null) {
260                     view.setImageDrawable(icon);
261                 }
262             }
263         }
264     }
265 }