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
17package com.android.settings.notification;
18
19import android.app.AlertDialog;
20import android.app.NotificationManager;
21import android.content.Context;
22import android.content.DialogInterface;
23import android.content.DialogInterface.OnDismissListener;
24import android.content.pm.ApplicationInfo;
25import android.content.pm.PackageManager;
26import android.content.pm.ServiceInfo;
27import android.graphics.drawable.Drawable;
28import android.os.AsyncTask;
29import android.service.notification.ZenModeConfig;
30import android.util.ArraySet;
31import android.util.Log;
32import android.view.LayoutInflater;
33import android.view.View;
34import android.widget.ImageView;
35import android.widget.LinearLayout;
36import android.widget.TextView;
37
38import com.android.settings.R;
39import com.android.settings.utils.ServiceListing;
40import com.android.settings.utils.ZenServiceListing;
41
42import java.lang.ref.WeakReference;
43import java.text.Collator;
44import java.util.ArrayList;
45import java.util.Collections;
46import java.util.Comparator;
47import java.util.List;
48import java.util.Set;
49import java.util.TreeSet;
50
51public 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
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
92    public void show() {
93        mDialog.show();
94    }
95
96    abstract public void onSystemRuleSelected(ZenRuleInfo ruleInfo);
97    abstract public void onExternalRuleSelected(ZenRuleInfo ruleInfo);
98
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
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
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
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 = ZenModeSettings.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
202        public LoadIconTask(ImageView view) {
203            viewReference = new WeakReference<>(view);
204        }
205
206        @Override
207        protected Drawable doInBackground(ApplicationInfo... params) {
208            return params[0].loadIcon(mPm);
209        }
210
211        @Override
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}