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