• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
17 package android.widget;
18 
19 import android.annotation.Widget;
20 import android.content.Context;
21 import android.content.res.Configuration;
22 import android.content.res.TypedArray;
23 import android.os.Parcel;
24 import android.os.Parcelable;
25 import android.text.format.DateFormat;
26 import android.text.format.DateUtils;
27 import android.util.AttributeSet;
28 import android.view.LayoutInflater;
29 import android.view.View;
30 import android.view.ViewGroup;
31 import android.view.accessibility.AccessibilityEvent;
32 import android.view.accessibility.AccessibilityNodeInfo;
33 import android.view.inputmethod.EditorInfo;
34 import android.view.inputmethod.InputMethodManager;
35 import android.widget.NumberPicker.OnValueChangeListener;
36 
37 import com.android.internal.R;
38 
39 import java.text.DateFormatSymbols;
40 import java.util.Calendar;
41 import 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
58 public 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          */
onTimeChanged(TimePicker view, int hourOfDay, int minute)123         void onTimeChanged(TimePicker view, int hourOfDay, int minute);
124     }
125 
TimePicker(Context context)126     public TimePicker(Context context) {
127         this(context, null);
128     }
129 
TimePicker(Context context, AttributeSet attrs)130     public TimePicker(Context context, AttributeSet attrs) {
131         this(context, attrs, R.attr.timePickerStyle);
132     }
133 
TimePicker(Context context, AttributeSet attrs, int defStyle)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 
getHourFormatData()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 
isAmPmAtStart()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
setEnabled(boolean enabled)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
isEnabled()334     public boolean isEnabled() {
335         return mIsEnabled;
336     }
337 
338     @Override
onConfigurationChanged(Configuration newConfig)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      */
setCurrentLocale(Locale locale)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 
SavedState(Parcelable superState, int hour, int minute)366         private SavedState(Parcelable superState, int hour, int minute) {
367             super(superState);
368             mHour = hour;
369             mMinute = minute;
370         }
371 
SavedState(Parcel in)372         private SavedState(Parcel in) {
373             super(in);
374             mHour = in.readInt();
375             mMinute = in.readInt();
376         }
377 
getHour()378         public int getHour() {
379             return mHour;
380         }
381 
getMinute()382         public int getMinute() {
383             return mMinute;
384         }
385 
386         @Override
writeToParcel(Parcel dest, int flags)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
onSaveInstanceState()406     protected Parcelable onSaveInstanceState() {
407         Parcelable superState = super.onSaveInstanceState();
408         return new SavedState(superState, getCurrentHour(), getCurrentMinute());
409     }
410 
411     @Override
onRestoreInstanceState(Parcelable state)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      */
setOnTimeChangedListener(OnTimeChangedListener onTimeChangedListener)424     public void setOnTimeChangedListener(OnTimeChangedListener onTimeChangedListener) {
425         mOnTimeChangedListener = onTimeChangedListener;
426     }
427 
428     /**
429      * @return The current hour in the range (0-23).
430      */
getCurrentHour()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      */
setCurrentHour(Integer currentHour)445     public void setCurrentHour(Integer currentHour) {
446         setCurrentHour(currentHour, true);
447     }
448 
setCurrentHour(Integer currentHour, boolean notifyTimeChanged)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      */
setIs24HourView(Boolean is24HourView)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      */
is24HourView()501     public boolean is24HourView() {
502         return mIs24HourView;
503     }
504 
505     /**
506      * @return The current minute.
507      */
getCurrentMinute()508     public Integer getCurrentMinute() {
509         return mMinuteSpinner.getValue();
510     }
511 
512     /**
513      * Set the current minute (0-59).
514      */
setCurrentMinute(Integer currentMinute)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      */
setDividerText()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
getBaseline()556     public int getBaseline() {
557         return mHourSpinner.getBaseline();
558     }
559 
560     @Override
dispatchPopulateAccessibilityEvent(AccessibilityEvent event)561     public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {
562         onPopulateAccessibilityEvent(event);
563         return true;
564     }
565 
566     @Override
onPopulateAccessibilityEvent(AccessibilityEvent event)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
onInitializeAccessibilityEvent(AccessibilityEvent event)584     public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
585         super.onInitializeAccessibilityEvent(event);
586         event.setClassName(TimePicker.class.getName());
587     }
588 
589     @Override
onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info)590     public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
591         super.onInitializeAccessibilityNodeInfo(info);
592         info.setClassName(TimePicker.class.getName());
593     }
594 
updateHourControl()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 
updateMinuteControl()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 
updateAmPmControl()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 
onTimeChanged()646     private void onTimeChanged() {
647         sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_SELECTED);
648         if (mOnTimeChangedListener != null) {
649             mOnTimeChangedListener.onTimeChanged(this, getCurrentHour(), getCurrentMinute());
650         }
651     }
652 
setContentDescriptions()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 
trySetContentDescription(View root, int viewId, int contDescResId)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 
updateInputState()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