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