ZenModeSettings.java revision 6d26d10fb3e5e2e491d1da0881eda192d30eec04
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 17package com.android.settings.notification; 18 19import android.app.ActionBar; 20import android.app.Activity; 21import android.app.Dialog; 22import android.app.DialogFragment; 23import android.app.FragmentManager; 24import android.app.INotificationManager; 25import android.app.TimePickerDialog; 26import android.content.Context; 27import android.content.res.Resources; 28import android.database.ContentObserver; 29import android.graphics.Typeface; 30import android.net.Uri; 31import android.os.AsyncTask; 32import android.os.Bundle; 33import android.os.Handler; 34import android.os.ServiceManager; 35import android.preference.Preference; 36import android.preference.Preference.OnPreferenceChangeListener; 37import android.preference.PreferenceCategory; 38import android.preference.PreferenceScreen; 39import android.preference.SwitchPreference; 40import android.provider.Settings.Global; 41import android.service.notification.ZenModeConfig; 42import android.text.format.DateFormat; 43import android.util.Log; 44import android.view.Gravity; 45import android.view.Menu; 46import android.view.MenuInflater; 47import android.view.MenuItem; 48import android.view.View; 49import android.view.ViewGroup; 50import android.widget.CompoundButton; 51import android.widget.CompoundButton.OnCheckedChangeListener; 52import android.widget.Switch; 53import android.widget.TextView; 54import android.widget.TimePicker; 55 56import com.android.settings.R; 57import com.android.settings.SettingsPreferenceFragment; 58import com.android.settings.search.BaseSearchIndexProvider; 59import com.android.settings.search.Indexable; 60import com.android.settings.search.SearchIndexableRaw; 61 62import java.util.ArrayList; 63import java.util.Calendar; 64import java.util.List; 65import java.util.Objects; 66 67public class ZenModeSettings extends SettingsPreferenceFragment implements Indexable { 68 private static final String TAG = "ZenModeSettings"; 69 private static final boolean DEBUG = true; 70 71 private static final String KEY_GENERAL = "general"; 72 private static final String KEY_CALLS = "phone_calls"; 73 private static final String KEY_MESSAGES = "messages"; 74 75 private static final String KEY_AUTOMATIC = "automatic"; 76 private static final String KEY_WHEN = "when"; 77 78 private final Handler mHandler = new Handler(); 79 private final SettingsObserver mSettingsObserver = new SettingsObserver(); 80 81 private Switch mSwitch; 82 private Activity mActivity; 83 private Context mContext; 84 private MenuItem mSearch; 85 private ZenModeConfig mConfig; 86 private boolean mDisableListeners; 87 private SwitchPreference mCalls; 88 private SwitchPreference mMessages; 89 private DropDownPreference mStarred; 90 private DropDownPreference mWhen; 91 private TimePickerPreference mStart; 92 private TimePickerPreference mEnd; 93 94 @Override 95 public void onCreate(Bundle icicle) { 96 super.onCreate(icicle); 97 mContext = mActivity = getActivity(); 98 mSwitch = new Switch(mActivity.getActionBar().getThemedContext()); 99 final Resources res = mContext.getResources(); 100 final int p = res.getDimensionPixelSize(R.dimen.content_margin_left); 101 mSwitch.setPadding(0, 0, p, 0); 102 setHasOptionsMenu(true); 103 104 addPreferencesFromResource(R.xml.zen_mode_settings); 105 final PreferenceScreen root = getPreferenceScreen(); 106 107 mConfig = getZenModeConfig(); 108 if (DEBUG) Log.d(TAG, "Loaded mConfig=" + mConfig); 109 110 final PreferenceCategory general = (PreferenceCategory) root.findPreference(KEY_GENERAL); 111 112 mCalls = (SwitchPreference) root.findPreference(KEY_CALLS); 113 mCalls.setOnPreferenceChangeListener(new OnPreferenceChangeListener() { 114 @Override 115 public boolean onPreferenceChange(Preference preference, Object newValue) { 116 if (mDisableListeners) return true; 117 final boolean val = (Boolean) newValue; 118 if (val == mConfig.allowCalls) return true; 119 if (DEBUG) Log.d(TAG, "onPrefChange allowCalls=" + val); 120 final ZenModeConfig newConfig = mConfig.copy(); 121 newConfig.allowCalls = val; 122 return setZenModeConfig(newConfig); 123 } 124 }); 125 126 mMessages = (SwitchPreference) root.findPreference(KEY_MESSAGES); 127 mMessages.setOnPreferenceChangeListener(new OnPreferenceChangeListener() { 128 @Override 129 public boolean onPreferenceChange(Preference preference, Object newValue) { 130 if (mDisableListeners) return true; 131 final boolean val = (Boolean) newValue; 132 if (val == mConfig.allowMessages) return true; 133 if (DEBUG) Log.d(TAG, "onPrefChange allowMessages=" + val); 134 final ZenModeConfig newConfig = mConfig.copy(); 135 newConfig.allowMessages = val; 136 return setZenModeConfig(newConfig); 137 } 138 }); 139 140 mStarred = new DropDownPreference(mContext); 141 mStarred.setEnabled(false); 142 mStarred.setTitle(R.string.zen_mode_from); 143 mStarred.setDropDownWidth(R.dimen.zen_mode_dropdown_width); 144 mStarred.addItem(R.string.zen_mode_from_anyone); 145 mStarred.addItem(R.string.zen_mode_from_starred); 146 mStarred.addItem(R.string.zen_mode_from_contacts); 147 general.addPreference(mStarred); 148 149 final Preference alarmInfo = new Preference(mContext) { 150 @Override 151 public View getView(View convertView, ViewGroup parent) { 152 final TextView tv = new TextView(mContext); 153 tv.setTypeface(Typeface.create(Typeface.DEFAULT, Typeface.ITALIC)); 154 tv.setPadding(p, p, p, p); 155 tv.setText(R.string.zen_mode_alarm_info); 156 return tv; 157 } 158 }; 159 alarmInfo.setPersistent(false); 160 alarmInfo.setSelectable(false); 161 general.addPreference(alarmInfo); 162 163 final PreferenceCategory auto = (PreferenceCategory) root.findPreference(KEY_AUTOMATIC); 164 165 mWhen = new DropDownPreference(mContext); 166 mWhen.setKey(KEY_WHEN); 167 mWhen.setTitle(R.string.zen_mode_when); 168 mWhen.setDropDownWidth(R.dimen.zen_mode_dropdown_width); 169 mWhen.addItem(R.string.zen_mode_when_never); 170 mWhen.addItem(R.string.zen_mode_when_every_night); 171 mWhen.addItem(R.string.zen_mode_when_weeknights); 172 mWhen.setCallback(new DropDownPreference.Callback() { 173 @Override 174 public boolean onItemSelected(int pos) { 175 if (mDisableListeners) return true; 176 final String mode = pos == 1 ? ZenModeConfig.SLEEP_MODE_NIGHTS : 177 pos == 2 ? ZenModeConfig.SLEEP_MODE_WEEKNIGHTS : null; 178 if (Objects.equals(mode, mConfig.sleepMode)) return true; 179 if (DEBUG) Log.d(TAG, "onPrefChange sleepMode=" + mode); 180 final ZenModeConfig newConfig = mConfig.copy(); 181 newConfig.sleepMode = mode; 182 return setZenModeConfig(newConfig); 183 } 184 }); 185 auto.addPreference(mWhen); 186 187 final FragmentManager mgr = getFragmentManager(); 188 189 mStart = new TimePickerPreference(mContext, mgr); 190 mStart.setTitle(R.string.zen_mode_start_time); 191 mStart.setCallback(new TimePickerPreference.Callback() { 192 @Override 193 public boolean onSetTime(int hour, int minute) { 194 if (mDisableListeners) return true; 195 if (!ZenModeConfig.isValidHour(hour)) return false; 196 if (!ZenModeConfig.isValidMinute(minute)) return false; 197 if (hour == mConfig.sleepStartHour && minute == mConfig.sleepStartMinute) { 198 return true; 199 } 200 if (DEBUG) Log.d(TAG, "onPrefChange sleepStart h=" + hour + " m=" + minute); 201 final ZenModeConfig newConfig = mConfig.copy(); 202 newConfig.sleepStartHour = hour; 203 newConfig.sleepStartMinute = minute; 204 return setZenModeConfig(newConfig); 205 } 206 }); 207 auto.addPreference(mStart); 208 209 mEnd = new TimePickerPreference(mContext, mgr); 210 mEnd.setTitle(R.string.zen_mode_end_time); 211 mEnd.setSummaryFormat(R.string.zen_mode_end_time_summary_format); 212 mEnd.setCallback(new TimePickerPreference.Callback() { 213 @Override 214 public boolean onSetTime(int hour, int minute) { 215 if (mDisableListeners) return true; 216 if (!ZenModeConfig.isValidHour(hour)) return false; 217 if (!ZenModeConfig.isValidMinute(minute)) return false; 218 if (hour == mConfig.sleepEndHour && minute == mConfig.sleepEndMinute) { 219 return true; 220 } 221 if (DEBUG) Log.d(TAG, "onPrefChange sleepEnd h=" + hour + " m=" + minute); 222 final ZenModeConfig newConfig = mConfig.copy(); 223 newConfig.sleepEndHour = hour; 224 newConfig.sleepEndMinute = minute; 225 return setZenModeConfig(newConfig); 226 } 227 }); 228 auto.addPreference(mEnd); 229 230 mStart.setDependency(mWhen.getKey()); 231 mEnd.setDependency(mWhen.getKey()); 232 233 updateControls(); 234 } 235 236 private void updateControls() { 237 mDisableListeners = true; 238 mCalls.setChecked(mConfig.allowCalls); 239 mMessages.setChecked(mConfig.allowMessages); 240 mStarred.setSelectedItem(0); 241 mWhen.setSelectedItem( 242 ZenModeConfig.SLEEP_MODE_NIGHTS.equals(mConfig.sleepMode) ? 1 : 243 ZenModeConfig.SLEEP_MODE_WEEKNIGHTS.equals(mConfig.sleepMode) ? 2 : 0); 244 mStart.setTime(mConfig.sleepStartHour, mConfig.sleepStartMinute); 245 mEnd.setTime(mConfig.sleepEndHour, mConfig.sleepEndMinute); 246 mDisableListeners = false; 247 } 248 249 @Override 250 public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { 251 super.onCreateOptionsMenu(menu, inflater); 252 mSearch = menu.findItem(R.id.search); 253 if (mSearch != null) mSearch.setVisible(false); 254 } 255 256 @Override 257 public void onResume() { 258 super.onResume(); 259 updateZenMode(); 260 mSettingsObserver.register(); 261 mActivity.getActionBar().setDisplayOptions(ActionBar.DISPLAY_SHOW_CUSTOM, 262 ActionBar.DISPLAY_SHOW_CUSTOM); 263 mActivity.getActionBar().setCustomView(mSwitch, new ActionBar.LayoutParams( 264 ActionBar.LayoutParams.WRAP_CONTENT, 265 ActionBar.LayoutParams.WRAP_CONTENT, 266 Gravity.CENTER_VERTICAL | Gravity.END)); 267 if (mSearch != null) mSearch.setVisible(false); 268 } 269 270 @Override 271 public void onPause() { 272 super.onPause(); 273 mSettingsObserver.unregister(); 274 mActivity.getActionBar().setDisplayOptions(0, ActionBar.DISPLAY_SHOW_CUSTOM); 275 if (mSearch != null) mSearch.setVisible(true); 276 } 277 278 private void updateZenMode() { 279 mSwitch.setOnCheckedChangeListener(null); 280 final boolean zenMode = Global.getInt(getContentResolver(), 281 Global.ZEN_MODE, Global.ZEN_MODE_OFF) != Global.ZEN_MODE_OFF; 282 mSwitch.setChecked(zenMode); 283 mSwitch.setOnCheckedChangeListener(mSwitchListener); 284 } 285 286 private void updateZenModeConfig() { 287 final ZenModeConfig config = getZenModeConfig(); 288 if (Objects.equals(config, mConfig)) return; 289 if (DEBUG) Log.d(TAG, "updateZenModeConfig"); 290 updateControls(); 291 } 292 293 private ZenModeConfig getZenModeConfig() { 294 final INotificationManager nm = INotificationManager.Stub.asInterface( 295 ServiceManager.getService(Context.NOTIFICATION_SERVICE)); 296 try { 297 return nm.getZenModeConfig(); 298 } catch (Exception e) { 299 Log.w(TAG, "Error calling NoMan", e); 300 return new ZenModeConfig(); 301 } 302 } 303 304 private boolean setZenModeConfig(ZenModeConfig config) { 305 final INotificationManager nm = INotificationManager.Stub.asInterface( 306 ServiceManager.getService(Context.NOTIFICATION_SERVICE)); 307 try { 308 final boolean success = nm.setZenModeConfig(config); 309 if (success) { 310 mConfig = config; 311 if (DEBUG) Log.d(TAG, "Saved mConfig=" + mConfig); 312 } 313 return success; 314 } catch (Exception e) { 315 Log.w(TAG, "Error calling NoMan", e); 316 return false; 317 } 318 } 319 320 private final OnCheckedChangeListener mSwitchListener = new OnCheckedChangeListener() { 321 @Override 322 public void onCheckedChanged(CompoundButton buttonView, final boolean isChecked) { 323 AsyncTask.execute(new Runnable() { 324 @Override 325 public void run() { 326 final int v = isChecked ? Global.ZEN_MODE_ON : Global.ZEN_MODE_OFF; 327 Global.putInt(getContentResolver(), Global.ZEN_MODE, v); 328 } 329 }); 330 } 331 }; 332 333 // Enable indexing of searchable data 334 public static final Indexable.SearchIndexProvider SEARCH_INDEX_DATA_PROVIDER = 335 new BaseSearchIndexProvider() { 336 @Override 337 public List<SearchIndexableRaw> getRawDataToIndex(Context context, boolean enabled) { 338 final List<SearchIndexableRaw> result = new ArrayList<SearchIndexableRaw>(); 339 add(result, context, R.string.zen_mode_settings_title); 340 add(result, context, R.string.zen_mode_general_category); 341 add(result, context, R.string.zen_mode_phone_calls); 342 add(result, context, R.string.zen_mode_messages); 343 add(result, context, R.string.zen_mode_automatic_category); 344 add(result, context, R.string.zen_mode_when); 345 add(result, context, R.string.zen_mode_start_time); 346 add(result, context, R.string.zen_mode_end_time); 347 return result; 348 } 349 350 private void add(List<SearchIndexableRaw> result, Context context, int title) { 351 final Resources res = context.getResources(); 352 final SearchIndexableRaw data = new SearchIndexableRaw(context); 353 data.title = res.getString(title); 354 data.screenTitle = res.getString(R.string.zen_mode_settings_title); 355 result.add(data); 356 } 357 }; 358 359 private final class SettingsObserver extends ContentObserver { 360 private final Uri ZEN_MODE_URI = Global.getUriFor(Global.ZEN_MODE); 361 private final Uri ZEN_MODE_CONFIG_ETAG_URI = Global.getUriFor(Global.ZEN_MODE_CONFIG_ETAG); 362 363 public SettingsObserver() { 364 super(mHandler); 365 } 366 367 public void register() { 368 getContentResolver().registerContentObserver(ZEN_MODE_URI, false, this); 369 getContentResolver().registerContentObserver(ZEN_MODE_CONFIG_ETAG_URI, false, this); 370 } 371 372 public void unregister() { 373 getContentResolver().unregisterContentObserver(this); 374 } 375 376 @Override 377 public void onChange(boolean selfChange, Uri uri) { 378 super.onChange(selfChange, uri); 379 if (ZEN_MODE_URI.equals(uri)) { 380 updateZenMode(); 381 } 382 if (ZEN_MODE_CONFIG_ETAG_URI.equals(uri)) { 383 updateZenModeConfig(); 384 } 385 } 386 } 387 388 private static class TimePickerPreference extends Preference { 389 private final Context mContext; 390 391 private int mSummaryFormat; 392 private int mHourOfDay; 393 private int mMinute; 394 private Callback mCallback; 395 396 public TimePickerPreference(Context context, final FragmentManager mgr) { 397 super(context); 398 mContext = context; 399 setPersistent(false); 400 setOnPreferenceClickListener(new OnPreferenceClickListener(){ 401 @Override 402 public boolean onPreferenceClick(Preference preference) { 403 final TimePickerFragment frag = new TimePickerFragment(); 404 frag.pref = TimePickerPreference.this; 405 frag.show(mgr, TimePickerPreference.class.getName()); 406 return true; 407 } 408 }); 409 } 410 411 public void setCallback(Callback callback) { 412 mCallback = callback; 413 } 414 415 public void setSummaryFormat(int resId) { 416 mSummaryFormat = resId; 417 updateSummary(); 418 } 419 420 public void setTime(int hourOfDay, int minute) { 421 if (mCallback != null && !mCallback.onSetTime(hourOfDay, minute)) return; 422 mHourOfDay = hourOfDay; 423 mMinute = minute; 424 updateSummary(); 425 } 426 427 private void updateSummary() { 428 final Calendar c = Calendar.getInstance(); 429 c.set(Calendar.HOUR_OF_DAY, mHourOfDay); 430 c.set(Calendar.MINUTE, mMinute); 431 String time = DateFormat.getTimeFormat(mContext).format(c.getTime()); 432 if (mSummaryFormat != 0) { 433 time = mContext.getResources().getString(mSummaryFormat, time); 434 } 435 setSummary(time); 436 } 437 438 public static class TimePickerFragment extends DialogFragment implements 439 TimePickerDialog.OnTimeSetListener { 440 public TimePickerPreference pref; 441 442 @Override 443 public Dialog onCreateDialog(Bundle savedInstanceState) { 444 final boolean usePref = pref != null && pref.mHourOfDay >= 0 && pref.mMinute >= 0; 445 final Calendar c = Calendar.getInstance(); 446 final int hour = usePref ? pref.mHourOfDay : c.get(Calendar.HOUR_OF_DAY); 447 final int minute = usePref ? pref.mMinute : c.get(Calendar.MINUTE); 448 return new TimePickerDialog(getActivity(), this, hour, minute, 449 DateFormat.is24HourFormat(getActivity())); 450 } 451 452 public void onTimeSet(TimePicker view, int hourOfDay, int minute) { 453 if (pref != null) { 454 pref.setTime(hourOfDay, minute); 455 } 456 } 457 } 458 459 public interface Callback { 460 boolean onSetTime(int hour, int minute); 461 } 462 } 463} 464