1/* 2 * Copyright (C) 2007 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 android.widget; 18 19import android.annotation.Widget; 20import android.content.Context; 21import android.content.res.Configuration; 22import android.content.res.TypedArray; 23import android.os.Parcel; 24import android.os.Parcelable; 25import android.text.format.DateFormat; 26import android.text.format.DateUtils; 27import android.util.AttributeSet; 28import android.view.LayoutInflater; 29import android.view.View; 30import android.view.ViewGroup; 31import android.view.accessibility.AccessibilityEvent; 32import android.view.accessibility.AccessibilityNodeInfo; 33import android.view.inputmethod.EditorInfo; 34import android.view.inputmethod.InputMethodManager; 35import android.widget.NumberPicker.OnValueChangeListener; 36 37import com.android.internal.R; 38 39import java.text.DateFormatSymbols; 40import java.util.Calendar; 41import java.util.Locale; 42 43/** 44 * A view for selecting the time of day, in either 24 hour or AM/PM mode. The 45 * hour, each minute digit, and AM/PM (if applicable) can be conrolled by 46 * vertical spinners. The hour can be entered by keyboard input. Entering in two 47 * digit hours can be accomplished by hitting two digits within a timeout of 48 * about a second (e.g. '1' then '2' to select 12). The minutes can be entered 49 * by entering single digits. Under AM/PM mode, the user can hit 'a', 'A", 'p' 50 * or 'P' to pick. For a dialog using this view, see 51 * {@link android.app.TimePickerDialog}. 52 *<p> 53 * See the <a href="{@docRoot}guide/topics/ui/controls/pickers.html">Pickers</a> 54 * guide. 55 * </p> 56 */ 57@Widget 58public class TimePicker extends FrameLayout { 59 60 private static final boolean DEFAULT_ENABLED_STATE = true; 61 62 private static final int HOURS_IN_HALF_DAY = 12; 63 64 /** 65 * A no-op callback used in the constructor to avoid null checks later in 66 * the code. 67 */ 68 private static final OnTimeChangedListener NO_OP_CHANGE_LISTENER = new OnTimeChangedListener() { 69 public void onTimeChanged(TimePicker view, int hourOfDay, int minute) { 70 } 71 }; 72 73 // state 74 private boolean mIs24HourView; 75 76 private boolean mIsAm; 77 78 // ui components 79 private final NumberPicker mHourSpinner; 80 81 private final NumberPicker mMinuteSpinner; 82 83 private final NumberPicker mAmPmSpinner; 84 85 private final EditText mHourSpinnerInput; 86 87 private final EditText mMinuteSpinnerInput; 88 89 private final EditText mAmPmSpinnerInput; 90 91 private final TextView mDivider; 92 93 // Note that the legacy implementation of the TimePicker is 94 // using a button for toggling between AM/PM while the new 95 // version uses a NumberPicker spinner. Therefore the code 96 // accommodates these two cases to be backwards compatible. 97 private final Button mAmPmButton; 98 99 private final String[] mAmPmStrings; 100 101 private boolean mIsEnabled = DEFAULT_ENABLED_STATE; 102 103 // callbacks 104 private OnTimeChangedListener mOnTimeChangedListener; 105 106 private Calendar mTempCalendar; 107 108 private Locale mCurrentLocale; 109 110 private boolean mHourWithTwoDigit; 111 private char mHourFormat; 112 113 /** 114 * The callback interface used to indicate the time has been adjusted. 115 */ 116 public interface OnTimeChangedListener { 117 118 /** 119 * @param view The view associated with this listener. 120 * @param hourOfDay The current hour. 121 * @param minute The current minute. 122 */ 123 void onTimeChanged(TimePicker view, int hourOfDay, int minute); 124 } 125 126 public TimePicker(Context context) { 127 this(context, null); 128 } 129 130 public TimePicker(Context context, AttributeSet attrs) { 131 this(context, attrs, R.attr.timePickerStyle); 132 } 133 134 public TimePicker(Context context, AttributeSet attrs, int defStyle) { 135 super(context, attrs, defStyle); 136 137 // initialization based on locale 138 setCurrentLocale(Locale.getDefault()); 139 140 // process style attributes 141 TypedArray attributesArray = context.obtainStyledAttributes( 142 attrs, R.styleable.TimePicker, defStyle, 0); 143 int layoutResourceId = attributesArray.getResourceId( 144 R.styleable.TimePicker_internalLayout, R.layout.time_picker); 145 attributesArray.recycle(); 146 147 LayoutInflater inflater = (LayoutInflater) context.getSystemService( 148 Context.LAYOUT_INFLATER_SERVICE); 149 inflater.inflate(layoutResourceId, this, true); 150 151 // hour 152 mHourSpinner = (NumberPicker) findViewById(R.id.hour); 153 mHourSpinner.setOnValueChangedListener(new NumberPicker.OnValueChangeListener() { 154 public void onValueChange(NumberPicker spinner, int oldVal, int newVal) { 155 updateInputState(); 156 if (!is24HourView()) { 157 if ((oldVal == HOURS_IN_HALF_DAY - 1 && newVal == HOURS_IN_HALF_DAY) 158 || (oldVal == HOURS_IN_HALF_DAY && newVal == HOURS_IN_HALF_DAY - 1)) { 159 mIsAm = !mIsAm; 160 updateAmPmControl(); 161 } 162 } 163 onTimeChanged(); 164 } 165 }); 166 mHourSpinnerInput = (EditText) mHourSpinner.findViewById(R.id.numberpicker_input); 167 mHourSpinnerInput.setImeOptions(EditorInfo.IME_ACTION_NEXT); 168 169 // divider (only for the new widget style) 170 mDivider = (TextView) findViewById(R.id.divider); 171 if (mDivider != null) { 172 setDividerText(); 173 } 174 175 // minute 176 mMinuteSpinner = (NumberPicker) findViewById(R.id.minute); 177 mMinuteSpinner.setMinValue(0); 178 mMinuteSpinner.setMaxValue(59); 179 mMinuteSpinner.setOnLongPressUpdateInterval(100); 180 mMinuteSpinner.setFormatter(NumberPicker.getTwoDigitFormatter()); 181 mMinuteSpinner.setOnValueChangedListener(new NumberPicker.OnValueChangeListener() { 182 public void onValueChange(NumberPicker spinner, int oldVal, int newVal) { 183 updateInputState(); 184 int minValue = mMinuteSpinner.getMinValue(); 185 int maxValue = mMinuteSpinner.getMaxValue(); 186 if (oldVal == maxValue && newVal == minValue) { 187 int newHour = mHourSpinner.getValue() + 1; 188 if (!is24HourView() && newHour == HOURS_IN_HALF_DAY) { 189 mIsAm = !mIsAm; 190 updateAmPmControl(); 191 } 192 mHourSpinner.setValue(newHour); 193 } else if (oldVal == minValue && newVal == maxValue) { 194 int newHour = mHourSpinner.getValue() - 1; 195 if (!is24HourView() && newHour == HOURS_IN_HALF_DAY - 1) { 196 mIsAm = !mIsAm; 197 updateAmPmControl(); 198 } 199 mHourSpinner.setValue(newHour); 200 } 201 onTimeChanged(); 202 } 203 }); 204 mMinuteSpinnerInput = (EditText) mMinuteSpinner.findViewById(R.id.numberpicker_input); 205 mMinuteSpinnerInput.setImeOptions(EditorInfo.IME_ACTION_NEXT); 206 207 /* Get the localized am/pm strings and use them in the spinner */ 208 mAmPmStrings = new DateFormatSymbols().getAmPmStrings(); 209 210 // am/pm 211 View amPmView = findViewById(R.id.amPm); 212 if (amPmView instanceof Button) { 213 mAmPmSpinner = null; 214 mAmPmSpinnerInput = null; 215 mAmPmButton = (Button) amPmView; 216 mAmPmButton.setOnClickListener(new OnClickListener() { 217 public void onClick(View button) { 218 button.requestFocus(); 219 mIsAm = !mIsAm; 220 updateAmPmControl(); 221 onTimeChanged(); 222 } 223 }); 224 } else { 225 mAmPmButton = null; 226 mAmPmSpinner = (NumberPicker) amPmView; 227 mAmPmSpinner.setMinValue(0); 228 mAmPmSpinner.setMaxValue(1); 229 mAmPmSpinner.setDisplayedValues(mAmPmStrings); 230 mAmPmSpinner.setOnValueChangedListener(new OnValueChangeListener() { 231 public void onValueChange(NumberPicker picker, int oldVal, int newVal) { 232 updateInputState(); 233 picker.requestFocus(); 234 mIsAm = !mIsAm; 235 updateAmPmControl(); 236 onTimeChanged(); 237 } 238 }); 239 mAmPmSpinnerInput = (EditText) mAmPmSpinner.findViewById(R.id.numberpicker_input); 240 mAmPmSpinnerInput.setImeOptions(EditorInfo.IME_ACTION_DONE); 241 } 242 243 if (isAmPmAtStart()) { 244 // Move the am/pm view to the beginning 245 ViewGroup amPmParent = (ViewGroup) findViewById(R.id.timePickerLayout); 246 amPmParent.removeView(amPmView); 247 amPmParent.addView(amPmView, 0); 248 // Swap layout margins if needed. They may be not symmetrical (Old Standard Theme for 249 // example and not for Holo Theme) 250 ViewGroup.MarginLayoutParams lp = (ViewGroup.MarginLayoutParams) amPmView.getLayoutParams(); 251 final int startMargin = lp.getMarginStart(); 252 final int endMargin = lp.getMarginEnd(); 253 if (startMargin != endMargin) { 254 lp.setMarginStart(endMargin); 255 lp.setMarginEnd(startMargin); 256 } 257 } 258 259 getHourFormatData(); 260 261 // update controls to initial state 262 updateHourControl(); 263 updateMinuteControl(); 264 updateAmPmControl(); 265 266 setOnTimeChangedListener(NO_OP_CHANGE_LISTENER); 267 268 // set to current time 269 setCurrentHour(mTempCalendar.get(Calendar.HOUR_OF_DAY)); 270 setCurrentMinute(mTempCalendar.get(Calendar.MINUTE)); 271 272 if (!isEnabled()) { 273 setEnabled(false); 274 } 275 276 // set the content descriptions 277 setContentDescriptions(); 278 279 // If not explicitly specified this view is important for accessibility. 280 if (getImportantForAccessibility() == IMPORTANT_FOR_ACCESSIBILITY_AUTO) { 281 setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_YES); 282 } 283 } 284 285 private void getHourFormatData() { 286 final Locale defaultLocale = Locale.getDefault(); 287 final String bestDateTimePattern = DateFormat.getBestDateTimePattern(defaultLocale, 288 (mIs24HourView) ? "Hm" : "hm"); 289 final int lengthPattern = bestDateTimePattern.length(); 290 mHourWithTwoDigit = false; 291 char hourFormat = '\0'; 292 // Check if the returned pattern is single or double 'H', 'h', 'K', 'k'. We also save 293 // the hour format that we found. 294 for (int i = 0; i < lengthPattern; i++) { 295 final char c = bestDateTimePattern.charAt(i); 296 if (c == 'H' || c == 'h' || c == 'K' || c == 'k') { 297 mHourFormat = c; 298 if (i + 1 < lengthPattern && c == bestDateTimePattern.charAt(i + 1)) { 299 mHourWithTwoDigit = true; 300 } 301 break; 302 } 303 } 304 } 305 306 private boolean isAmPmAtStart() { 307 final Locale defaultLocale = Locale.getDefault(); 308 final String bestDateTimePattern = DateFormat.getBestDateTimePattern(defaultLocale, 309 "hm" /* skeleton */); 310 311 return bestDateTimePattern.startsWith("a"); 312 } 313 314 @Override 315 public void setEnabled(boolean enabled) { 316 if (mIsEnabled == enabled) { 317 return; 318 } 319 super.setEnabled(enabled); 320 mMinuteSpinner.setEnabled(enabled); 321 if (mDivider != null) { 322 mDivider.setEnabled(enabled); 323 } 324 mHourSpinner.setEnabled(enabled); 325 if (mAmPmSpinner != null) { 326 mAmPmSpinner.setEnabled(enabled); 327 } else { 328 mAmPmButton.setEnabled(enabled); 329 } 330 mIsEnabled = enabled; 331 } 332 333 @Override 334 public boolean isEnabled() { 335 return mIsEnabled; 336 } 337 338 @Override 339 protected void onConfigurationChanged(Configuration newConfig) { 340 super.onConfigurationChanged(newConfig); 341 setCurrentLocale(newConfig.locale); 342 } 343 344 /** 345 * Sets the current locale. 346 * 347 * @param locale The current locale. 348 */ 349 private void setCurrentLocale(Locale locale) { 350 if (locale.equals(mCurrentLocale)) { 351 return; 352 } 353 mCurrentLocale = locale; 354 mTempCalendar = Calendar.getInstance(locale); 355 } 356 357 /** 358 * Used to save / restore state of time picker 359 */ 360 private static class SavedState extends BaseSavedState { 361 362 private final int mHour; 363 364 private final int mMinute; 365 366 private SavedState(Parcelable superState, int hour, int minute) { 367 super(superState); 368 mHour = hour; 369 mMinute = minute; 370 } 371 372 private SavedState(Parcel in) { 373 super(in); 374 mHour = in.readInt(); 375 mMinute = in.readInt(); 376 } 377 378 public int getHour() { 379 return mHour; 380 } 381 382 public int getMinute() { 383 return mMinute; 384 } 385 386 @Override 387 public void writeToParcel(Parcel dest, int flags) { 388 super.writeToParcel(dest, flags); 389 dest.writeInt(mHour); 390 dest.writeInt(mMinute); 391 } 392 393 @SuppressWarnings({"unused", "hiding"}) 394 public static final Parcelable.Creator<SavedState> CREATOR = new Creator<SavedState>() { 395 public SavedState createFromParcel(Parcel in) { 396 return new SavedState(in); 397 } 398 399 public SavedState[] newArray(int size) { 400 return new SavedState[size]; 401 } 402 }; 403 } 404 405 @Override 406 protected Parcelable onSaveInstanceState() { 407 Parcelable superState = super.onSaveInstanceState(); 408 return new SavedState(superState, getCurrentHour(), getCurrentMinute()); 409 } 410 411 @Override 412 protected void onRestoreInstanceState(Parcelable state) { 413 SavedState ss = (SavedState) state; 414 super.onRestoreInstanceState(ss.getSuperState()); 415 setCurrentHour(ss.getHour()); 416 setCurrentMinute(ss.getMinute()); 417 } 418 419 /** 420 * Set the callback that indicates the time has been adjusted by the user. 421 * 422 * @param onTimeChangedListener the callback, should not be null. 423 */ 424 public void setOnTimeChangedListener(OnTimeChangedListener onTimeChangedListener) { 425 mOnTimeChangedListener = onTimeChangedListener; 426 } 427 428 /** 429 * @return The current hour in the range (0-23). 430 */ 431 public Integer getCurrentHour() { 432 int currentHour = mHourSpinner.getValue(); 433 if (is24HourView()) { 434 return currentHour; 435 } else if (mIsAm) { 436 return currentHour % HOURS_IN_HALF_DAY; 437 } else { 438 return (currentHour % HOURS_IN_HALF_DAY) + HOURS_IN_HALF_DAY; 439 } 440 } 441 442 /** 443 * Set the current hour. 444 */ 445 public void setCurrentHour(Integer currentHour) { 446 setCurrentHour(currentHour, true); 447 } 448 449 private void setCurrentHour(Integer currentHour, boolean notifyTimeChanged) { 450 // why was Integer used in the first place? 451 if (currentHour == null || currentHour == getCurrentHour()) { 452 return; 453 } 454 if (!is24HourView()) { 455 // convert [0,23] ordinal to wall clock display 456 if (currentHour >= HOURS_IN_HALF_DAY) { 457 mIsAm = false; 458 if (currentHour > HOURS_IN_HALF_DAY) { 459 currentHour = currentHour - HOURS_IN_HALF_DAY; 460 } 461 } else { 462 mIsAm = true; 463 if (currentHour == 0) { 464 currentHour = HOURS_IN_HALF_DAY; 465 } 466 } 467 updateAmPmControl(); 468 } 469 mHourSpinner.setValue(currentHour); 470 if (notifyTimeChanged) { 471 onTimeChanged(); 472 } 473 } 474 475 /** 476 * Set whether in 24 hour or AM/PM mode. 477 * 478 * @param is24HourView True = 24 hour mode. False = AM/PM. 479 */ 480 public void setIs24HourView(Boolean is24HourView) { 481 if (mIs24HourView == is24HourView) { 482 return; 483 } 484 // cache the current hour since spinner range changes and BEFORE changing mIs24HourView!! 485 int currentHour = getCurrentHour(); 486 // Order is important here. 487 mIs24HourView = is24HourView; 488 getHourFormatData(); 489 updateHourControl(); 490 // set value after spinner range is updated - be aware that because mIs24HourView has 491 // changed then getCurrentHour() is not equal to the currentHour we cached before so 492 // explicitly ask for *not* propagating any onTimeChanged() 493 setCurrentHour(currentHour, false /* no onTimeChanged() */); 494 updateMinuteControl(); 495 updateAmPmControl(); 496 } 497 498 /** 499 * @return true if this is in 24 hour view else false. 500 */ 501 public boolean is24HourView() { 502 return mIs24HourView; 503 } 504 505 /** 506 * @return The current minute. 507 */ 508 public Integer getCurrentMinute() { 509 return mMinuteSpinner.getValue(); 510 } 511 512 /** 513 * Set the current minute (0-59). 514 */ 515 public void setCurrentMinute(Integer currentMinute) { 516 if (currentMinute == getCurrentMinute()) { 517 return; 518 } 519 mMinuteSpinner.setValue(currentMinute); 520 onTimeChanged(); 521 } 522 523 /** 524 * The time separator is defined in the Unicode CLDR and cannot be supposed to be ":". 525 * 526 * See http://unicode.org/cldr/trac/browser/trunk/common/main 527 * 528 * We pass the correct "skeleton" depending on 12 or 24 hours view and then extract the 529 * separator as the character which is just after the hour marker in the returned pattern. 530 */ 531 private void setDividerText() { 532 final Locale defaultLocale = Locale.getDefault(); 533 final String skeleton = (mIs24HourView) ? "Hm" : "hm"; 534 final String bestDateTimePattern = DateFormat.getBestDateTimePattern(defaultLocale, 535 skeleton); 536 final String separatorText; 537 int hourIndex = bestDateTimePattern.lastIndexOf('H'); 538 if (hourIndex == -1) { 539 hourIndex = bestDateTimePattern.lastIndexOf('h'); 540 } 541 if (hourIndex == -1) { 542 // Default case 543 separatorText = ":"; 544 } else { 545 int minuteIndex = bestDateTimePattern.indexOf('m', hourIndex + 1); 546 if (minuteIndex == -1) { 547 separatorText = Character.toString(bestDateTimePattern.charAt(hourIndex + 1)); 548 } else { 549 separatorText = bestDateTimePattern.substring(hourIndex + 1, minuteIndex); 550 } 551 } 552 mDivider.setText(separatorText); 553 } 554 555 @Override 556 public int getBaseline() { 557 return mHourSpinner.getBaseline(); 558 } 559 560 @Override 561 public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) { 562 onPopulateAccessibilityEvent(event); 563 return true; 564 } 565 566 @Override 567 public void onPopulateAccessibilityEvent(AccessibilityEvent event) { 568 super.onPopulateAccessibilityEvent(event); 569 570 int flags = DateUtils.FORMAT_SHOW_TIME; 571 if (mIs24HourView) { 572 flags |= DateUtils.FORMAT_24HOUR; 573 } else { 574 flags |= DateUtils.FORMAT_12HOUR; 575 } 576 mTempCalendar.set(Calendar.HOUR_OF_DAY, getCurrentHour()); 577 mTempCalendar.set(Calendar.MINUTE, getCurrentMinute()); 578 String selectedDateUtterance = DateUtils.formatDateTime(mContext, 579 mTempCalendar.getTimeInMillis(), flags); 580 event.getText().add(selectedDateUtterance); 581 } 582 583 @Override 584 public void onInitializeAccessibilityEvent(AccessibilityEvent event) { 585 super.onInitializeAccessibilityEvent(event); 586 event.setClassName(TimePicker.class.getName()); 587 } 588 589 @Override 590 public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) { 591 super.onInitializeAccessibilityNodeInfo(info); 592 info.setClassName(TimePicker.class.getName()); 593 } 594 595 private void updateHourControl() { 596 if (is24HourView()) { 597 // 'k' means 1-24 hour 598 if (mHourFormat == 'k') { 599 mHourSpinner.setMinValue(1); 600 mHourSpinner.setMaxValue(24); 601 } else { 602 mHourSpinner.setMinValue(0); 603 mHourSpinner.setMaxValue(23); 604 } 605 } else { 606 // 'K' means 0-11 hour 607 if (mHourFormat == 'K') { 608 mHourSpinner.setMinValue(0); 609 mHourSpinner.setMaxValue(11); 610 } else { 611 mHourSpinner.setMinValue(1); 612 mHourSpinner.setMaxValue(12); 613 } 614 } 615 mHourSpinner.setFormatter(mHourWithTwoDigit ? NumberPicker.getTwoDigitFormatter() : null); 616 } 617 618 private void updateMinuteControl() { 619 if (is24HourView()) { 620 mMinuteSpinnerInput.setImeOptions(EditorInfo.IME_ACTION_DONE); 621 } else { 622 mMinuteSpinnerInput.setImeOptions(EditorInfo.IME_ACTION_NEXT); 623 } 624 } 625 626 private void updateAmPmControl() { 627 if (is24HourView()) { 628 if (mAmPmSpinner != null) { 629 mAmPmSpinner.setVisibility(View.GONE); 630 } else { 631 mAmPmButton.setVisibility(View.GONE); 632 } 633 } else { 634 int index = mIsAm ? Calendar.AM : Calendar.PM; 635 if (mAmPmSpinner != null) { 636 mAmPmSpinner.setValue(index); 637 mAmPmSpinner.setVisibility(View.VISIBLE); 638 } else { 639 mAmPmButton.setText(mAmPmStrings[index]); 640 mAmPmButton.setVisibility(View.VISIBLE); 641 } 642 } 643 sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_SELECTED); 644 } 645 646 private void onTimeChanged() { 647 sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_SELECTED); 648 if (mOnTimeChangedListener != null) { 649 mOnTimeChangedListener.onTimeChanged(this, getCurrentHour(), getCurrentMinute()); 650 } 651 } 652 653 private void setContentDescriptions() { 654 // Minute 655 trySetContentDescription(mMinuteSpinner, R.id.increment, 656 R.string.time_picker_increment_minute_button); 657 trySetContentDescription(mMinuteSpinner, R.id.decrement, 658 R.string.time_picker_decrement_minute_button); 659 // Hour 660 trySetContentDescription(mHourSpinner, R.id.increment, 661 R.string.time_picker_increment_hour_button); 662 trySetContentDescription(mHourSpinner, R.id.decrement, 663 R.string.time_picker_decrement_hour_button); 664 // AM/PM 665 if (mAmPmSpinner != null) { 666 trySetContentDescription(mAmPmSpinner, R.id.increment, 667 R.string.time_picker_increment_set_pm_button); 668 trySetContentDescription(mAmPmSpinner, R.id.decrement, 669 R.string.time_picker_decrement_set_am_button); 670 } 671 } 672 673 private void trySetContentDescription(View root, int viewId, int contDescResId) { 674 View target = root.findViewById(viewId); 675 if (target != null) { 676 target.setContentDescription(mContext.getString(contDescResId)); 677 } 678 } 679 680 private void updateInputState() { 681 // Make sure that if the user changes the value and the IME is active 682 // for one of the inputs if this widget, the IME is closed. If the user 683 // changed the value via the IME and there is a next input the IME will 684 // be shown, otherwise the user chose another means of changing the 685 // value and having the IME up makes no sense. 686 InputMethodManager inputMethodManager = InputMethodManager.peekInstance(); 687 if (inputMethodManager != null) { 688 if (inputMethodManager.isActive(mHourSpinnerInput)) { 689 mHourSpinnerInput.clearFocus(); 690 inputMethodManager.hideSoftInputFromWindow(getWindowToken(), 0); 691 } else if (inputMethodManager.isActive(mMinuteSpinnerInput)) { 692 mMinuteSpinnerInput.clearFocus(); 693 inputMethodManager.hideSoftInputFromWindow(getWindowToken(), 0); 694 } else if (inputMethodManager.isActive(mAmPmSpinnerInput)) { 695 mAmPmSpinnerInput.clearFocus(); 696 inputMethodManager.hideSoftInputFromWindow(getWindowToken(), 0); 697 } 698 } 699 } 700} 701