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