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.AutomaticZenRule;
20 import android.content.Context;
21 import android.content.pm.PackageManager.NameNotFoundException;
22 import android.database.Cursor;
23 import android.os.UserHandle;
24 import android.os.UserManager;
25 import android.provider.CalendarContract.Calendars;
26 import android.provider.Settings;
27 import android.service.notification.ZenModeConfig;
28 import android.service.notification.ZenModeConfig.EventInfo;
29 import android.support.v7.preference.DropDownPreference;
30 import android.support.v7.preference.Preference;
31 import android.support.v7.preference.Preference.OnPreferenceChangeListener;
32 import android.support.v7.preference.PreferenceScreen;
33 
34 import com.android.internal.logging.MetricsProto.MetricsEvent;
35 import com.android.settings.R;
36 
37 import java.util.ArrayList;
38 import java.util.Collections;
39 import java.util.Comparator;
40 import java.util.List;
41 
42 public class ZenModeEventRuleSettings extends ZenModeRuleSettingsBase {
43     private static final String KEY_CALENDAR = "calendar";
44     private static final String KEY_REPLY = "reply";
45 
46     public static final String ACTION = Settings.ACTION_ZEN_MODE_EVENT_RULE_SETTINGS;
47 
48     private DropDownPreference mCalendar;
49     private DropDownPreference mReply;
50 
51     private EventInfo mEvent;
52     private List<CalendarInfo> mCalendars;
53     private boolean mCreate;
54 
55     @Override
setRule(AutomaticZenRule rule)56     protected boolean setRule(AutomaticZenRule rule) {
57         mEvent = rule != null ? ZenModeConfig.tryParseEventConditionId(rule.getConditionId())
58                 : null;
59         return mEvent != null;
60     }
61 
62     @Override
getZenModeDependency()63     protected String getZenModeDependency() {
64         return null;
65     }
66 
67     @Override
getEnabledToastText()68     protected int getEnabledToastText() {
69         return R.string.zen_event_rule_enabled_toast;
70     }
71 
72     @Override
onResume()73     public void onResume() {
74         super.onResume();
75         if (isUiRestricted()) {
76             return;
77         }
78         if (!mCreate) {
79             reloadCalendar();
80         }
81         mCreate = false;
82     }
83 
reloadCalendar()84     private void reloadCalendar() {
85         mCalendars = getCalendars(mContext);
86         ArrayList<CharSequence> entries = new ArrayList<>();
87         ArrayList<CharSequence> values = new ArrayList<>();
88         entries.add(getString(R.string.zen_mode_event_rule_calendar_any));
89         values.add(key(0, null));
90         final String eventCalendar = mEvent != null ? mEvent.calendar : null;
91         boolean found = false;
92         for (CalendarInfo calendar : mCalendars) {
93             entries.add(calendar.name);
94             values.add(key(calendar));
95             if (eventCalendar != null && eventCalendar.equals(calendar.name)) {
96                 found = true;
97             }
98         }
99         if (eventCalendar != null && !found) {
100             entries.add(eventCalendar);
101             values.add(key(mEvent.userId, eventCalendar));
102         }
103         mCalendar.setEntries(entries.toArray(new CharSequence[entries.size()]));
104         mCalendar.setEntryValues(values.toArray(new CharSequence[values.size()]));
105     }
106 
107     @Override
onCreateInternal()108     protected void onCreateInternal() {
109         mCreate = true;
110         addPreferencesFromResource(R.xml.zen_mode_event_rule_settings);
111         final PreferenceScreen root = getPreferenceScreen();
112 
113         mCalendar = (DropDownPreference) root.findPreference(KEY_CALENDAR);
114         mCalendar.setOnPreferenceChangeListener(new OnPreferenceChangeListener() {
115             @Override
116             public boolean onPreferenceChange(Preference preference, Object newValue) {
117                 final String calendarKey = (String) newValue;
118                 if (calendarKey.equals(key(mEvent))) return false;
119                 final int i = calendarKey.indexOf(':');
120                 mEvent.userId = Integer.parseInt(calendarKey.substring(0, i));
121                 mEvent.calendar = calendarKey.substring(i + 1);
122                 if (mEvent.calendar.isEmpty()) {
123                     mEvent.calendar = null;
124                 }
125                 updateRule(ZenModeConfig.toEventConditionId(mEvent));
126                 return true;
127             }
128         });
129 
130         mReply = (DropDownPreference) root.findPreference(KEY_REPLY);
131         mReply.setEntries(new CharSequence[] {
132                 getString(R.string.zen_mode_event_rule_reply_any_except_no),
133                 getString(R.string.zen_mode_event_rule_reply_yes_or_maybe),
134                 getString(R.string.zen_mode_event_rule_reply_yes),
135         });
136         mReply.setEntryValues(new CharSequence[] {
137                 Integer.toString(EventInfo.REPLY_ANY_EXCEPT_NO),
138                 Integer.toString(EventInfo.REPLY_YES_OR_MAYBE),
139                 Integer.toString(EventInfo.REPLY_YES),
140         });
141         mReply.setOnPreferenceChangeListener(new OnPreferenceChangeListener() {
142             @Override
143             public boolean onPreferenceChange(Preference preference, Object newValue) {
144                 final int reply = Integer.parseInt((String) newValue);
145                 if (reply == mEvent.reply) return false;
146                 mEvent.reply = reply;
147                 updateRule(ZenModeConfig.toEventConditionId(mEvent));
148                 return true;
149             }
150         });
151 
152         reloadCalendar();
153         updateControlsInternal();
154     }
155 
156     @Override
updateControlsInternal()157     protected void updateControlsInternal() {
158         mCalendar.setValue(key(mEvent));
159         mReply.setValue(Integer.toString(mEvent.reply));
160     }
161 
162     @Override
getMetricsCategory()163     protected int getMetricsCategory() {
164         return MetricsEvent.NOTIFICATION_ZEN_MODE_EVENT_RULE;
165     }
166 
findCalendar(Context context, EventInfo event)167     public static CalendarInfo findCalendar(Context context, EventInfo event) {
168         if (context == null || event == null) return null;
169         final String eventKey = key(event);
170         for (CalendarInfo calendar : getCalendars(context)) {
171             if (eventKey.equals(key(calendar))) {
172                 return calendar;
173             }
174         }
175         return null;
176     }
177 
getCalendars(Context context)178     private static List<CalendarInfo> getCalendars(Context context) {
179         final List<CalendarInfo> calendars = new ArrayList<>();
180         for (UserHandle user : UserManager.get(context).getUserProfiles()) {
181             final Context userContext = getContextForUser(context, user);
182             if (userContext != null) {
183                 addCalendars(userContext, calendars);
184             }
185         }
186         Collections.sort(calendars, CALENDAR_NAME);
187         return calendars;
188     }
189 
getContextForUser(Context context, UserHandle user)190     private static Context getContextForUser(Context context, UserHandle user) {
191         try {
192             return context.createPackageContextAsUser(context.getPackageName(), 0, user);
193         } catch (NameNotFoundException e) {
194             return null;
195         }
196     }
197 
addCalendars(Context context, List<CalendarInfo> outCalendars)198     public static void addCalendars(Context context, List<CalendarInfo> outCalendars) {
199         final String primary = "\"primary\"";
200         final String[] projection = { Calendars._ID, Calendars.CALENDAR_DISPLAY_NAME,
201                 "(" + Calendars.ACCOUNT_NAME + "=" + Calendars.OWNER_ACCOUNT + ") AS " + primary };
202         final String selection = primary + " = 1";
203         Cursor cursor = null;
204         try {
205             cursor = context.getContentResolver().query(Calendars.CONTENT_URI, projection,
206                     selection, null, null);
207             if (cursor == null) {
208                 return;
209             }
210             while (cursor.moveToNext()) {
211                 final CalendarInfo ci = new CalendarInfo();
212                 ci.name = cursor.getString(1);
213                 ci.userId = context.getUserId();
214                 outCalendars.add(ci);
215             }
216         } finally {
217             if (cursor != null) {
218                 cursor.close();
219             }
220         }
221     }
222 
key(CalendarInfo calendar)223     private static String key(CalendarInfo calendar) {
224         return key(calendar.userId, calendar.name);
225     }
226 
key(EventInfo event)227     private static String key(EventInfo event) {
228         return key(event.userId, event.calendar);
229     }
230 
key(int userId, String calendar)231     private static String key(int userId, String calendar) {
232         return EventInfo.resolveUserId(userId) + ":" + (calendar == null ? "" : calendar);
233     }
234 
235     private static final Comparator<CalendarInfo> CALENDAR_NAME = new Comparator<CalendarInfo>() {
236         @Override
237         public int compare(CalendarInfo lhs, CalendarInfo rhs) {
238             return lhs.name.compareTo(rhs.name);
239         }
240     };
241 
242     public static class CalendarInfo {
243         public String name;
244         public int userId;
245     }
246 
247 }
248