ZenModeSettings.java revision 7ad86dce1c1dd07bb06bd996b0daa6b059eeca21
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.AlertDialog; 20import android.app.Dialog; 21import android.app.DialogFragment; 22import android.app.FragmentManager; 23import android.app.INotificationManager; 24import android.app.TimePickerDialog; 25import android.content.Context; 26import android.content.DialogInterface; 27import android.content.DialogInterface.OnDismissListener; 28import android.content.pm.PackageManager; 29import android.content.res.Resources; 30import android.database.ContentObserver; 31import android.graphics.Typeface; 32import android.net.Uri; 33import android.os.AsyncTask; 34import android.os.Bundle; 35import android.os.Handler; 36import android.os.ServiceManager; 37import android.preference.Preference; 38import android.preference.Preference.OnPreferenceChangeListener; 39import android.preference.Preference.OnPreferenceClickListener; 40import android.preference.PreferenceCategory; 41import android.preference.PreferenceScreen; 42import android.preference.SwitchPreference; 43import android.provider.Settings.Global; 44import android.service.notification.Condition; 45import android.service.notification.ZenModeConfig; 46import android.text.format.DateFormat; 47import android.util.Log; 48import android.util.SparseArray; 49import android.view.View; 50import android.view.ViewGroup; 51import android.widget.Switch; 52import android.widget.TextView; 53import android.widget.TimePicker; 54 55import com.android.settings.R; 56import com.android.settings.SettingsActivity; 57import com.android.settings.SettingsPreferenceFragment; 58import com.android.settings.Utils; 59import com.android.settings.search.BaseSearchIndexProvider; 60import com.android.settings.search.Indexable; 61import com.android.settings.search.SearchIndexableRaw; 62import com.android.settings.widget.SwitchBar; 63 64import java.util.ArrayList; 65import java.util.Calendar; 66import java.util.List; 67import java.util.Objects; 68 69public class ZenModeSettings extends SettingsPreferenceFragment implements Indexable, 70 SwitchBar.OnSwitchChangeListener { 71 private static final String TAG = "ZenModeSettings"; 72 private static final boolean DEBUG = true; 73 74 private static final String KEY_GENERAL = "general"; 75 private static final String KEY_CALLS = "phone_calls"; 76 private static final String KEY_MESSAGES = "messages"; 77 private static final String KEY_STARRED = "starred"; 78 79 private static final String KEY_AUTOMATIC = "automatic"; 80 private static final String KEY_WHEN = "when"; 81 private static final String KEY_START_TIME = "start_time"; 82 private static final String KEY_END_TIME = "end_time"; 83 84 private static final String KEY_AUTOMATION = "automation"; 85 private static final String KEY_ENTRY = "entry"; 86 private static final String KEY_CONDITION_PROVIDERS = "manage_condition_providers"; 87 88 private static SparseArray<String> allKeyTitles(Context context) { 89 final SparseArray<String> rt = new SparseArray<String>(); 90 rt.put(R.string.zen_mode_general_category, KEY_GENERAL); 91 if (Utils.isVoiceCapable(context)) { 92 rt.put(R.string.zen_mode_phone_calls, KEY_CALLS); 93 } 94 rt.put(R.string.zen_mode_messages, KEY_MESSAGES); 95 rt.put(R.string.zen_mode_from_starred, KEY_STARRED); 96 rt.put(R.string.zen_mode_automatic_category, KEY_AUTOMATIC); 97 rt.put(R.string.zen_mode_when, KEY_WHEN); 98 rt.put(R.string.zen_mode_start_time, KEY_START_TIME); 99 rt.put(R.string.zen_mode_end_time, KEY_END_TIME); 100 rt.put(R.string.zen_mode_automation_category, KEY_AUTOMATION); 101 rt.put(R.string.manage_condition_providers, KEY_CONDITION_PROVIDERS); 102 return rt; 103 } 104 105 private final Handler mHandler = new Handler(); 106 private final SettingsObserver mSettingsObserver = new SettingsObserver(); 107 108 private SwitchBar mSwitchBar; 109 private Switch mSwitch; 110 private Context mContext; 111 private PackageManager mPM; 112 private ZenModeConfig mConfig; 113 private boolean mDisableListeners; 114 private SwitchPreference mCalls; 115 private SwitchPreference mMessages; 116 private DropDownPreference mStarred; 117 private DropDownPreference mWhen; 118 private TimePickerPreference mStart; 119 private TimePickerPreference mEnd; 120 private PreferenceCategory mAutomationCategory; 121 private Preference mEntry; 122 private Preference mConditionProviders; 123 private AlertDialog mDialog; 124 private boolean mIgnoreNext; 125 126 @Override 127 public void onSwitchChanged(Switch switchView, final boolean isChecked) { 128 if (DEBUG) Log.d(TAG, "onPreferenceChange isChecked=" + isChecked 129 + " mIgnoreNext=" + mIgnoreNext); 130 if (mIgnoreNext) { 131 mIgnoreNext = false; 132 } 133 AsyncTask.execute(new Runnable() { 134 @Override 135 public void run() { 136 final int v = isChecked ? Global.ZEN_MODE_ON : Global.ZEN_MODE_OFF; 137 putZenModeSetting(v); 138 final int n = ConditionProviderSettings.getEnabledProviderCount(mContext); 139 if (n > 0) { 140 mHandler.post(isChecked ? mShowDialog : mHideDialog); 141 } 142 } 143 }); 144 } 145 146 @Override 147 public void onActivityCreated(Bundle savedInstanceState) { 148 super.onActivityCreated(savedInstanceState); 149 mContext = getActivity(); 150 mPM = mContext.getPackageManager(); 151 final Resources res = mContext.getResources(); 152 final int p = res.getDimensionPixelSize(R.dimen.content_margin_left); 153 154 addPreferencesFromResource(R.xml.zen_mode_settings); 155 final PreferenceScreen root = getPreferenceScreen(); 156 157 mConfig = getZenModeConfig(); 158 if (DEBUG) Log.d(TAG, "Loaded mConfig=" + mConfig); 159 160 mSwitchBar = ((SettingsActivity) mContext).getSwitchBar(); 161 mSwitch = mSwitchBar.getSwitch(); 162 163 final PreferenceCategory general = (PreferenceCategory) root.findPreference(KEY_GENERAL); 164 165 mCalls = (SwitchPreference) general.findPreference(KEY_CALLS); 166 if (Utils.isVoiceCapable(mContext)) { 167 mCalls.setOnPreferenceChangeListener(new OnPreferenceChangeListener() { 168 @Override 169 public boolean onPreferenceChange(Preference preference, Object newValue) { 170 if (mDisableListeners) return true; 171 final boolean val = (Boolean) newValue; 172 if (val == mConfig.allowCalls) return true; 173 if (DEBUG) Log.d(TAG, "onPrefChange allowCalls=" + val); 174 final ZenModeConfig newConfig = mConfig.copy(); 175 newConfig.allowCalls = val; 176 return setZenModeConfig(newConfig); 177 } 178 }); 179 } else { 180 general.removePreference(mCalls); 181 mCalls = null; 182 } 183 184 mMessages = (SwitchPreference) general.findPreference(KEY_MESSAGES); 185 mMessages.setOnPreferenceChangeListener(new OnPreferenceChangeListener() { 186 @Override 187 public boolean onPreferenceChange(Preference preference, Object newValue) { 188 if (mDisableListeners) return true; 189 final boolean val = (Boolean) newValue; 190 if (val == mConfig.allowMessages) return true; 191 if (DEBUG) Log.d(TAG, "onPrefChange allowMessages=" + val); 192 final ZenModeConfig newConfig = mConfig.copy(); 193 newConfig.allowMessages = val; 194 return setZenModeConfig(newConfig); 195 } 196 }); 197 198 mStarred = new DropDownPreference(mContext); 199 mStarred.setKey(KEY_STARRED); 200 mStarred.setTitle(R.string.zen_mode_from); 201 mStarred.setDropDownWidth(R.dimen.zen_mode_dropdown_width); 202 mStarred.addItem(R.string.zen_mode_from_anyone, ZenModeConfig.SOURCE_ANYONE); 203 mStarred.addItem(R.string.zen_mode_from_starred, ZenModeConfig.SOURCE_STAR); 204 mStarred.addItem(R.string.zen_mode_from_contacts, ZenModeConfig.SOURCE_CONTACT); 205 mStarred.setCallback(new DropDownPreference.Callback() { 206 @Override 207 public boolean onItemSelected(int pos, Object newValue) { 208 if (mDisableListeners) return true; 209 final int val = (Integer) newValue; 210 if (val == mConfig.allowFrom) return true; 211 if (DEBUG) Log.d(TAG, "onPrefChange allowFrom=" + 212 ZenModeConfig.sourceToString(val)); 213 final ZenModeConfig newConfig = mConfig.copy(); 214 newConfig.allowFrom = val; 215 return setZenModeConfig(newConfig); 216 } 217 }); 218 general.addPreference(mStarred); 219 220 final Preference alarmInfo = new Preference(mContext) { 221 @Override 222 public View getView(View convertView, ViewGroup parent) { 223 final TextView tv = new TextView(mContext); 224 tv.setTypeface(Typeface.create(Typeface.DEFAULT, Typeface.ITALIC)); 225 tv.setPadding(p, p, p, p); 226 tv.setText(R.string.zen_mode_alarm_info); 227 return tv; 228 } 229 }; 230 alarmInfo.setPersistent(false); 231 alarmInfo.setSelectable(false); 232 general.addPreference(alarmInfo); 233 234 final PreferenceCategory auto = (PreferenceCategory) root.findPreference(KEY_AUTOMATIC); 235 236 mWhen = new DropDownPreference(mContext); 237 mWhen.setKey(KEY_WHEN); 238 mWhen.setTitle(R.string.zen_mode_when); 239 mWhen.setDropDownWidth(R.dimen.zen_mode_dropdown_width); 240 mWhen.addItem(R.string.zen_mode_when_every_night, ZenModeConfig.SLEEP_MODE_NIGHTS); 241 mWhen.addItem(R.string.zen_mode_when_weeknights, ZenModeConfig.SLEEP_MODE_WEEKNIGHTS); 242 mWhen.addItem(R.string.zen_mode_when_never, null); 243 mWhen.setCallback(new DropDownPreference.Callback() { 244 @Override 245 public boolean onItemSelected(int pos, Object value) { 246 if (mDisableListeners) return true; 247 final String mode = (String) value; 248 if (Objects.equals(mode, mConfig.sleepMode)) return true; 249 if (DEBUG) Log.d(TAG, "onPrefChange sleepMode=" + mode); 250 final ZenModeConfig newConfig = mConfig.copy(); 251 newConfig.sleepMode = mode; 252 return setZenModeConfig(newConfig); 253 } 254 }); 255 auto.addPreference(mWhen); 256 257 final FragmentManager mgr = getFragmentManager(); 258 259 mStart = new TimePickerPreference(mContext, mgr); 260 mStart.setKey(KEY_START_TIME); 261 mStart.setTitle(R.string.zen_mode_start_time); 262 mStart.setCallback(new TimePickerPreference.Callback() { 263 @Override 264 public boolean onSetTime(int hour, int minute) { 265 if (mDisableListeners) return true; 266 if (!ZenModeConfig.isValidHour(hour)) return false; 267 if (!ZenModeConfig.isValidMinute(minute)) return false; 268 if (hour == mConfig.sleepStartHour && minute == mConfig.sleepStartMinute) { 269 return true; 270 } 271 if (DEBUG) Log.d(TAG, "onPrefChange sleepStart h=" + hour + " m=" + minute); 272 final ZenModeConfig newConfig = mConfig.copy(); 273 newConfig.sleepStartHour = hour; 274 newConfig.sleepStartMinute = minute; 275 return setZenModeConfig(newConfig); 276 } 277 }); 278 auto.addPreference(mStart); 279 mStart.setDependency(mWhen.getKey()); 280 281 mEnd = new TimePickerPreference(mContext, mgr); 282 mEnd.setKey(KEY_END_TIME); 283 mEnd.setTitle(R.string.zen_mode_end_time); 284 mEnd.setSummaryFormat(R.string.zen_mode_end_time_summary_format); 285 mEnd.setCallback(new TimePickerPreference.Callback() { 286 @Override 287 public boolean onSetTime(int hour, int minute) { 288 if (mDisableListeners) return true; 289 if (!ZenModeConfig.isValidHour(hour)) return false; 290 if (!ZenModeConfig.isValidMinute(minute)) return false; 291 if (hour == mConfig.sleepEndHour && minute == mConfig.sleepEndMinute) { 292 return true; 293 } 294 if (DEBUG) Log.d(TAG, "onPrefChange sleepEnd h=" + hour + " m=" + minute); 295 final ZenModeConfig newConfig = mConfig.copy(); 296 newConfig.sleepEndHour = hour; 297 newConfig.sleepEndMinute = minute; 298 return setZenModeConfig(newConfig); 299 } 300 }); 301 auto.addPreference(mEnd); 302 mEnd.setDependency(mWhen.getKey()); 303 304 mAutomationCategory = (PreferenceCategory) findPreference(KEY_AUTOMATION); 305 mEntry = findPreference(KEY_ENTRY); 306 mEntry.setOnPreferenceClickListener(new OnPreferenceClickListener() { 307 @Override 308 public boolean onPreferenceClick(Preference preference) { 309 new AlertDialog.Builder(mContext) 310 .setTitle(R.string.zen_mode_entry_conditions_title) 311 .setView(new ZenModeAutomaticConditionSelection(mContext)) 312 .setOnDismissListener(new OnDismissListener() { 313 @Override 314 public void onDismiss(DialogInterface dialog) { 315 refreshAutomationSection(); 316 } 317 }) 318 .setPositiveButton(R.string.dlg_ok, null) 319 .show(); 320 return true; 321 } 322 }); 323 mConditionProviders = findPreference(KEY_CONDITION_PROVIDERS); 324 325 updateZenMode(); 326 updateControls(); 327 } 328 329 private void updateControls() { 330 mDisableListeners = true; 331 if (mCalls != null) { 332 mCalls.setChecked(mConfig.allowCalls); 333 } 334 mMessages.setChecked(mConfig.allowMessages); 335 mStarred.setSelectedValue(mConfig.allowFrom); 336 mWhen.setSelectedValue(mConfig.sleepMode); 337 mStart.setTime(mConfig.sleepStartHour, mConfig.sleepStartMinute); 338 mEnd.setTime(mConfig.sleepEndHour, mConfig.sleepEndMinute); 339 mDisableListeners = false; 340 refreshAutomationSection(); 341 } 342 343 private void refreshAutomationSection() { 344 if (mConditionProviders != null) { 345 final int total = ConditionProviderSettings.getProviderCount(mPM); 346 if (total == 0) { 347 getPreferenceScreen().removePreference(mAutomationCategory); 348 } else { 349 final int n = ConditionProviderSettings.getEnabledProviderCount(mContext); 350 if (n == 0) { 351 mConditionProviders.setSummary(getResources().getString( 352 R.string.manage_condition_providers_summary_zero)); 353 } else { 354 mConditionProviders.setSummary(String.format(getResources().getQuantityString( 355 R.plurals.manage_condition_providers_summary_nonzero, 356 n, n))); 357 } 358 final String entrySummary = getEntryConditionSummary(); 359 if (n == 0 || entrySummary == null) { 360 mEntry.setSummary(R.string.zen_mode_entry_conditions_summary_none); 361 } else { 362 mEntry.setSummary(entrySummary); 363 } 364 } 365 } 366 } 367 368 private String getEntryConditionSummary() { 369 final INotificationManager nm = INotificationManager.Stub.asInterface( 370 ServiceManager.getService(Context.NOTIFICATION_SERVICE)); 371 try { 372 final Condition[] automatic = nm.getAutomaticZenModeConditions(); 373 if (automatic == null || automatic.length == 0) { 374 return null; 375 } 376 final String divider = getString(R.string.summary_divider_text); 377 final StringBuilder sb = new StringBuilder(); 378 for (int i = 0; i < automatic.length; i++) { 379 if (i > 0) sb.append(divider); 380 sb.append(automatic[i].summary); 381 } 382 return sb.toString(); 383 } catch (Exception e) { 384 Log.w(TAG, "Error calling getAutomaticZenModeConditions", e); 385 return null; 386 } 387 } 388 389 @Override 390 public void onResume() { 391 super.onResume(); 392 updateControls(); 393 updateZenMode(); 394 mSettingsObserver.register(); 395 mSwitchBar.addOnSwitchChangeListener(this); 396 mSwitchBar.show(); 397 } 398 399 @Override 400 public void onPause() { 401 super.onPause(); 402 mSettingsObserver.unregister(); 403 mSwitchBar.removeOnSwitchChangeListener(this); 404 mSwitchBar.hide(); 405 } 406 407 private void updateZenMode() { 408 final boolean zenMode = Global.getInt(getContentResolver(), 409 Global.ZEN_MODE, Global.ZEN_MODE_OFF) != Global.ZEN_MODE_OFF; 410 if (mSwitch.isChecked() != zenMode) { 411 mSwitch.setChecked(zenMode); 412 mIgnoreNext = true; 413 } 414 } 415 416 private void updateZenModeConfig() { 417 final ZenModeConfig config = getZenModeConfig(); 418 if (Objects.equals(config, mConfig)) return; 419 mConfig = config; 420 if (DEBUG) Log.d(TAG, "updateZenModeConfig mConfig=" + mConfig); 421 updateControls(); 422 } 423 424 private ZenModeConfig getZenModeConfig() { 425 final INotificationManager nm = INotificationManager.Stub.asInterface( 426 ServiceManager.getService(Context.NOTIFICATION_SERVICE)); 427 try { 428 return nm.getZenModeConfig(); 429 } catch (Exception e) { 430 Log.w(TAG, "Error calling NoMan", e); 431 return new ZenModeConfig(); 432 } 433 } 434 435 private boolean setZenModeConfig(ZenModeConfig config) { 436 final INotificationManager nm = INotificationManager.Stub.asInterface( 437 ServiceManager.getService(Context.NOTIFICATION_SERVICE)); 438 try { 439 final boolean success = nm.setZenModeConfig(config); 440 if (success) { 441 mConfig = config; 442 if (DEBUG) Log.d(TAG, "Saved mConfig=" + mConfig); 443 } 444 return success; 445 } catch (Exception e) { 446 Log.w(TAG, "Error calling NoMan", e); 447 return false; 448 } 449 } 450 451 protected void putZenModeSetting(int value) { 452 Global.putInt(getContentResolver(), Global.ZEN_MODE, value); 453 } 454 455 protected ZenModeConditionSelection newConditionSelection() { 456 return new ZenModeConditionSelection(mContext); 457 } 458 459 private final Runnable mHideDialog = new Runnable() { 460 @Override 461 public void run() { 462 if (mDialog != null) { 463 mDialog.dismiss(); 464 mDialog = null; 465 } 466 } 467 }; 468 469 private final Runnable mShowDialog = new Runnable() { 470 @Override 471 public void run() { 472 mDialog = new AlertDialog.Builder(mContext) 473 .setTitle(R.string.zen_mode_settings_title) 474 .setView(newConditionSelection()) 475 .setNegativeButton(R.string.dlg_cancel, new DialogInterface.OnClickListener() { 476 @Override 477 public void onClick(DialogInterface dialog, int which) { 478 putZenModeSetting(Global.ZEN_MODE_OFF); 479 } 480 }) 481 .setPositiveButton(R.string.dlg_ok, new DialogInterface.OnClickListener() { 482 @Override 483 public void onClick(DialogInterface dialog, int which) { 484 // noop 485 } 486 }) 487 .show(); 488 } 489 }; 490 491 // Enable indexing of searchable data 492 public static final Indexable.SearchIndexProvider SEARCH_INDEX_DATA_PROVIDER = 493 new BaseSearchIndexProvider() { 494 @Override 495 public List<SearchIndexableRaw> getRawDataToIndex(Context context, boolean enabled) { 496 final SparseArray<String> keyTitles = allKeyTitles(context); 497 final int N = keyTitles.size(); 498 final List<SearchIndexableRaw> result = new ArrayList<SearchIndexableRaw>(N); 499 final Resources res = context.getResources(); 500 for (int i = 0; i < N; i++) { 501 final SearchIndexableRaw data = new SearchIndexableRaw(context); 502 data.key = keyTitles.valueAt(i); 503 data.title = res.getString(keyTitles.keyAt(i)); 504 data.screenTitle = res.getString(R.string.zen_mode_settings_title); 505 result.add(data); 506 } 507 return result; 508 } 509 510 public List<String> getNonIndexableKeys(Context context) { 511 final ArrayList<String> rt = new ArrayList<String>(); 512 if (!Utils.isVoiceCapable(context)) { 513 rt.add(KEY_CALLS); 514 } 515 return rt; 516 } 517 }; 518 519 private final class SettingsObserver extends ContentObserver { 520 private final Uri ZEN_MODE_URI = Global.getUriFor(Global.ZEN_MODE); 521 private final Uri ZEN_MODE_CONFIG_ETAG_URI = Global.getUriFor(Global.ZEN_MODE_CONFIG_ETAG); 522 523 public SettingsObserver() { 524 super(mHandler); 525 } 526 527 public void register() { 528 getContentResolver().registerContentObserver(ZEN_MODE_URI, false, this); 529 getContentResolver().registerContentObserver(ZEN_MODE_CONFIG_ETAG_URI, false, this); 530 } 531 532 public void unregister() { 533 getContentResolver().unregisterContentObserver(this); 534 } 535 536 @Override 537 public void onChange(boolean selfChange, Uri uri) { 538 super.onChange(selfChange, uri); 539 if (ZEN_MODE_URI.equals(uri)) { 540 updateZenMode(); 541 } 542 if (ZEN_MODE_CONFIG_ETAG_URI.equals(uri)) { 543 updateZenModeConfig(); 544 } 545 } 546 } 547 548 private static class TimePickerPreference extends Preference { 549 private final Context mContext; 550 551 private int mSummaryFormat; 552 private int mHourOfDay; 553 private int mMinute; 554 private Callback mCallback; 555 556 public TimePickerPreference(Context context, final FragmentManager mgr) { 557 super(context); 558 mContext = context; 559 setPersistent(false); 560 setOnPreferenceClickListener(new OnPreferenceClickListener(){ 561 @Override 562 public boolean onPreferenceClick(Preference preference) { 563 final TimePickerFragment frag = new TimePickerFragment(); 564 frag.pref = TimePickerPreference.this; 565 frag.show(mgr, TimePickerPreference.class.getName()); 566 return true; 567 } 568 }); 569 } 570 571 public void setCallback(Callback callback) { 572 mCallback = callback; 573 } 574 575 public void setSummaryFormat(int resId) { 576 mSummaryFormat = resId; 577 updateSummary(); 578 } 579 580 public void setTime(int hourOfDay, int minute) { 581 if (mCallback != null && !mCallback.onSetTime(hourOfDay, minute)) return; 582 mHourOfDay = hourOfDay; 583 mMinute = minute; 584 updateSummary(); 585 } 586 587 private void updateSummary() { 588 final Calendar c = Calendar.getInstance(); 589 c.set(Calendar.HOUR_OF_DAY, mHourOfDay); 590 c.set(Calendar.MINUTE, mMinute); 591 String time = DateFormat.getTimeFormat(mContext).format(c.getTime()); 592 if (mSummaryFormat != 0) { 593 time = mContext.getResources().getString(mSummaryFormat, time); 594 } 595 setSummary(time); 596 } 597 598 public static class TimePickerFragment extends DialogFragment implements 599 TimePickerDialog.OnTimeSetListener { 600 public TimePickerPreference pref; 601 602 @Override 603 public Dialog onCreateDialog(Bundle savedInstanceState) { 604 final boolean usePref = pref != null && pref.mHourOfDay >= 0 && pref.mMinute >= 0; 605 final Calendar c = Calendar.getInstance(); 606 final int hour = usePref ? pref.mHourOfDay : c.get(Calendar.HOUR_OF_DAY); 607 final int minute = usePref ? pref.mMinute : c.get(Calendar.MINUTE); 608 return new TimePickerDialog(getActivity(), this, hour, minute, 609 DateFormat.is24HourFormat(getActivity())); 610 } 611 612 public void onTimeSet(TimePicker view, int hourOfDay, int minute) { 613 if (pref != null) { 614 pref.setTime(hourOfDay, minute); 615 } 616 } 617 } 618 619 public interface Callback { 620 boolean onSetTime(int hour, int minute); 621 } 622 } 623} 624