blob: d8a3c5638b725cb02274b7ee20493e0a7561322f [file] [log] [blame]
Alan Viverette6b3f85f2016-03-01 16:48:04 -05001/*
2 * Copyright (C) 2016 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.content.Context;
20import android.content.res.Configuration;
21import android.content.res.TypedArray;
22import android.os.Parcelable;
23import android.text.InputType;
24import android.text.TextUtils;
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.accessibility.AccessibilityEvent;
31import android.view.inputmethod.EditorInfo;
32import android.view.inputmethod.InputMethodManager;
33import android.widget.DatePicker.AbstractDatePickerDelegate;
34import android.widget.NumberPicker.OnValueChangeListener;
35
36import java.text.DateFormatSymbols;
37import java.text.ParseException;
38import java.text.SimpleDateFormat;
39import java.util.Arrays;
40import java.util.Calendar;
41import java.util.Locale;
42
43import libcore.icu.ICU;
44
45/**
46 * A delegate implementing the basic DatePicker
47 */
48class DatePickerSpinnerDelegate extends AbstractDatePickerDelegate {
49
50 private static final String DATE_FORMAT = "MM/dd/yyyy";
51
52 private static final int DEFAULT_START_YEAR = 1900;
53
54 private static final int DEFAULT_END_YEAR = 2100;
55
56 private static final boolean DEFAULT_CALENDAR_VIEW_SHOWN = true;
57
58 private static final boolean DEFAULT_SPINNERS_SHOWN = true;
59
60 private static final boolean DEFAULT_ENABLED_STATE = true;
61
62 private final LinearLayout mSpinners;
63
64 private final NumberPicker mDaySpinner;
65
66 private final NumberPicker mMonthSpinner;
67
68 private final NumberPicker mYearSpinner;
69
70 private final EditText mDaySpinnerInput;
71
72 private final EditText mMonthSpinnerInput;
73
74 private final EditText mYearSpinnerInput;
75
76 private final CalendarView mCalendarView;
77
78 private String[] mShortMonths;
79
80 private final java.text.DateFormat mDateFormat = new SimpleDateFormat(DATE_FORMAT);
81
82 private int mNumberOfMonths;
83
84 private Calendar mTempDate;
85
86 private Calendar mMinDate;
87
88 private Calendar mMaxDate;
89
90 private Calendar mCurrentDate;
91
92 private boolean mIsEnabled = DEFAULT_ENABLED_STATE;
93
94 DatePickerSpinnerDelegate(DatePicker delegator, Context context, AttributeSet attrs,
95 int defStyleAttr, int defStyleRes) {
96 super(delegator, context);
97
98 mDelegator = delegator;
99 mContext = context;
100
101 // initialization based on locale
102 setCurrentLocale(Locale.getDefault());
103
104 final TypedArray attributesArray = context.obtainStyledAttributes(attrs,
105 com.android.internal.R.styleable.DatePicker, defStyleAttr, defStyleRes);
106 boolean spinnersShown = attributesArray.getBoolean(com.android.internal.R.styleable.DatePicker_spinnersShown,
107 DEFAULT_SPINNERS_SHOWN);
108 boolean calendarViewShown = attributesArray.getBoolean(
109 com.android.internal.R.styleable.DatePicker_calendarViewShown, DEFAULT_CALENDAR_VIEW_SHOWN);
110 int startYear = attributesArray.getInt(com.android.internal.R.styleable.DatePicker_startYear,
111 DEFAULT_START_YEAR);
112 int endYear = attributesArray.getInt(com.android.internal.R.styleable.DatePicker_endYear, DEFAULT_END_YEAR);
113 String minDate = attributesArray.getString(com.android.internal.R.styleable.DatePicker_minDate);
114 String maxDate = attributesArray.getString(com.android.internal.R.styleable.DatePicker_maxDate);
115 int layoutResourceId = attributesArray.getResourceId(
116 com.android.internal.R.styleable.DatePicker_legacyLayout, com.android.internal.R.layout.date_picker_legacy);
117 attributesArray.recycle();
118
119 LayoutInflater inflater = (LayoutInflater) context
120 .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
121 inflater.inflate(layoutResourceId, mDelegator, true);
122
123 OnValueChangeListener onChangeListener = new OnValueChangeListener() {
124 public void onValueChange(NumberPicker picker, int oldVal, int newVal) {
125 updateInputState();
126 mTempDate.setTimeInMillis(mCurrentDate.getTimeInMillis());
127 // take care of wrapping of days and months to update greater fields
128 if (picker == mDaySpinner) {
129 int maxDayOfMonth = mTempDate.getActualMaximum(Calendar.DAY_OF_MONTH);
130 if (oldVal == maxDayOfMonth && newVal == 1) {
131 mTempDate.add(Calendar.DAY_OF_MONTH, 1);
132 } else if (oldVal == 1 && newVal == maxDayOfMonth) {
133 mTempDate.add(Calendar.DAY_OF_MONTH, -1);
134 } else {
135 mTempDate.add(Calendar.DAY_OF_MONTH, newVal - oldVal);
136 }
137 } else if (picker == mMonthSpinner) {
138 if (oldVal == 11 && newVal == 0) {
139 mTempDate.add(Calendar.MONTH, 1);
140 } else if (oldVal == 0 && newVal == 11) {
141 mTempDate.add(Calendar.MONTH, -1);
142 } else {
143 mTempDate.add(Calendar.MONTH, newVal - oldVal);
144 }
145 } else if (picker == mYearSpinner) {
146 mTempDate.set(Calendar.YEAR, newVal);
147 } else {
148 throw new IllegalArgumentException();
149 }
150 // now set the date to the adjusted one
151 setDate(mTempDate.get(Calendar.YEAR), mTempDate.get(Calendar.MONTH),
152 mTempDate.get(Calendar.DAY_OF_MONTH));
153 updateSpinners();
154 updateCalendarView();
155 notifyDateChanged();
156 }
157 };
158
159 mSpinners = (LinearLayout) mDelegator.findViewById(com.android.internal.R.id.pickers);
160
161 // calendar view day-picker
162 mCalendarView = (CalendarView) mDelegator.findViewById(com.android.internal.R.id.calendar_view);
163 mCalendarView.setOnDateChangeListener(new CalendarView.OnDateChangeListener() {
164 public void onSelectedDayChange(CalendarView view, int year, int month, int monthDay) {
165 setDate(year, month, monthDay);
166 updateSpinners();
167 notifyDateChanged();
168 }
169 });
170
171 // day
172 mDaySpinner = (NumberPicker) mDelegator.findViewById(com.android.internal.R.id.day);
173 mDaySpinner.setFormatter(NumberPicker.getTwoDigitFormatter());
174 mDaySpinner.setOnLongPressUpdateInterval(100);
175 mDaySpinner.setOnValueChangedListener(onChangeListener);
176 mDaySpinnerInput = (EditText) mDaySpinner.findViewById(com.android.internal.R.id.numberpicker_input);
177
178 // month
179 mMonthSpinner = (NumberPicker) mDelegator.findViewById(com.android.internal.R.id.month);
180 mMonthSpinner.setMinValue(0);
181 mMonthSpinner.setMaxValue(mNumberOfMonths - 1);
182 mMonthSpinner.setDisplayedValues(mShortMonths);
183 mMonthSpinner.setOnLongPressUpdateInterval(200);
184 mMonthSpinner.setOnValueChangedListener(onChangeListener);
185 mMonthSpinnerInput = (EditText) mMonthSpinner.findViewById(com.android.internal.R.id.numberpicker_input);
186
187 // year
188 mYearSpinner = (NumberPicker) mDelegator.findViewById(com.android.internal.R.id.year);
189 mYearSpinner.setOnLongPressUpdateInterval(100);
190 mYearSpinner.setOnValueChangedListener(onChangeListener);
191 mYearSpinnerInput = (EditText) mYearSpinner.findViewById(com.android.internal.R.id.numberpicker_input);
192
193 // show only what the user required but make sure we
194 // show something and the spinners have higher priority
195 if (!spinnersShown && !calendarViewShown) {
196 setSpinnersShown(true);
197 } else {
198 setSpinnersShown(spinnersShown);
199 setCalendarViewShown(calendarViewShown);
200 }
201
202 // set the min date giving priority of the minDate over startYear
203 mTempDate.clear();
204 if (!TextUtils.isEmpty(minDate)) {
205 if (!parseDate(minDate, mTempDate)) {
206 mTempDate.set(startYear, 0, 1);
207 }
208 } else {
209 mTempDate.set(startYear, 0, 1);
210 }
211 setMinDate(mTempDate.getTimeInMillis());
212
213 // set the max date giving priority of the maxDate over endYear
214 mTempDate.clear();
215 if (!TextUtils.isEmpty(maxDate)) {
216 if (!parseDate(maxDate, mTempDate)) {
217 mTempDate.set(endYear, 11, 31);
218 }
219 } else {
220 mTempDate.set(endYear, 11, 31);
221 }
222 setMaxDate(mTempDate.getTimeInMillis());
223
224 // initialize to current date
225 mCurrentDate.setTimeInMillis(System.currentTimeMillis());
226 init(mCurrentDate.get(Calendar.YEAR), mCurrentDate.get(Calendar.MONTH), mCurrentDate
227 .get(Calendar.DAY_OF_MONTH), null);
228
229 // re-order the number spinners to match the current date format
230 reorderSpinners();
231
232 // accessibility
233 setContentDescriptions();
234
235 // If not explicitly specified this view is important for accessibility.
236 if (mDelegator.getImportantForAccessibility() == View.IMPORTANT_FOR_ACCESSIBILITY_AUTO) {
237 mDelegator.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES);
238 }
239 }
240
241 @Override
242 public void init(int year, int monthOfYear, int dayOfMonth,
243 DatePicker.OnDateChangedListener onDateChangedListener) {
244 setDate(year, monthOfYear, dayOfMonth);
245 updateSpinners();
246 updateCalendarView();
Alan Viverettea9a75f52016-04-01 13:22:10 -0400247
Alan Viverette6b3f85f2016-03-01 16:48:04 -0500248 mOnDateChangedListener = onDateChangedListener;
249 }
250
251 @Override
252 public void updateDate(int year, int month, int dayOfMonth) {
253 if (!isNewDate(year, month, dayOfMonth)) {
254 return;
255 }
256 setDate(year, month, dayOfMonth);
257 updateSpinners();
258 updateCalendarView();
259 notifyDateChanged();
260 }
261
262 @Override
263 public int getYear() {
264 return mCurrentDate.get(Calendar.YEAR);
265 }
266
267 @Override
268 public int getMonth() {
269 return mCurrentDate.get(Calendar.MONTH);
270 }
271
272 @Override
273 public int getDayOfMonth() {
274 return mCurrentDate.get(Calendar.DAY_OF_MONTH);
275 }
276
277 @Override
278 public void setFirstDayOfWeek(int firstDayOfWeek) {
279 mCalendarView.setFirstDayOfWeek(firstDayOfWeek);
280 }
281
282 @Override
283 public int getFirstDayOfWeek() {
284 return mCalendarView.getFirstDayOfWeek();
285 }
286
287 @Override
288 public void setMinDate(long minDate) {
289 mTempDate.setTimeInMillis(minDate);
290 if (mTempDate.get(Calendar.YEAR) == mMinDate.get(Calendar.YEAR)
291 && mTempDate.get(Calendar.DAY_OF_YEAR) != mMinDate.get(Calendar.DAY_OF_YEAR)) {
292 return;
293 }
294 mMinDate.setTimeInMillis(minDate);
295 mCalendarView.setMinDate(minDate);
296 if (mCurrentDate.before(mMinDate)) {
297 mCurrentDate.setTimeInMillis(mMinDate.getTimeInMillis());
298 updateCalendarView();
299 }
300 updateSpinners();
301 }
302
303 @Override
304 public Calendar getMinDate() {
305 final Calendar minDate = Calendar.getInstance();
306 minDate.setTimeInMillis(mCalendarView.getMinDate());
307 return minDate;
308 }
309
310 @Override
311 public void setMaxDate(long maxDate) {
312 mTempDate.setTimeInMillis(maxDate);
313 if (mTempDate.get(Calendar.YEAR) == mMaxDate.get(Calendar.YEAR)
314 && mTempDate.get(Calendar.DAY_OF_YEAR) != mMaxDate.get(Calendar.DAY_OF_YEAR)) {
315 return;
316 }
317 mMaxDate.setTimeInMillis(maxDate);
318 mCalendarView.setMaxDate(maxDate);
319 if (mCurrentDate.after(mMaxDate)) {
320 mCurrentDate.setTimeInMillis(mMaxDate.getTimeInMillis());
321 updateCalendarView();
322 }
323 updateSpinners();
324 }
325
326 @Override
327 public Calendar getMaxDate() {
328 final Calendar maxDate = Calendar.getInstance();
329 maxDate.setTimeInMillis(mCalendarView.getMaxDate());
330 return maxDate;
331 }
332
333 @Override
334 public void setEnabled(boolean enabled) {
335 mDaySpinner.setEnabled(enabled);
336 mMonthSpinner.setEnabled(enabled);
337 mYearSpinner.setEnabled(enabled);
338 mCalendarView.setEnabled(enabled);
339 mIsEnabled = enabled;
340 }
341
342 @Override
343 public boolean isEnabled() {
344 return mIsEnabled;
345 }
346
347 @Override
348 public CalendarView getCalendarView() {
349 return mCalendarView;
350 }
351
352 @Override
353 public void setCalendarViewShown(boolean shown) {
354 mCalendarView.setVisibility(shown ? View.VISIBLE : View.GONE);
355 }
356
357 @Override
358 public boolean getCalendarViewShown() {
359 return (mCalendarView.getVisibility() == View.VISIBLE);
360 }
361
362 @Override
363 public void setSpinnersShown(boolean shown) {
364 mSpinners.setVisibility(shown ? View.VISIBLE : View.GONE);
365 }
366
367 @Override
368 public boolean getSpinnersShown() {
369 return mSpinners.isShown();
370 }
371
372 @Override
373 public void onConfigurationChanged(Configuration newConfig) {
374 setCurrentLocale(newConfig.locale);
375 }
376
377 @Override
378 public Parcelable onSaveInstanceState(Parcelable superState) {
379 return new SavedState(superState, getYear(), getMonth(), getDayOfMonth(),
380 getMinDate().getTimeInMillis(), getMaxDate().getTimeInMillis());
381 }
382
383 @Override
384 public void onRestoreInstanceState(Parcelable state) {
385 if (state instanceof SavedState) {
386 final SavedState ss = (SavedState) state;
387 setDate(ss.getSelectedYear(), ss.getSelectedMonth(), ss.getSelectedDay());
388 updateSpinners();
389 updateCalendarView();
390 }
391 }
392
393 @Override
394 public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {
395 onPopulateAccessibilityEvent(event);
396 return true;
397 }
398
399 @Override
400 public void onPopulateAccessibilityEvent(AccessibilityEvent event) {
401 final int flags = DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_SHOW_YEAR;
402 String selectedDateUtterance = DateUtils.formatDateTime(mContext,
403 mCurrentDate.getTimeInMillis(), flags);
404 event.getText().add(selectedDateUtterance);
405 }
406
407 /**
408 * Sets the current locale.
409 *
410 * @param locale The current locale.
411 */
412 @Override
413 protected void setCurrentLocale(Locale locale) {
414 super.setCurrentLocale(locale);
415
416 mTempDate = getCalendarForLocale(mTempDate, locale);
417 mMinDate = getCalendarForLocale(mMinDate, locale);
418 mMaxDate = getCalendarForLocale(mMaxDate, locale);
419 mCurrentDate = getCalendarForLocale(mCurrentDate, locale);
420
421 mNumberOfMonths = mTempDate.getActualMaximum(Calendar.MONTH) + 1;
422 mShortMonths = new DateFormatSymbols().getShortMonths();
423
424 if (usingNumericMonths()) {
425 // We're in a locale where a date should either be all-numeric, or all-text.
426 // All-text would require custom NumberPicker formatters for day and year.
427 mShortMonths = new String[mNumberOfMonths];
428 for (int i = 0; i < mNumberOfMonths; ++i) {
429 mShortMonths[i] = String.format("%d", i + 1);
430 }
431 }
432 }
433
434 /**
435 * Tests whether the current locale is one where there are no real month names,
436 * such as Chinese, Japanese, or Korean locales.
437 */
438 private boolean usingNumericMonths() {
439 return Character.isDigit(mShortMonths[Calendar.JANUARY].charAt(0));
440 }
441
442 /**
443 * Gets a calendar for locale bootstrapped with the value of a given calendar.
444 *
445 * @param oldCalendar The old calendar.
446 * @param locale The locale.
447 */
448 private Calendar getCalendarForLocale(Calendar oldCalendar, Locale locale) {
449 if (oldCalendar == null) {
450 return Calendar.getInstance(locale);
451 } else {
452 final long currentTimeMillis = oldCalendar.getTimeInMillis();
453 Calendar newCalendar = Calendar.getInstance(locale);
454 newCalendar.setTimeInMillis(currentTimeMillis);
455 return newCalendar;
456 }
457 }
458
459 /**
460 * Reorders the spinners according to the date format that is
461 * explicitly set by the user and if no such is set fall back
462 * to the current locale's default format.
463 */
464 private void reorderSpinners() {
465 mSpinners.removeAllViews();
466 // We use numeric spinners for year and day, but textual months. Ask icu4c what
467 // order the user's locale uses for that combination. http://b/7207103.
468 String pattern = DateFormat.getBestDateTimePattern(Locale.getDefault(), "yyyyMMMdd");
469 char[] order = ICU.getDateFormatOrder(pattern);
470 final int spinnerCount = order.length;
471 for (int i = 0; i < spinnerCount; i++) {
472 switch (order[i]) {
473 case 'd':
474 mSpinners.addView(mDaySpinner);
475 setImeOptions(mDaySpinner, spinnerCount, i);
476 break;
477 case 'M':
478 mSpinners.addView(mMonthSpinner);
479 setImeOptions(mMonthSpinner, spinnerCount, i);
480 break;
481 case 'y':
482 mSpinners.addView(mYearSpinner);
483 setImeOptions(mYearSpinner, spinnerCount, i);
484 break;
485 default:
486 throw new IllegalArgumentException(Arrays.toString(order));
487 }
488 }
489 }
490
491 /**
492 * Parses the given <code>date</code> and in case of success sets the result
493 * to the <code>outDate</code>.
494 *
495 * @return True if the date was parsed.
496 */
497 private boolean parseDate(String date, Calendar outDate) {
498 try {
499 outDate.setTime(mDateFormat.parse(date));
500 return true;
501 } catch (ParseException e) {
502 e.printStackTrace();
503 return false;
504 }
505 }
506
507 private boolean isNewDate(int year, int month, int dayOfMonth) {
508 return (mCurrentDate.get(Calendar.YEAR) != year
509 || mCurrentDate.get(Calendar.MONTH) != month
510 || mCurrentDate.get(Calendar.DAY_OF_MONTH) != dayOfMonth);
511 }
512
513 private void setDate(int year, int month, int dayOfMonth) {
514 mCurrentDate.set(year, month, dayOfMonth);
515 if (mCurrentDate.before(mMinDate)) {
516 mCurrentDate.setTimeInMillis(mMinDate.getTimeInMillis());
517 } else if (mCurrentDate.after(mMaxDate)) {
518 mCurrentDate.setTimeInMillis(mMaxDate.getTimeInMillis());
519 }
520 }
521
522 private void updateSpinners() {
523 // set the spinner ranges respecting the min and max dates
524 if (mCurrentDate.equals(mMinDate)) {
525 mDaySpinner.setMinValue(mCurrentDate.get(Calendar.DAY_OF_MONTH));
526 mDaySpinner.setMaxValue(mCurrentDate.getActualMaximum(Calendar.DAY_OF_MONTH));
527 mDaySpinner.setWrapSelectorWheel(false);
528 mMonthSpinner.setDisplayedValues(null);
529 mMonthSpinner.setMinValue(mCurrentDate.get(Calendar.MONTH));
530 mMonthSpinner.setMaxValue(mCurrentDate.getActualMaximum(Calendar.MONTH));
531 mMonthSpinner.setWrapSelectorWheel(false);
532 } else if (mCurrentDate.equals(mMaxDate)) {
533 mDaySpinner.setMinValue(mCurrentDate.getActualMinimum(Calendar.DAY_OF_MONTH));
534 mDaySpinner.setMaxValue(mCurrentDate.get(Calendar.DAY_OF_MONTH));
535 mDaySpinner.setWrapSelectorWheel(false);
536 mMonthSpinner.setDisplayedValues(null);
537 mMonthSpinner.setMinValue(mCurrentDate.getActualMinimum(Calendar.MONTH));
538 mMonthSpinner.setMaxValue(mCurrentDate.get(Calendar.MONTH));
539 mMonthSpinner.setWrapSelectorWheel(false);
540 } else {
541 mDaySpinner.setMinValue(1);
542 mDaySpinner.setMaxValue(mCurrentDate.getActualMaximum(Calendar.DAY_OF_MONTH));
543 mDaySpinner.setWrapSelectorWheel(true);
544 mMonthSpinner.setDisplayedValues(null);
545 mMonthSpinner.setMinValue(0);
546 mMonthSpinner.setMaxValue(11);
547 mMonthSpinner.setWrapSelectorWheel(true);
548 }
549
550 // make sure the month names are a zero based array
551 // with the months in the month spinner
552 String[] displayedValues = Arrays.copyOfRange(mShortMonths,
553 mMonthSpinner.getMinValue(), mMonthSpinner.getMaxValue() + 1);
554 mMonthSpinner.setDisplayedValues(displayedValues);
555
556 // year spinner range does not change based on the current date
557 mYearSpinner.setMinValue(mMinDate.get(Calendar.YEAR));
558 mYearSpinner.setMaxValue(mMaxDate.get(Calendar.YEAR));
559 mYearSpinner.setWrapSelectorWheel(false);
560
561 // set the spinner values
562 mYearSpinner.setValue(mCurrentDate.get(Calendar.YEAR));
563 mMonthSpinner.setValue(mCurrentDate.get(Calendar.MONTH));
564 mDaySpinner.setValue(mCurrentDate.get(Calendar.DAY_OF_MONTH));
565
566 if (usingNumericMonths()) {
567 mMonthSpinnerInput.setRawInputType(InputType.TYPE_CLASS_NUMBER);
568 }
569 }
570
571 /**
572 * Updates the calendar view with the current date.
573 */
574 private void updateCalendarView() {
575 mCalendarView.setDate(mCurrentDate.getTimeInMillis(), false, false);
576 }
577
578
579 /**
580 * Notifies the listener, if such, for a change in the selected date.
581 */
582 private void notifyDateChanged() {
583 mDelegator.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_SELECTED);
584 if (mOnDateChangedListener != null) {
585 mOnDateChangedListener.onDateChanged(mDelegator, getYear(), getMonth(),
586 getDayOfMonth());
587 }
588 }
589
590 /**
591 * Sets the IME options for a spinner based on its ordering.
592 *
593 * @param spinner The spinner.
594 * @param spinnerCount The total spinner count.
595 * @param spinnerIndex The index of the given spinner.
596 */
597 private void setImeOptions(NumberPicker spinner, int spinnerCount, int spinnerIndex) {
598 final int imeOptions;
599 if (spinnerIndex < spinnerCount - 1) {
600 imeOptions = EditorInfo.IME_ACTION_NEXT;
601 } else {
602 imeOptions = EditorInfo.IME_ACTION_DONE;
603 }
604 TextView input = (TextView) spinner.findViewById(com.android.internal.R.id.numberpicker_input);
605 input.setImeOptions(imeOptions);
606 }
607
608 private void setContentDescriptions() {
609 // Day
610 trySetContentDescription(mDaySpinner, com.android.internal.R.id.increment,
611 com.android.internal.R.string.date_picker_increment_day_button);
612 trySetContentDescription(mDaySpinner, com.android.internal.R.id.decrement,
613 com.android.internal.R.string.date_picker_decrement_day_button);
614 // Month
615 trySetContentDescription(mMonthSpinner, com.android.internal.R.id.increment,
616 com.android.internal.R.string.date_picker_increment_month_button);
617 trySetContentDescription(mMonthSpinner, com.android.internal.R.id.decrement,
618 com.android.internal.R.string.date_picker_decrement_month_button);
619 // Year
620 trySetContentDescription(mYearSpinner, com.android.internal.R.id.increment,
621 com.android.internal.R.string.date_picker_increment_year_button);
622 trySetContentDescription(mYearSpinner, com.android.internal.R.id.decrement,
623 com.android.internal.R.string.date_picker_decrement_year_button);
624 }
625
626 private void trySetContentDescription(View root, int viewId, int contDescResId) {
627 View target = root.findViewById(viewId);
628 if (target != null) {
629 target.setContentDescription(mContext.getString(contDescResId));
630 }
631 }
632
633 private void updateInputState() {
634 // Make sure that if the user changes the value and the IME is active
635 // for one of the inputs if this widget, the IME is closed. If the user
636 // changed the value via the IME and there is a next input the IME will
637 // be shown, otherwise the user chose another means of changing the
638 // value and having the IME up makes no sense.
639 InputMethodManager inputMethodManager = InputMethodManager.peekInstance();
640 if (inputMethodManager != null) {
641 if (inputMethodManager.isActive(mYearSpinnerInput)) {
642 mYearSpinnerInput.clearFocus();
643 inputMethodManager.hideSoftInputFromWindow(mDelegator.getWindowToken(), 0);
644 } else if (inputMethodManager.isActive(mMonthSpinnerInput)) {
645 mMonthSpinnerInput.clearFocus();
646 inputMethodManager.hideSoftInputFromWindow(mDelegator.getWindowToken(), 0);
647 } else if (inputMethodManager.isActive(mDaySpinnerInput)) {
648 mDaySpinnerInput.clearFocus();
649 inputMethodManager.hideSoftInputFromWindow(mDelegator.getWindowToken(), 0);
650 }
651 }
652 }
653}