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.Dialog; 21 import android.app.TimePickerDialog; 22 import android.app.settings.SettingsEnums; 23 import android.content.Context; 24 import android.content.DialogInterface; 25 import android.content.DialogInterface.OnDismissListener; 26 import android.os.Bundle; 27 import android.provider.Settings; 28 import android.service.notification.ZenModeConfig; 29 import android.service.notification.ZenModeConfig.ScheduleInfo; 30 import android.text.format.DateFormat; 31 import android.util.Log; 32 import android.widget.TimePicker; 33 34 import androidx.appcompat.app.AlertDialog; 35 import androidx.fragment.app.FragmentManager; 36 import androidx.preference.Preference; 37 import androidx.preference.Preference.OnPreferenceClickListener; 38 import androidx.preference.PreferenceScreen; 39 import androidx.preference.SwitchPreference; 40 41 import com.android.settings.R; 42 import com.android.settings.core.instrumentation.InstrumentedDialogFragment; 43 import com.android.settingslib.core.AbstractPreferenceController; 44 45 import java.text.SimpleDateFormat; 46 import java.util.ArrayList; 47 import java.util.Arrays; 48 import java.util.Calendar; 49 import java.util.List; 50 51 public class ZenModeScheduleRuleSettings extends ZenModeRuleSettingsBase { 52 private static final String KEY_DAYS = "days"; 53 private static final String KEY_START_TIME = "start_time"; 54 private static final String KEY_END_TIME = "end_time"; 55 private static final String KEY_EXIT_AT_ALARM = "exit_at_alarm"; 56 57 public static final String ACTION = Settings.ACTION_ZEN_MODE_SCHEDULE_RULE_SETTINGS; 58 59 // per-instance to ensure we're always using the current locale 60 private final SimpleDateFormat mDayFormat = new SimpleDateFormat("EEE"); 61 62 private Preference mDays; 63 private TimePickerPreference mStart; 64 private TimePickerPreference mEnd; 65 private SwitchPreference mExitAtAlarm; 66 private AlertDialog mDayDialog; 67 private ScheduleInfo mSchedule; 68 69 @Override setRule(AutomaticZenRule rule)70 protected boolean setRule(AutomaticZenRule rule) { 71 mSchedule = rule != null ? ZenModeConfig.tryParseScheduleConditionId(rule.getConditionId()) 72 : null; 73 return mSchedule != null; 74 } 75 76 @Override getPreferenceScreenResId()77 protected int getPreferenceScreenResId() { 78 return R.xml.zen_mode_schedule_rule_settings; 79 } 80 81 @Override onCreateInternal()82 protected void onCreateInternal() { 83 final PreferenceScreen root = getPreferenceScreen(); 84 85 mDays = root.findPreference(KEY_DAYS); 86 mDays.setOnPreferenceClickListener(new OnPreferenceClickListener() { 87 @Override 88 public boolean onPreferenceClick(Preference preference) { 89 showDaysDialog(); 90 return true; 91 } 92 }); 93 94 final FragmentManager mgr = getFragmentManager(); 95 96 mStart = new TimePickerPreference(getPrefContext(), mgr); 97 mStart.setKey(KEY_START_TIME); 98 mStart.setTitle(R.string.zen_mode_start_time); 99 mStart.setCallback(new TimePickerPreference.Callback() { 100 @Override 101 public boolean onSetTime(final int hour, final int minute) { 102 if (mDisableListeners) return true; 103 if (!ZenModeConfig.isValidHour(hour)) return false; 104 if (!ZenModeConfig.isValidMinute(minute)) return false; 105 if (hour == mSchedule.startHour && minute == mSchedule.startMinute) { 106 return true; 107 } 108 if (DEBUG) Log.d(TAG, "onPrefChange start h=" + hour + " m=" + minute); 109 mSchedule.startHour = hour; 110 mSchedule.startMinute = minute; 111 updateRule(ZenModeConfig.toScheduleConditionId(mSchedule)); 112 return true; 113 } 114 }); 115 root.addPreference(mStart); 116 mStart.setDependency(mDays.getKey()); 117 118 mEnd = new TimePickerPreference(getPrefContext(), mgr); 119 mEnd.setKey(KEY_END_TIME); 120 mEnd.setTitle(R.string.zen_mode_end_time); 121 mEnd.setCallback(new TimePickerPreference.Callback() { 122 @Override 123 public boolean onSetTime(final int hour, final int minute) { 124 if (mDisableListeners) return true; 125 if (!ZenModeConfig.isValidHour(hour)) return false; 126 if (!ZenModeConfig.isValidMinute(minute)) return false; 127 if (hour == mSchedule.endHour && minute == mSchedule.endMinute) { 128 return true; 129 } 130 if (DEBUG) Log.d(TAG, "onPrefChange end h=" + hour + " m=" + minute); 131 mSchedule.endHour = hour; 132 mSchedule.endMinute = minute; 133 updateRule(ZenModeConfig.toScheduleConditionId(mSchedule)); 134 return true; 135 } 136 }); 137 root.addPreference(mEnd); 138 mEnd.setDependency(mDays.getKey()); 139 140 mExitAtAlarm = (SwitchPreference) root.findPreference(KEY_EXIT_AT_ALARM); 141 mExitAtAlarm.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() { 142 @Override 143 public boolean onPreferenceChange(Preference preference, Object o) { 144 mSchedule.exitAtAlarm = (Boolean) o; 145 updateRule(ZenModeConfig.toScheduleConditionId(mSchedule)); 146 return true; 147 } 148 }); 149 } 150 updateDays()151 private void updateDays() { 152 // Compute an ordered, delimited list of day names based on the persisted user config. 153 final int[] days = mSchedule.days; 154 if (days != null && days.length > 0) { 155 final StringBuilder sb = new StringBuilder(); 156 final Calendar c = Calendar.getInstance(); 157 int[] daysOfWeek = ZenModeScheduleDaysSelection.getDaysOfWeekForLocale(c); 158 for (int i = 0; i < daysOfWeek.length; i++) { 159 final int day = daysOfWeek[i]; 160 for (int j = 0; j < days.length; j++) { 161 if (day == days[j]) { 162 c.set(Calendar.DAY_OF_WEEK, day); 163 if (sb.length() > 0) { 164 sb.append(mContext.getString(R.string.summary_divider_text)); 165 } 166 sb.append(mDayFormat.format(c.getTime())); 167 break; 168 } 169 } 170 } 171 if (sb.length() > 0) { 172 mDays.setSummary(sb); 173 mDays.notifyDependencyChange(false); 174 return; 175 } 176 } 177 mDays.setSummary(R.string.zen_mode_schedule_rule_days_none); 178 mDays.notifyDependencyChange(true); 179 } 180 updateEndSummary()181 private void updateEndSummary() { 182 final int startMin = 60 * mSchedule.startHour + mSchedule.startMinute; 183 final int endMin = 60 * mSchedule.endHour + mSchedule.endMinute; 184 final boolean nextDay = startMin >= endMin; 185 final int summaryFormat = nextDay ? R.string.zen_mode_end_time_next_day_summary_format : 0; 186 mEnd.setSummaryFormat(summaryFormat); 187 } 188 @Override updateControlsInternal()189 protected void updateControlsInternal() { 190 updateDays(); 191 mStart.setTime(mSchedule.startHour, mSchedule.startMinute); 192 mEnd.setTime(mSchedule.endHour, mSchedule.endMinute); 193 mExitAtAlarm.setChecked(mSchedule.exitAtAlarm); 194 updateEndSummary(); 195 } 196 197 @Override createPreferenceControllers(Context context)198 protected List<AbstractPreferenceController> createPreferenceControllers(Context context) { 199 List<AbstractPreferenceController> controllers = new ArrayList<>(); 200 mHeader = new ZenAutomaticRuleHeaderPreferenceController(context, this, 201 getSettingsLifecycle()); 202 mActionButtons = new ZenRuleButtonsPreferenceController(context, this, 203 getSettingsLifecycle()); 204 mSwitch = new ZenAutomaticRuleSwitchPreferenceController(context, this, 205 getSettingsLifecycle()); 206 controllers.add(mHeader); 207 controllers.add(mActionButtons); 208 controllers.add(mSwitch); 209 return controllers; 210 } 211 212 @Override getMetricsCategory()213 public int getMetricsCategory() { 214 return SettingsEnums.NOTIFICATION_ZEN_MODE_SCHEDULE_RULE; 215 } 216 217 @Override onDestroy()218 public void onDestroy() { 219 super.onDestroy(); 220 if (mDayDialog != null && mDayDialog.isShowing()) { 221 mDayDialog.dismiss(); 222 mDayDialog = null; 223 } 224 } 225 showDaysDialog()226 private void showDaysDialog() { 227 mDayDialog = new AlertDialog.Builder(mContext) 228 .setTitle(R.string.zen_mode_schedule_rule_days) 229 .setView(new ZenModeScheduleDaysSelection(mContext, mSchedule.days) { 230 @Override 231 protected void onChanged(final int[] days) { 232 if (mDisableListeners) return; 233 if (Arrays.equals(days, mSchedule.days)) return; 234 if (DEBUG) Log.d(TAG, "days.onChanged days=" + Arrays.asList(days)); 235 mSchedule.days = days; 236 updateRule(ZenModeConfig.toScheduleConditionId(mSchedule)); 237 } 238 }) 239 .setOnDismissListener(new OnDismissListener() { 240 @Override 241 public void onDismiss(DialogInterface dialog) { 242 updateDays(); 243 } 244 }) 245 .setPositiveButton(R.string.done_button, null) 246 .show(); 247 } 248 249 private static class TimePickerPreference extends Preference { 250 private final Context mContext; 251 252 private int mSummaryFormat; 253 private int mHourOfDay; 254 private int mMinute; 255 private Callback mCallback; 256 TimePickerPreference(Context context, final FragmentManager mgr)257 public TimePickerPreference(Context context, final FragmentManager mgr) { 258 super(context); 259 mContext = context; 260 setPersistent(false); 261 setOnPreferenceClickListener(new OnPreferenceClickListener() { 262 @Override 263 public boolean onPreferenceClick(Preference preference) { 264 final TimePickerFragment frag = new TimePickerFragment(); 265 frag.pref = TimePickerPreference.this; 266 frag.show(mgr, TimePickerPreference.class.getName()); 267 return true; 268 } 269 }); 270 } 271 setCallback(Callback callback)272 public void setCallback(Callback callback) { 273 mCallback = callback; 274 } 275 setSummaryFormat(int resId)276 public void setSummaryFormat(int resId) { 277 mSummaryFormat = resId; 278 updateSummary(); 279 } 280 setTime(int hourOfDay, int minute)281 public void setTime(int hourOfDay, int minute) { 282 if (mCallback != null && !mCallback.onSetTime(hourOfDay, minute)) return; 283 mHourOfDay = hourOfDay; 284 mMinute = minute; 285 updateSummary(); 286 } 287 updateSummary()288 private void updateSummary() { 289 final Calendar c = Calendar.getInstance(); 290 c.set(Calendar.HOUR_OF_DAY, mHourOfDay); 291 c.set(Calendar.MINUTE, mMinute); 292 String time = DateFormat.getTimeFormat(mContext).format(c.getTime()); 293 if (mSummaryFormat != 0) { 294 time = mContext.getResources().getString(mSummaryFormat, time); 295 } 296 setSummary(time); 297 } 298 299 public static class TimePickerFragment extends InstrumentedDialogFragment implements 300 TimePickerDialog.OnTimeSetListener { 301 public TimePickerPreference pref; 302 303 @Override getMetricsCategory()304 public int getMetricsCategory() { 305 return SettingsEnums.DIALOG_ZEN_TIMEPICKER; 306 } 307 308 @Override onCreateDialog(Bundle savedInstanceState)309 public Dialog onCreateDialog(Bundle savedInstanceState) { 310 final boolean usePref = pref != null && pref.mHourOfDay >= 0 && pref.mMinute >= 0; 311 final Calendar c = Calendar.getInstance(); 312 final int hour = usePref ? pref.mHourOfDay : c.get(Calendar.HOUR_OF_DAY); 313 final int minute = usePref ? pref.mMinute : c.get(Calendar.MINUTE); 314 return new TimePickerDialog(getActivity(), this, hour, minute, 315 DateFormat.is24HourFormat(getActivity())); 316 } 317 onTimeSet(TimePicker view, int hourOfDay, int minute)318 public void onTimeSet(TimePicker view, int hourOfDay, int minute) { 319 if (pref != null) { 320 pref.setTime(hourOfDay, minute); 321 } 322 } 323 } 324 325 public interface Callback { onSetTime(int hour, int minute)326 boolean onSetTime(int hour, int minute); 327 } 328 } 329 } 330