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.AutomaticZenRule; 20 import android.app.settings.SettingsEnums; 21 import android.content.Context; 22 import android.content.pm.PackageManager.NameNotFoundException; 23 import android.database.Cursor; 24 import android.os.UserHandle; 25 import android.os.UserManager; 26 import android.provider.CalendarContract.Calendars; 27 import android.provider.Settings; 28 import android.service.notification.ZenModeConfig; 29 import android.service.notification.ZenModeConfig.EventInfo; 30 31 import androidx.preference.DropDownPreference; 32 import androidx.preference.Preference; 33 import androidx.preference.Preference.OnPreferenceChangeListener; 34 import androidx.preference.PreferenceScreen; 35 36 import com.android.internal.annotations.VisibleForTesting; 37 import com.android.settings.R; 38 import com.android.settingslib.core.AbstractPreferenceController; 39 40 import java.util.ArrayList; 41 import java.util.Arrays; 42 import java.util.Collections; 43 import java.util.Comparator; 44 import java.util.List; 45 import java.util.Objects; 46 47 public class ZenModeEventRuleSettings extends ZenModeRuleSettingsBase { 48 private static final String KEY_CALENDAR = "calendar"; 49 private static final String KEY_REPLY = "reply"; 50 51 public static final String ACTION = Settings.ACTION_ZEN_MODE_EVENT_RULE_SETTINGS; 52 53 private DropDownPreference mCalendar; 54 private DropDownPreference mReply; 55 56 private EventInfo mEvent; 57 58 private boolean mCreate; 59 60 @Override setRule(AutomaticZenRule rule)61 protected boolean setRule(AutomaticZenRule rule) { 62 mEvent = rule != null ? ZenModeConfig.tryParseEventConditionId(rule.getConditionId()) 63 : null; 64 return mEvent != null; 65 } 66 67 @Override onResume()68 public void onResume() { 69 super.onResume(); 70 if (isUiRestricted()) { 71 return; 72 } 73 if (!mCreate) { 74 reloadCalendar(); 75 } 76 mCreate = false; 77 } 78 79 @Override getPreferenceScreenResId()80 protected int getPreferenceScreenResId() { 81 return R.xml.zen_mode_event_rule_settings; 82 } 83 84 @Override createPreferenceControllers(Context context)85 protected List<AbstractPreferenceController> createPreferenceControllers(Context context) { 86 List<AbstractPreferenceController> controllers = new ArrayList<>(); 87 mHeader = new ZenAutomaticRuleHeaderPreferenceController(context, this, 88 getSettingsLifecycle()); 89 mActionButtons = new ZenRuleButtonsPreferenceController(context, this, 90 getSettingsLifecycle()); 91 mSwitch = new ZenAutomaticRuleSwitchPreferenceController(context, this, 92 getSettingsLifecycle()); 93 controllers.add(mHeader); 94 controllers.add(mActionButtons); 95 controllers.add(mSwitch); 96 return controllers; 97 } 98 reloadCalendar()99 private void reloadCalendar() { 100 List<CalendarInfo> calendars = getCalendars(mContext); 101 ArrayList<CharSequence> entries = new ArrayList<>(); 102 ArrayList<CharSequence> values = new ArrayList<>(); 103 entries.add(getString(R.string.zen_mode_event_rule_calendar_any)); 104 values.add(key(0, null, "")); 105 final String eventCalendar = mEvent != null ? mEvent.calName : null; 106 for (CalendarInfo calendar : calendars) { 107 entries.add(calendar.name); 108 values.add(key(calendar)); 109 if (eventCalendar != null && (mEvent.calendarId == null 110 && eventCalendar.equals(calendar.name))) { 111 mEvent.calendarId = calendar.calendarId; 112 } 113 } 114 115 CharSequence[] entriesArr = entries.toArray(new CharSequence[entries.size()]); 116 CharSequence[] valuesArr = values.toArray(new CharSequence[values.size()]); 117 if (!Arrays.equals(mCalendar.getEntries(), entriesArr)) { 118 mCalendar.setEntries(entriesArr); 119 } 120 121 if (!Arrays.equals(mCalendar.getEntryValues(), valuesArr)) { 122 mCalendar.setEntryValues(valuesArr); 123 } 124 } 125 126 @Override onCreateInternal()127 protected void onCreateInternal() { 128 mCreate = true; 129 final PreferenceScreen root = getPreferenceScreen(); 130 131 mCalendar = (DropDownPreference) root.findPreference(KEY_CALENDAR); 132 mCalendar.setOnPreferenceChangeListener(new OnPreferenceChangeListener() { 133 @Override 134 public boolean onPreferenceChange(Preference preference, Object newValue) { 135 final String calendarKey = (String) newValue; 136 if (calendarKey.equals(key(mEvent))) return false; 137 String[] key = calendarKey.split(":", 3); 138 mEvent.userId = Integer.parseInt(key[0]); 139 mEvent.calendarId = key[1].equals("") ? null : Long.parseLong(key[1]); 140 mEvent.calName = key[2].equals("") ? null : key[2]; 141 updateEventRule(mEvent); 142 return true; 143 } 144 }); 145 146 mReply = (DropDownPreference) root.findPreference(KEY_REPLY); 147 mReply.setEntries(new CharSequence[] { 148 getString(R.string.zen_mode_event_rule_reply_any_except_no), 149 getString(R.string.zen_mode_event_rule_reply_yes_or_maybe), 150 getString(R.string.zen_mode_event_rule_reply_yes), 151 }); 152 mReply.setEntryValues(new CharSequence[] { 153 Integer.toString(EventInfo.REPLY_ANY_EXCEPT_NO), 154 Integer.toString(EventInfo.REPLY_YES_OR_MAYBE), 155 Integer.toString(EventInfo.REPLY_YES), 156 }); 157 mReply.setOnPreferenceChangeListener(new OnPreferenceChangeListener() { 158 @Override 159 public boolean onPreferenceChange(Preference preference, Object newValue) { 160 final int reply = Integer.parseInt((String) newValue); 161 if (reply == mEvent.reply) return false; 162 mEvent.reply = reply; 163 updateEventRule(mEvent); 164 return true; 165 } 166 }); 167 168 reloadCalendar(); 169 updateControlsInternal(); 170 } 171 172 @Override updateControlsInternal()173 protected void updateControlsInternal() { 174 if (!Objects.equals(mCalendar.getValue(), key(mEvent))) { 175 mCalendar.setValue(key(mEvent)); 176 } 177 if (!Objects.equals(mReply.getValue(), Integer.toString(mEvent.reply))) { 178 mReply.setValue(Integer.toString(mEvent.reply)); 179 } 180 } 181 182 @Override getMetricsCategory()183 public int getMetricsCategory() { 184 return SettingsEnums.NOTIFICATION_ZEN_MODE_EVENT_RULE; 185 } 186 getCalendars(Context context)187 private List<CalendarInfo> getCalendars(Context context) { 188 final List<CalendarInfo> calendars = new ArrayList<>(); 189 for (UserHandle user : UserManager.get(context).getUserProfiles()) { 190 final Context userContext = getContextForUser(context, user); 191 if (userContext != null) { 192 addCalendars(userContext, calendars); 193 } 194 } 195 Collections.sort(calendars, CALENDAR_NAME); 196 return calendars; 197 } 198 getContextForUser(Context context, UserHandle user)199 private static Context getContextForUser(Context context, UserHandle user) { 200 try { 201 return context.createPackageContextAsUser(context.getPackageName(), 0, user); 202 } catch (NameNotFoundException e) { 203 return null; 204 } 205 } 206 addCalendars(Context context, List<CalendarInfo> outCalendars)207 private void addCalendars(Context context, List<CalendarInfo> outCalendars) { 208 final String[] projection = { Calendars._ID, Calendars.CALENDAR_DISPLAY_NAME }; 209 final String selection = Calendars.CALENDAR_ACCESS_LEVEL + " >= " 210 + Calendars.CAL_ACCESS_CONTRIBUTOR 211 + " AND " + Calendars.SYNC_EVENTS + " = 1"; 212 Cursor cursor = null; 213 try { 214 cursor = context.getContentResolver().query(Calendars.CONTENT_URI, projection, 215 selection, null, null); 216 if (cursor == null) { 217 return; 218 } 219 while (cursor.moveToNext()) { 220 addCalendar(cursor.getLong(0), cursor.getString(1), 221 context.getUserId(), outCalendars); 222 } 223 } finally { 224 if (cursor != null) { 225 cursor.close(); 226 } 227 } 228 } 229 230 @VisibleForTesting addCalendar(long calendarId, String calName, int userId, List<CalendarInfo> outCalendars)231 void addCalendar(long calendarId, String calName, int userId, List<CalendarInfo> 232 outCalendars) { 233 final CalendarInfo ci = new CalendarInfo(); 234 ci.calendarId = calendarId; 235 ci.name = calName; 236 ci.userId = userId; 237 if (!outCalendars.contains(ci)) { 238 outCalendars.add(ci); 239 } 240 } 241 key(CalendarInfo calendar)242 private static String key(CalendarInfo calendar) { 243 return key(calendar.userId, calendar.calendarId, calendar.name); 244 } 245 key(EventInfo event)246 private static String key(EventInfo event) { 247 return key(event.userId, event.calendarId, event.calName); 248 } 249 key(int userId, Long calendarId, String displayName)250 private static String key(int userId, Long calendarId, String displayName) { 251 return EventInfo.resolveUserId(userId) + ":" + (calendarId == null ? "" : calendarId) 252 + ":" + (displayName == null ? "" : displayName); 253 } 254 255 private static final Comparator<CalendarInfo> CALENDAR_NAME = new Comparator<CalendarInfo>() { 256 @Override 257 public int compare(CalendarInfo lhs, CalendarInfo rhs) { 258 return lhs.name.compareTo(rhs.name); 259 } 260 }; 261 262 public static class CalendarInfo { 263 public String name; 264 public int userId; 265 public Long calendarId; 266 267 @Override equals(Object o)268 public boolean equals(Object o) { 269 if (!(o instanceof CalendarInfo)) return false; 270 if (o == this) return true; 271 final CalendarInfo other = (CalendarInfo) o; 272 return Objects.equals(other.name, name) 273 && Objects.equals(other.calendarId, calendarId); 274 } 275 276 @Override hashCode()277 public int hashCode() { 278 return Objects.hash(name, calendarId); 279 } 280 } 281 } 282