OSDN Git Service

Adding zen mode condition dialog
[android-x86/packages-apps-Settings.git] / src / com / android / settings / notification / ZenModeSettings.java
1 /*
2  * Copyright (C) 2014 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.ZenModeDowntimeDaysSelection.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.INotificationManager;
26 import android.app.TimePickerDialog;
27 import android.content.Context;
28 import android.content.DialogInterface;
29 import android.content.DialogInterface.OnDismissListener;
30 import android.content.pm.PackageManager;
31 import android.content.res.Resources;
32 import android.database.ContentObserver;
33 import android.net.Uri;
34 import android.os.Bundle;
35 import android.os.Handler;
36 import android.os.ServiceManager;
37 import android.preference.Preference;
38 import android.preference.Preference.OnPreferenceChangeListener;
39 import android.preference.Preference.OnPreferenceClickListener;
40 import android.preference.PreferenceCategory;
41 import android.preference.PreferenceScreen;
42 import android.preference.SwitchPreference;
43 import android.provider.Settings.Global;
44 import android.service.notification.Condition;
45 import android.service.notification.ZenModeConfig;
46 import android.text.format.DateFormat;
47 import android.util.Log;
48 import android.util.SparseArray;
49 import android.widget.ScrollView;
50 import android.widget.TimePicker;
51
52 import com.android.settings.R;
53 import com.android.settings.SettingsPreferenceFragment;
54 import com.android.settings.Utils;
55 import com.android.settings.notification.DropDownPreference.Callback;
56 import com.android.settings.search.BaseSearchIndexProvider;
57 import com.android.settings.search.Indexable;
58 import com.android.settings.search.SearchIndexableRaw;
59
60 import java.text.SimpleDateFormat;
61 import java.util.ArrayList;
62 import java.util.Calendar;
63 import java.util.List;
64 import java.util.Objects;
65
66 public class ZenModeSettings extends SettingsPreferenceFragment implements Indexable {
67     private static final String TAG = "ZenModeSettings";
68     private static final boolean DEBUG = true;
69
70     private static final String KEY_ZEN_MODE = "zen_mode";
71     private static final String KEY_IMPORTANT = "important";
72     private static final String KEY_CALLS = "phone_calls";
73     private static final String KEY_MESSAGES = "messages";
74     private static final String KEY_STARRED = "starred";
75     private static final String KEY_ALARM_INFO = "alarm_info";
76
77     private static final String KEY_DOWNTIME = "downtime";
78     private static final String KEY_DAYS = "days";
79     private static final String KEY_START_TIME = "start_time";
80     private static final String KEY_END_TIME = "end_time";
81
82     private static final String KEY_AUTOMATION = "automation";
83     private static final String KEY_ENTRY = "entry";
84     private static final String KEY_CONDITION_PROVIDERS = "manage_condition_providers";
85
86     private static final SettingPrefWithCallback PREF_ZEN_MODE = new SettingPrefWithCallback(
87             SettingPref.TYPE_GLOBAL, KEY_ZEN_MODE, Global.ZEN_MODE, Global.ZEN_MODE_OFF,
88             Global.ZEN_MODE_OFF, Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS,
89             Global.ZEN_MODE_NO_INTERRUPTIONS) {
90         protected String getCaption(Resources res, int value) {
91             switch (value) {
92                 case Global.ZEN_MODE_NO_INTERRUPTIONS:
93                     return res.getString(R.string.zen_mode_option_no_interruptions);
94                 case Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS:
95                     return res.getString(R.string.zen_mode_option_important_interruptions);
96                 default:
97                     return res.getString(R.string.zen_mode_option_off);
98             }
99         }
100     };
101
102     private static final SimpleDateFormat DAY_FORMAT = new SimpleDateFormat("EEE");
103
104     private static SparseArray<String> allKeyTitles(Context context) {
105         final SparseArray<String> rt = new SparseArray<String>();
106         rt.put(R.string.zen_mode_important_category, KEY_IMPORTANT);
107         if (Utils.isVoiceCapable(context)) {
108             rt.put(R.string.zen_mode_phone_calls, KEY_CALLS);
109             rt.put(R.string.zen_mode_option_title, KEY_ZEN_MODE);
110         } else {
111             rt.put(R.string.zen_mode_option_title_novoice, KEY_ZEN_MODE);
112         }
113         rt.put(R.string.zen_mode_messages, KEY_MESSAGES);
114         rt.put(R.string.zen_mode_from_starred, KEY_STARRED);
115         rt.put(R.string.zen_mode_alarm_info, KEY_ALARM_INFO);
116         rt.put(R.string.zen_mode_downtime_category, KEY_DOWNTIME);
117         rt.put(R.string.zen_mode_downtime_days, KEY_DAYS);
118         rt.put(R.string.zen_mode_start_time, KEY_START_TIME);
119         rt.put(R.string.zen_mode_end_time, KEY_END_TIME);
120         rt.put(R.string.zen_mode_automation_category, KEY_AUTOMATION);
121         rt.put(R.string.manage_condition_providers, KEY_CONDITION_PROVIDERS);
122         return rt;
123     }
124
125     private final Handler mHandler = new Handler();
126     private final SettingsObserver mSettingsObserver = new SettingsObserver();
127
128     private Context mContext;
129     private PackageManager mPM;
130     private ZenModeConfig mConfig;
131     private boolean mDisableListeners;
132     private SwitchPreference mCalls;
133     private SwitchPreference mMessages;
134     private DropDownPreference mStarred;
135     private Preference mDays;
136     private TimePickerPreference mStart;
137     private TimePickerPreference mEnd;
138     private PreferenceCategory mAutomationCategory;
139     private Preference mEntry;
140     private Preference mConditionProviders;
141     private AlertDialog mDialog;
142
143     @Override
144     public void onCreate(Bundle savedInstanceState) {
145         super.onCreate(savedInstanceState);
146         mContext = getActivity();
147         mPM = mContext.getPackageManager();
148
149         addPreferencesFromResource(R.xml.zen_mode_settings);
150         final PreferenceScreen root = getPreferenceScreen();
151
152         mConfig = getZenModeConfig();
153         if (DEBUG) Log.d(TAG, "Loaded mConfig=" + mConfig);
154
155         final Preference zenMode = PREF_ZEN_MODE.init(this);
156         PREF_ZEN_MODE.setCallback(new SettingPrefWithCallback.Callback() {
157             @Override
158             public void onSettingSelected(int value) {
159                 if (value != Global.ZEN_MODE_OFF) {
160                     showConditionSelection(value);
161                 }
162             }
163         });
164         if (!Utils.isVoiceCapable(mContext)) {
165             zenMode.setTitle(R.string.zen_mode_option_title_novoice);
166         }
167
168         final PreferenceCategory important =
169                 (PreferenceCategory) root.findPreference(KEY_IMPORTANT);
170
171         mCalls = (SwitchPreference) important.findPreference(KEY_CALLS);
172         if (Utils.isVoiceCapable(mContext)) {
173             mCalls.setOnPreferenceChangeListener(new OnPreferenceChangeListener() {
174                 @Override
175                 public boolean onPreferenceChange(Preference preference, Object newValue) {
176                     if (mDisableListeners) return true;
177                     final boolean val = (Boolean) newValue;
178                     if (val == mConfig.allowCalls) return true;
179                     if (DEBUG) Log.d(TAG, "onPrefChange allowCalls=" + val);
180                     final ZenModeConfig newConfig = mConfig.copy();
181                     newConfig.allowCalls = val;
182                     return setZenModeConfig(newConfig);
183                 }
184             });
185         } else {
186             important.removePreference(mCalls);
187             mCalls = null;
188         }
189
190         mMessages = (SwitchPreference) important.findPreference(KEY_MESSAGES);
191         mMessages.setOnPreferenceChangeListener(new OnPreferenceChangeListener() {
192             @Override
193             public boolean onPreferenceChange(Preference preference, Object newValue) {
194                 if (mDisableListeners) return true;
195                 final boolean val = (Boolean) newValue;
196                 if (val == mConfig.allowMessages) return true;
197                 if (DEBUG) Log.d(TAG, "onPrefChange allowMessages=" + val);
198                 final ZenModeConfig newConfig = mConfig.copy();
199                 newConfig.allowMessages = val;
200                 return setZenModeConfig(newConfig);
201             }
202         });
203
204         mStarred = (DropDownPreference) important.findPreference(KEY_STARRED);
205         mStarred.setDropDownWidth(R.dimen.zen_mode_dropdown_width);
206         mStarred.addItem(R.string.zen_mode_from_anyone, ZenModeConfig.SOURCE_ANYONE);
207         mStarred.addItem(R.string.zen_mode_from_starred, ZenModeConfig.SOURCE_STAR);
208         mStarred.addItem(R.string.zen_mode_from_contacts, ZenModeConfig.SOURCE_CONTACT);
209         mStarred.setCallback(new DropDownPreference.Callback() {
210             @Override
211             public boolean onItemSelected(int pos, Object newValue) {
212                 if (mDisableListeners) return true;
213                 final int val = (Integer) newValue;
214                 if (val == mConfig.allowFrom) return true;
215                 if (DEBUG) Log.d(TAG, "onPrefChange allowFrom=" +
216                         ZenModeConfig.sourceToString(val));
217                 final ZenModeConfig newConfig = mConfig.copy();
218                 newConfig.allowFrom = val;
219                 return setZenModeConfig(newConfig);
220             }
221         });
222         important.addPreference(mStarred);
223
224         final PreferenceCategory downtime = (PreferenceCategory) root.findPreference(KEY_DOWNTIME);
225
226         mDays = downtime.findPreference(KEY_DAYS);
227         mDays.setOnPreferenceClickListener(new OnPreferenceClickListener() {
228             @Override
229             public boolean onPreferenceClick(Preference preference) {
230                 new AlertDialog.Builder(mContext)
231                         .setTitle(R.string.zen_mode_downtime_days)
232                         .setView(new ZenModeDowntimeDaysSelection(mContext, mConfig.sleepMode) {
233                               @Override
234                               protected void onChanged(String mode) {
235                                   if (mDisableListeners) return;
236                                   if (Objects.equals(mode, mConfig.sleepMode)) return;
237                                   if (DEBUG) Log.d(TAG, "days.onChanged sleepMode=" + mode);
238                                   final ZenModeConfig newConfig = mConfig.copy();
239                                   newConfig.sleepMode = mode;
240                                   setZenModeConfig(newConfig);
241                               }
242                         })
243                         .setOnDismissListener(new OnDismissListener() {
244                             @Override
245                             public void onDismiss(DialogInterface dialog) {
246                                 updateDays();
247                             }
248                         })
249                         .setPositiveButton(R.string.done_button, null)
250                         .show();
251                 return true;
252             }
253         });
254
255         final FragmentManager mgr = getFragmentManager();
256
257         mStart = new TimePickerPreference(mContext, mgr);
258         mStart.setKey(KEY_START_TIME);
259         mStart.setTitle(R.string.zen_mode_start_time);
260         mStart.setCallback(new TimePickerPreference.Callback() {
261             @Override
262             public boolean onSetTime(int hour, int minute) {
263                 if (mDisableListeners) return true;
264                 if (!ZenModeConfig.isValidHour(hour)) return false;
265                 if (!ZenModeConfig.isValidMinute(minute)) return false;
266                 if (hour == mConfig.sleepStartHour && minute == mConfig.sleepStartMinute) {
267                     return true;
268                 }
269                 if (DEBUG) Log.d(TAG, "onPrefChange sleepStart h=" + hour + " m=" + minute);
270                 final ZenModeConfig newConfig = mConfig.copy();
271                 newConfig.sleepStartHour = hour;
272                 newConfig.sleepStartMinute = minute;
273                 return setZenModeConfig(newConfig);
274             }
275         });
276         downtime.addPreference(mStart);
277         mStart.setDependency(mDays.getKey());
278
279         mEnd = new TimePickerPreference(mContext, mgr);
280         mEnd.setKey(KEY_END_TIME);
281         mEnd.setTitle(R.string.zen_mode_end_time);
282         mEnd.setCallback(new TimePickerPreference.Callback() {
283             @Override
284             public boolean onSetTime(int hour, int minute) {
285                 if (mDisableListeners) return true;
286                 if (!ZenModeConfig.isValidHour(hour)) return false;
287                 if (!ZenModeConfig.isValidMinute(minute)) return false;
288                 if (hour == mConfig.sleepEndHour && minute == mConfig.sleepEndMinute) {
289                     return true;
290                 }
291                 if (DEBUG) Log.d(TAG, "onPrefChange sleepEnd h=" + hour + " m=" + minute);
292                 final ZenModeConfig newConfig = mConfig.copy();
293                 newConfig.sleepEndHour = hour;
294                 newConfig.sleepEndMinute = minute;
295                 return setZenModeConfig(newConfig);
296             }
297         });
298         downtime.addPreference(mEnd);
299         mEnd.setDependency(mDays.getKey());
300
301         mAutomationCategory = (PreferenceCategory) findPreference(KEY_AUTOMATION);
302         mEntry = findPreference(KEY_ENTRY);
303         mEntry.setOnPreferenceClickListener(new OnPreferenceClickListener() {
304             @Override
305             public boolean onPreferenceClick(Preference preference) {
306                 new AlertDialog.Builder(mContext)
307                     .setTitle(R.string.zen_mode_entry_conditions_title)
308                     .setView(new ZenModeAutomaticConditionSelection(mContext))
309                     .setOnDismissListener(new OnDismissListener() {
310                         @Override
311                         public void onDismiss(DialogInterface dialog) {
312                             refreshAutomationSection();
313                         }
314                     })
315                     .setPositiveButton(R.string.dlg_ok, null)
316                     .show();
317                 return true;
318             }
319         });
320         mConditionProviders = findPreference(KEY_CONDITION_PROVIDERS);
321
322         updateControls();
323     }
324
325     private void updateDays() {
326         if (mConfig != null) {
327             final int[] days = ZenModeConfig.tryParseDays(mConfig.sleepMode);
328             if (days != null && days.length != 0) {
329                 final StringBuilder sb = new StringBuilder();
330                 final Calendar c = Calendar.getInstance();
331                 for (int i = 0; i < DAYS.length; i++) {
332                     final int day = DAYS[i];
333                     for (int j = 0; j < days.length; j++) {
334                         if (day == days[j]) {
335                             c.set(Calendar.DAY_OF_WEEK, day);
336                             if (sb.length() > 0) {
337                                 sb.append(mContext.getString(R.string.summary_divider_text));
338                             }
339                             sb.append(DAY_FORMAT.format(c.getTime()));
340                             break;
341                         }
342                     }
343                 }
344                 if (sb.length() > 0) {
345                     mDays.setSummary(sb);
346                     mDays.notifyDependencyChange(false);
347                     return;
348                 }
349             }
350         }
351         mDays.setSummary(R.string.zen_mode_downtime_days_none);
352         mDays.notifyDependencyChange(true);
353     }
354
355     private void updateEndSummary() {
356         final int startMin = 60 * mConfig.sleepStartHour + mConfig.sleepStartMinute;
357         final int endMin = 60 * mConfig.sleepEndHour + mConfig.sleepEndMinute;
358         final boolean nextDay = startMin >= endMin;
359         mEnd.setSummaryFormat(nextDay ? R.string.zen_mode_end_time_summary_format : 0);
360     }
361
362     private void updateControls() {
363         mDisableListeners = true;
364         if (mCalls != null) {
365             mCalls.setChecked(mConfig.allowCalls);
366         }
367         mMessages.setChecked(mConfig.allowMessages);
368         mStarred.setSelectedValue(mConfig.allowFrom);
369         updateStarredEnabled();
370         updateDays();
371         mStart.setTime(mConfig.sleepStartHour, mConfig.sleepStartMinute);
372         mEnd.setTime(mConfig.sleepEndHour, mConfig.sleepEndMinute);
373         mDisableListeners = false;
374         refreshAutomationSection();
375         updateEndSummary();
376     }
377
378     private void updateStarredEnabled() {
379         mStarred.setEnabled(mConfig.allowCalls || mConfig.allowMessages);
380     }
381
382     private void refreshAutomationSection() {
383         if (mConditionProviders != null) {
384             final int total = ConditionProviderSettings.getProviderCount(mPM);
385             if (total == 0) {
386                 getPreferenceScreen().removePreference(mAutomationCategory);
387             } else {
388                 final int n = ConditionProviderSettings.getEnabledProviderCount(mContext);
389                 if (n == 0) {
390                     mConditionProviders.setSummary(getResources().getString(
391                             R.string.manage_condition_providers_summary_zero));
392                 } else {
393                     mConditionProviders.setSummary(String.format(getResources().getQuantityString(
394                             R.plurals.manage_condition_providers_summary_nonzero,
395                             n, n)));
396                 }
397                 final String entrySummary = getEntryConditionSummary();
398                 if (n == 0 || entrySummary == null) {
399                     mEntry.setSummary(R.string.zen_mode_entry_conditions_summary_none);
400                 } else {
401                     mEntry.setSummary(entrySummary);
402                 }
403             }
404         }
405     }
406
407     private String getEntryConditionSummary() {
408         final INotificationManager nm = INotificationManager.Stub.asInterface(
409                 ServiceManager.getService(Context.NOTIFICATION_SERVICE));
410         try {
411             final Condition[] automatic = nm.getAutomaticZenModeConditions();
412             if (automatic == null || automatic.length == 0) {
413                 return null;
414             }
415             final String divider = getString(R.string.summary_divider_text);
416             final StringBuilder sb = new StringBuilder();
417             for (int i = 0; i < automatic.length; i++) {
418                 if (i > 0) sb.append(divider);
419                 sb.append(automatic[i].summary);
420             }
421             return sb.toString();
422         } catch (Exception e) {
423             Log.w(TAG, "Error calling getAutomaticZenModeConditions", e);
424             return null;
425         }
426     }
427
428     @Override
429     public void onResume() {
430         super.onResume();
431         updateControls();
432         mSettingsObserver.register();
433     }
434
435     @Override
436     public void onPause() {
437         super.onPause();
438         mSettingsObserver.unregister();
439     }
440
441     private void updateZenModeConfig() {
442         final ZenModeConfig config = getZenModeConfig();
443         if (Objects.equals(config, mConfig)) return;
444         mConfig = config;
445         if (DEBUG) Log.d(TAG, "updateZenModeConfig mConfig=" + mConfig);
446         updateControls();
447     }
448
449     private ZenModeConfig getZenModeConfig() {
450         final INotificationManager nm = INotificationManager.Stub.asInterface(
451                 ServiceManager.getService(Context.NOTIFICATION_SERVICE));
452         try {
453             return nm.getZenModeConfig();
454         } catch (Exception e) {
455            Log.w(TAG, "Error calling NoMan", e);
456            return new ZenModeConfig();
457         }
458     }
459
460     private boolean setZenModeConfig(ZenModeConfig config) {
461         final INotificationManager nm = INotificationManager.Stub.asInterface(
462                 ServiceManager.getService(Context.NOTIFICATION_SERVICE));
463         try {
464             final boolean success = nm.setZenModeConfig(config);
465             if (success) {
466                 mConfig = config;
467                 if (DEBUG) Log.d(TAG, "Saved mConfig=" + mConfig);
468                 updateEndSummary();
469                 updateStarredEnabled();
470             }
471             return success;
472         } catch (Exception e) {
473            Log.w(TAG, "Error calling NoMan", e);
474            return false;
475         }
476     }
477
478     protected void putZenModeSetting(int value) {
479         Global.putInt(getContentResolver(), Global.ZEN_MODE, value);
480     }
481
482     protected void showConditionSelection(final int newSettingsValue) {
483         if (mDialog != null) return;
484
485         final ZenModeConditionSelection zenModeConditionSelection =
486                 new ZenModeConditionSelection(mContext);
487         DialogInterface.OnClickListener positiveListener = new DialogInterface.OnClickListener() {
488             @Override
489             public void onClick(DialogInterface dialog, int which) {
490                 zenModeConditionSelection.confirmCondition();
491                 mDialog = null;
492             }
493         };
494         final int oldSettingsValue = PREF_ZEN_MODE.getValue(mContext);
495         ScrollView scrollView = new ScrollView(mContext);
496         scrollView.addView(zenModeConditionSelection);
497         mDialog = new AlertDialog.Builder(getActivity())
498                 .setTitle(PREF_ZEN_MODE.getCaption(getResources(), newSettingsValue))
499                 .setView(scrollView)
500                 .setPositiveButton(R.string.okay, positiveListener)
501                 .setNegativeButton(R.string.cancel_all_caps, new DialogInterface.OnClickListener() {
502                     @Override
503                     public void onClick(DialogInterface dialog, int which) {
504                         cancelDialog(oldSettingsValue);
505                     }
506                 })
507                 .setOnCancelListener(new DialogInterface.OnCancelListener() {
508                     @Override
509                     public void onCancel(DialogInterface dialog) {
510                         cancelDialog(oldSettingsValue);
511                     }
512                 }).create();
513         mDialog.show();
514     }
515
516     protected void cancelDialog(int oldSettingsValue) {
517         // If not making a decision, reset drop down to current setting.
518         PREF_ZEN_MODE.setValueWithoutCallback(mContext, oldSettingsValue);
519         mDialog = null;
520     }
521
522     // Enable indexing of searchable data
523     public static final Indexable.SearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
524         new BaseSearchIndexProvider() {
525             @Override
526             public List<SearchIndexableRaw> getRawDataToIndex(Context context, boolean enabled) {
527                 final SparseArray<String> keyTitles = allKeyTitles(context);
528                 final int N = keyTitles.size();
529                 final List<SearchIndexableRaw> result = new ArrayList<SearchIndexableRaw>(N);
530                 final Resources res = context.getResources();
531                 for (int i = 0; i < N; i++) {
532                     final SearchIndexableRaw data = new SearchIndexableRaw(context);
533                     data.key = keyTitles.valueAt(i);
534                     data.title = res.getString(keyTitles.keyAt(i));
535                     data.screenTitle = res.getString(R.string.zen_mode_settings_title);
536                     result.add(data);
537                 }
538                 return result;
539             }
540
541             public List<String> getNonIndexableKeys(Context context) {
542                 final ArrayList<String> rt = new ArrayList<String>();
543                 if (!Utils.isVoiceCapable(context)) {
544                     rt.add(KEY_CALLS);
545                 }
546                 return rt;
547             }
548         };
549
550     private static class SettingPrefWithCallback extends SettingPref {
551
552         private Callback mCallback;
553         private int mValue;
554
555         public SettingPrefWithCallback(int type, String key, String setting, int def,
556                 int... values) {
557             super(type, key, setting, def, values);
558         }
559
560         public void setCallback(Callback callback) {
561             mCallback = callback;
562         }
563
564         @Override
565         public void update(Context context) {
566             // Avoid callbacks from non-user changes.
567             mValue = getValue(context);
568             super.update(context);
569         }
570
571         @Override
572         protected boolean setSetting(Context context, int value) {
573             if (value == mValue) return true;
574             mValue = value;
575             if (mCallback != null) {
576                 mCallback.onSettingSelected(value);
577             }
578             return super.setSetting(context, value);
579         }
580
581         @Override
582         public Preference init(SettingsPreferenceFragment settings) {
583             Preference ret = super.init(settings);
584             mValue = getValue(settings.getActivity());
585
586             return ret;
587         }
588
589         public boolean setValueWithoutCallback(Context context, int value) {
590             // Set the current value ahead of time, this way we won't trigger a callback.
591             mValue = value;
592             return putInt(mType, context.getContentResolver(), mSetting, value);
593         }
594
595         public int getValue(Context context) {
596             return getInt(mType, context.getContentResolver(), mSetting, mDefault);
597         }
598
599         public interface Callback {
600             void onSettingSelected(int value);
601         }
602     }
603
604     private final class SettingsObserver extends ContentObserver {
605         private final Uri ZEN_MODE_URI = Global.getUriFor(Global.ZEN_MODE);
606         private final Uri ZEN_MODE_CONFIG_ETAG_URI = Global.getUriFor(Global.ZEN_MODE_CONFIG_ETAG);
607
608         public SettingsObserver() {
609             super(mHandler);
610         }
611
612         public void register() {
613             getContentResolver().registerContentObserver(ZEN_MODE_URI, false, this);
614             getContentResolver().registerContentObserver(ZEN_MODE_CONFIG_ETAG_URI, false, this);
615         }
616
617         public void unregister() {
618             getContentResolver().unregisterContentObserver(this);
619         }
620
621         @Override
622         public void onChange(boolean selfChange, Uri uri) {
623             super.onChange(selfChange, uri);
624             if (ZEN_MODE_URI.equals(uri)) {
625                 PREF_ZEN_MODE.update(mContext);
626             }
627             if (ZEN_MODE_CONFIG_ETAG_URI.equals(uri)) {
628                 updateZenModeConfig();
629             }
630         }
631     }
632
633     private static class TimePickerPreference extends Preference {
634         private final Context mContext;
635
636         private int mSummaryFormat;
637         private int mHourOfDay;
638         private int mMinute;
639         private Callback mCallback;
640
641         public TimePickerPreference(Context context, final FragmentManager mgr) {
642             super(context);
643             mContext = context;
644             setPersistent(false);
645             setOnPreferenceClickListener(new OnPreferenceClickListener(){
646                 @Override
647                 public boolean onPreferenceClick(Preference preference) {
648                     final TimePickerFragment frag = new TimePickerFragment();
649                     frag.pref = TimePickerPreference.this;
650                     frag.show(mgr, TimePickerPreference.class.getName());
651                     return true;
652                 }
653             });
654         }
655
656         public void setCallback(Callback callback) {
657             mCallback = callback;
658         }
659
660         public void setSummaryFormat(int resId) {
661             mSummaryFormat = resId;
662             updateSummary();
663         }
664
665         public void setTime(int hourOfDay, int minute) {
666             if (mCallback != null && !mCallback.onSetTime(hourOfDay, minute)) return;
667             mHourOfDay = hourOfDay;
668             mMinute = minute;
669             updateSummary();
670         }
671
672         private void updateSummary() {
673             final Calendar c = Calendar.getInstance();
674             c.set(Calendar.HOUR_OF_DAY, mHourOfDay);
675             c.set(Calendar.MINUTE, mMinute);
676             String time = DateFormat.getTimeFormat(mContext).format(c.getTime());
677             if (mSummaryFormat != 0) {
678                 time = mContext.getResources().getString(mSummaryFormat, time);
679             }
680             setSummary(time);
681         }
682
683         public static class TimePickerFragment extends DialogFragment implements
684                 TimePickerDialog.OnTimeSetListener {
685             public TimePickerPreference pref;
686
687             @Override
688             public Dialog onCreateDialog(Bundle savedInstanceState) {
689                 final boolean usePref = pref != null && pref.mHourOfDay >= 0 && pref.mMinute >= 0;
690                 final Calendar c = Calendar.getInstance();
691                 final int hour = usePref ? pref.mHourOfDay : c.get(Calendar.HOUR_OF_DAY);
692                 final int minute = usePref ? pref.mMinute : c.get(Calendar.MINUTE);
693                 return new TimePickerDialog(getActivity(), this, hour, minute,
694                         DateFormat.is24HourFormat(getActivity()));
695             }
696
697             public void onTimeSet(TimePicker view, int hourOfDay, int minute) {
698                 if (pref != null) {
699                     pref.setTime(hourOfDay, minute);
700                 }
701             }
702         }
703
704         public interface Callback {
705             boolean onSetTime(int hour, int minute);
706         }
707     }
708 }