Home | History | Annotate | Download | only in widget
      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          */
    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