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