• 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.IntDef;
20 import android.annotation.Nullable;
21 import android.annotation.TestApi;
22 import android.annotation.Widget;
23 import android.content.Context;
24 import android.content.res.Configuration;
25 import android.content.res.TypedArray;
26 import android.icu.util.Calendar;
27 import android.icu.util.TimeZone;
28 import android.os.Parcel;
29 import android.os.Parcelable;
30 import android.text.format.DateUtils;
31 import android.util.AttributeSet;
32 import android.util.Log;
33 import android.util.SparseArray;
34 import android.view.View;
35 import android.view.ViewStructure;
36 import android.view.accessibility.AccessibilityEvent;
37 import android.view.autofill.AutofillManager;
38 import android.view.autofill.AutofillValue;
39 
40 import com.android.internal.R;
41 
42 import java.lang.annotation.Retention;
43 import java.lang.annotation.RetentionPolicy;
44 import java.util.Locale;
45 
46 /**
47  * Provides a widget for selecting a date.
48  * <p>
49  * When the {@link android.R.styleable#DatePicker_datePickerMode} attribute is
50  * set to {@code spinner}, the date can be selected using year, month, and day
51  * spinners or a {@link CalendarView}. The set of spinners and the calendar
52  * view are automatically synchronized. The client can customize whether only
53  * the spinners, or only the calendar view, or both to be displayed.
54  * </p>
55  * <p>
56  * When the {@link android.R.styleable#DatePicker_datePickerMode} attribute is
57  * set to {@code calendar}, the month and day can be selected using a
58  * calendar-style view while the year can be selected separately using a list.
59  * </p>
60  * <p>
61  * See the <a href="{@docRoot}guide/topics/ui/controls/pickers.html">Pickers</a>
62  * guide.
63  * </p>
64  * <p>
65  * For a dialog using this view, see {@link android.app.DatePickerDialog}.
66  * </p>
67  *
68  * @attr ref android.R.styleable#DatePicker_startYear
69  * @attr ref android.R.styleable#DatePicker_endYear
70  * @attr ref android.R.styleable#DatePicker_maxDate
71  * @attr ref android.R.styleable#DatePicker_minDate
72  * @attr ref android.R.styleable#DatePicker_spinnersShown
73  * @attr ref android.R.styleable#DatePicker_calendarViewShown
74  * @attr ref android.R.styleable#DatePicker_dayOfWeekBackground
75  * @attr ref android.R.styleable#DatePicker_dayOfWeekTextAppearance
76  * @attr ref android.R.styleable#DatePicker_headerBackground
77  * @attr ref android.R.styleable#DatePicker_headerMonthTextAppearance
78  * @attr ref android.R.styleable#DatePicker_headerDayOfMonthTextAppearance
79  * @attr ref android.R.styleable#DatePicker_headerYearTextAppearance
80  * @attr ref android.R.styleable#DatePicker_yearListItemTextAppearance
81  * @attr ref android.R.styleable#DatePicker_yearListSelectorColor
82  * @attr ref android.R.styleable#DatePicker_calendarTextColor
83  * @attr ref android.R.styleable#DatePicker_datePickerMode
84  */
85 @Widget
86 public class DatePicker extends FrameLayout {
87     private static final String LOG_TAG = DatePicker.class.getSimpleName();
88 
89     /**
90      * Presentation mode for the Holo-style date picker that uses a set of
91      * {@link android.widget.NumberPicker}s.
92      *
93      * @see #getMode()
94      * @hide Visible for testing only.
95      */
96     @TestApi
97     public static final int MODE_SPINNER = 1;
98 
99     /**
100      * Presentation mode for the Material-style date picker that uses a
101      * calendar.
102      *
103      * @see #getMode()
104      * @hide Visible for testing only.
105      */
106     @TestApi
107     public static final int MODE_CALENDAR = 2;
108 
109     /** @hide */
110     @IntDef(prefix = { "MODE_" }, value = {
111             MODE_SPINNER,
112             MODE_CALENDAR
113     })
114     @Retention(RetentionPolicy.SOURCE)
115     public @interface DatePickerMode {}
116 
117     private final DatePickerDelegate mDelegate;
118 
119     @DatePickerMode
120     private final int mMode;
121 
122     /**
123      * The callback used to indicate the user changed the date.
124      */
125     public interface OnDateChangedListener {
126 
127         /**
128          * Called upon a date change.
129          *
130          * @param view The view associated with this listener.
131          * @param year The year that was set.
132          * @param monthOfYear The month that was set (0-11) for compatibility
133          *            with {@link java.util.Calendar}.
134          * @param dayOfMonth The day of the month that was set.
135          */
onDateChanged(DatePicker view, int year, int monthOfYear, int dayOfMonth)136         void onDateChanged(DatePicker view, int year, int monthOfYear, int dayOfMonth);
137     }
138 
DatePicker(Context context)139     public DatePicker(Context context) {
140         this(context, null);
141     }
142 
DatePicker(Context context, AttributeSet attrs)143     public DatePicker(Context context, AttributeSet attrs) {
144         this(context, attrs, R.attr.datePickerStyle);
145     }
146 
DatePicker(Context context, AttributeSet attrs, int defStyleAttr)147     public DatePicker(Context context, AttributeSet attrs, int defStyleAttr) {
148         this(context, attrs, defStyleAttr, 0);
149     }
150 
DatePicker(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes)151     public DatePicker(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
152         super(context, attrs, defStyleAttr, defStyleRes);
153 
154         // DatePicker is important by default, unless app developer overrode attribute.
155         if (getImportantForAutofill() == IMPORTANT_FOR_AUTOFILL_AUTO) {
156             setImportantForAutofill(IMPORTANT_FOR_AUTOFILL_YES);
157         }
158 
159         final TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.DatePicker,
160                 defStyleAttr, defStyleRes);
161         final boolean isDialogMode = a.getBoolean(R.styleable.DatePicker_dialogMode, false);
162         final int requestedMode = a.getInt(R.styleable.DatePicker_datePickerMode, MODE_SPINNER);
163         final int firstDayOfWeek = a.getInt(R.styleable.DatePicker_firstDayOfWeek, 0);
164         a.recycle();
165 
166         if (requestedMode == MODE_CALENDAR && isDialogMode) {
167             // You want MODE_CALENDAR? YOU CAN'T HANDLE MODE_CALENDAR! Well,
168             // maybe you can depending on your screen size. Let's check...
169             mMode = context.getResources().getInteger(R.integer.date_picker_mode);
170         } else {
171             mMode = requestedMode;
172         }
173 
174         switch (mMode) {
175             case MODE_CALENDAR:
176                 mDelegate = createCalendarUIDelegate(context, attrs, defStyleAttr, defStyleRes);
177                 break;
178             case MODE_SPINNER:
179             default:
180                 mDelegate = createSpinnerUIDelegate(context, attrs, defStyleAttr, defStyleRes);
181                 break;
182         }
183 
184         if (firstDayOfWeek != 0) {
185             setFirstDayOfWeek(firstDayOfWeek);
186         }
187 
188         mDelegate.setAutoFillChangeListener((v, y, m, d) -> {
189             final AutofillManager afm = context.getSystemService(AutofillManager.class);
190             if (afm != null) {
191                 afm.notifyValueChanged(this);
192             }
193         });
194     }
195 
createSpinnerUIDelegate(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes)196     private DatePickerDelegate createSpinnerUIDelegate(Context context, AttributeSet attrs,
197             int defStyleAttr, int defStyleRes) {
198         return new DatePickerSpinnerDelegate(this, context, attrs, defStyleAttr, defStyleRes);
199     }
200 
createCalendarUIDelegate(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes)201     private DatePickerDelegate createCalendarUIDelegate(Context context, AttributeSet attrs,
202             int defStyleAttr, int defStyleRes) {
203         return new DatePickerCalendarDelegate(this, context, attrs, defStyleAttr,
204                 defStyleRes);
205     }
206 
207     /**
208      * @return the picker's presentation mode, one of {@link #MODE_CALENDAR} or
209      *         {@link #MODE_SPINNER}
210      * @attr ref android.R.styleable#DatePicker_datePickerMode
211      * @hide Visible for testing only.
212      */
213     @DatePickerMode
214     @TestApi
getMode()215     public int getMode() {
216         return mMode;
217     }
218 
219     /**
220      * Initialize the state. If the provided values designate an inconsistent
221      * date the values are normalized before updating the spinners.
222      *
223      * @param year The initial year.
224      * @param monthOfYear The initial month <strong>starting from zero</strong>.
225      * @param dayOfMonth The initial day of the month.
226      * @param onDateChangedListener How user is notified date is changed by
227      *            user, can be null.
228      */
init(int year, int monthOfYear, int dayOfMonth, OnDateChangedListener onDateChangedListener)229     public void init(int year, int monthOfYear, int dayOfMonth,
230                      OnDateChangedListener onDateChangedListener) {
231         mDelegate.init(year, monthOfYear, dayOfMonth, onDateChangedListener);
232     }
233 
234     /**
235      * Set the callback that indicates the date has been adjusted by the user.
236      *
237      * @param onDateChangedListener How user is notified date is changed by
238      *            user, can be null.
239      */
setOnDateChangedListener(OnDateChangedListener onDateChangedListener)240     public void setOnDateChangedListener(OnDateChangedListener onDateChangedListener) {
241         mDelegate.setOnDateChangedListener(onDateChangedListener);
242     }
243 
244     /**
245      * Update the current date.
246      *
247      * @param year The year.
248      * @param month The month which is <strong>starting from zero</strong>.
249      * @param dayOfMonth The day of the month.
250      */
updateDate(int year, int month, int dayOfMonth)251     public void updateDate(int year, int month, int dayOfMonth) {
252         mDelegate.updateDate(year, month, dayOfMonth);
253     }
254 
255     /**
256      * @return The selected year.
257      */
getYear()258     public int getYear() {
259         return mDelegate.getYear();
260     }
261 
262     /**
263      * @return The selected month.
264      */
getMonth()265     public int getMonth() {
266         return mDelegate.getMonth();
267     }
268 
269     /**
270      * @return The selected day of month.
271      */
getDayOfMonth()272     public int getDayOfMonth() {
273         return mDelegate.getDayOfMonth();
274     }
275 
276     /**
277      * Gets the minimal date supported by this {@link DatePicker} in
278      * milliseconds since January 1, 1970 00:00:00 in
279      * {@link TimeZone#getDefault()} time zone.
280      * <p>
281      * Note: The default minimal date is 01/01/1900.
282      * <p>
283      *
284      * @return The minimal supported date.
285      */
getMinDate()286     public long getMinDate() {
287         return mDelegate.getMinDate().getTimeInMillis();
288     }
289 
290     /**
291      * Sets the minimal date supported by this {@link NumberPicker} in
292      * milliseconds since January 1, 1970 00:00:00 in
293      * {@link TimeZone#getDefault()} time zone.
294      *
295      * @param minDate The minimal supported date.
296      */
setMinDate(long minDate)297     public void setMinDate(long minDate) {
298         mDelegate.setMinDate(minDate);
299     }
300 
301     /**
302      * Gets the maximal date supported by this {@link DatePicker} in
303      * milliseconds since January 1, 1970 00:00:00 in
304      * {@link TimeZone#getDefault()} time zone.
305      * <p>
306      * Note: The default maximal date is 12/31/2100.
307      * <p>
308      *
309      * @return The maximal supported date.
310      */
getMaxDate()311     public long getMaxDate() {
312         return mDelegate.getMaxDate().getTimeInMillis();
313     }
314 
315     /**
316      * Sets the maximal date supported by this {@link DatePicker} in
317      * milliseconds since January 1, 1970 00:00:00 in
318      * {@link TimeZone#getDefault()} time zone.
319      *
320      * @param maxDate The maximal supported date.
321      */
setMaxDate(long maxDate)322     public void setMaxDate(long maxDate) {
323         mDelegate.setMaxDate(maxDate);
324     }
325 
326     /**
327      * Sets the callback that indicates the current date is valid.
328      *
329      * @param callback the callback, may be null
330      * @hide
331      */
setValidationCallback(@ullable ValidationCallback callback)332     public void setValidationCallback(@Nullable ValidationCallback callback) {
333         mDelegate.setValidationCallback(callback);
334     }
335 
336     @Override
setEnabled(boolean enabled)337     public void setEnabled(boolean enabled) {
338         if (mDelegate.isEnabled() == enabled) {
339             return;
340         }
341         super.setEnabled(enabled);
342         mDelegate.setEnabled(enabled);
343     }
344 
345     @Override
isEnabled()346     public boolean isEnabled() {
347         return mDelegate.isEnabled();
348     }
349 
350     /** @hide */
351     @Override
dispatchPopulateAccessibilityEventInternal(AccessibilityEvent event)352     public boolean dispatchPopulateAccessibilityEventInternal(AccessibilityEvent event) {
353         return mDelegate.dispatchPopulateAccessibilityEvent(event);
354     }
355 
356     /** @hide */
357     @Override
onPopulateAccessibilityEventInternal(AccessibilityEvent event)358     public void onPopulateAccessibilityEventInternal(AccessibilityEvent event) {
359         super.onPopulateAccessibilityEventInternal(event);
360         mDelegate.onPopulateAccessibilityEvent(event);
361     }
362 
363     @Override
getAccessibilityClassName()364     public CharSequence getAccessibilityClassName() {
365         return DatePicker.class.getName();
366     }
367 
368     @Override
onConfigurationChanged(Configuration newConfig)369     protected void onConfigurationChanged(Configuration newConfig) {
370         super.onConfigurationChanged(newConfig);
371         mDelegate.onConfigurationChanged(newConfig);
372     }
373 
374     /**
375      * Sets the first day of week.
376      *
377      * @param firstDayOfWeek The first day of the week conforming to the
378      *            {@link CalendarView} APIs.
379      * @see Calendar#SUNDAY
380      * @see Calendar#MONDAY
381      * @see Calendar#TUESDAY
382      * @see Calendar#WEDNESDAY
383      * @see Calendar#THURSDAY
384      * @see Calendar#FRIDAY
385      * @see Calendar#SATURDAY
386      *
387      * @attr ref android.R.styleable#DatePicker_firstDayOfWeek
388      */
setFirstDayOfWeek(int firstDayOfWeek)389     public void setFirstDayOfWeek(int firstDayOfWeek) {
390         if (firstDayOfWeek < Calendar.SUNDAY || firstDayOfWeek > Calendar.SATURDAY) {
391             throw new IllegalArgumentException("firstDayOfWeek must be between 1 and 7");
392         }
393         mDelegate.setFirstDayOfWeek(firstDayOfWeek);
394     }
395 
396     /**
397      * Gets the first day of week.
398      *
399      * @return The first day of the week conforming to the {@link CalendarView}
400      *         APIs.
401      * @see Calendar#SUNDAY
402      * @see Calendar#MONDAY
403      * @see Calendar#TUESDAY
404      * @see Calendar#WEDNESDAY
405      * @see Calendar#THURSDAY
406      * @see Calendar#FRIDAY
407      * @see Calendar#SATURDAY
408      *
409      * @attr ref android.R.styleable#DatePicker_firstDayOfWeek
410      */
getFirstDayOfWeek()411     public int getFirstDayOfWeek() {
412         return mDelegate.getFirstDayOfWeek();
413     }
414 
415     /**
416      * Returns whether the {@link CalendarView} is shown.
417      * <p>
418      * <strong>Note:</strong> This method returns {@code false} when the
419      * {@link android.R.styleable#DatePicker_datePickerMode} attribute is set
420      * to {@code calendar}.
421      *
422      * @return {@code true} if the calendar view is shown
423      * @see #getCalendarView()
424      * @deprecated Not supported by Material-style {@code calendar} mode
425      */
426     @Deprecated
getCalendarViewShown()427     public boolean getCalendarViewShown() {
428         return mDelegate.getCalendarViewShown();
429     }
430 
431     /**
432      * Returns the {@link CalendarView} used by this picker.
433      * <p>
434      * <strong>Note:</strong> This method throws an
435      * {@link UnsupportedOperationException} when the
436      * {@link android.R.styleable#DatePicker_datePickerMode} attribute is set
437      * to {@code calendar}.
438      *
439      * @return the calendar view
440      * @see #getCalendarViewShown()
441      * @deprecated Not supported by Material-style {@code calendar} mode
442      * @throws UnsupportedOperationException if called when the picker is
443      *         displayed in {@code calendar} mode
444      */
445     @Deprecated
getCalendarView()446     public CalendarView getCalendarView() {
447         return mDelegate.getCalendarView();
448     }
449 
450     /**
451      * Sets whether the {@link CalendarView} is shown.
452      * <p>
453      * <strong>Note:</strong> Calling this method has no effect when the
454      * {@link android.R.styleable#DatePicker_datePickerMode} attribute is set
455      * to {@code calendar}.
456      *
457      * @param shown {@code true} to show the calendar view, {@code false} to
458      *              hide it
459      * @deprecated Not supported by Material-style {@code calendar} mode
460      */
461     @Deprecated
setCalendarViewShown(boolean shown)462     public void setCalendarViewShown(boolean shown) {
463         mDelegate.setCalendarViewShown(shown);
464     }
465 
466     /**
467      * Returns whether the spinners are shown.
468      * <p>
469      * <strong>Note:</strong> his method returns {@code false} when the
470      * {@link android.R.styleable#DatePicker_datePickerMode} attribute is set
471      * to {@code calendar}.
472      *
473      * @return {@code true} if the spinners are shown
474      * @deprecated Not supported by Material-style {@code calendar} mode
475      */
476     @Deprecated
getSpinnersShown()477     public boolean getSpinnersShown() {
478         return mDelegate.getSpinnersShown();
479     }
480 
481     /**
482      * Sets whether the spinners are shown.
483      * <p>
484      * Calling this method has no effect when the
485      * {@link android.R.styleable#DatePicker_datePickerMode} attribute is set
486      * to {@code calendar}.
487      *
488      * @param shown {@code true} to show the spinners, {@code false} to hide
489      *              them
490      * @deprecated Not supported by Material-style {@code calendar} mode
491      */
492     @Deprecated
setSpinnersShown(boolean shown)493     public void setSpinnersShown(boolean shown) {
494         mDelegate.setSpinnersShown(shown);
495     }
496 
497     @Override
dispatchRestoreInstanceState(SparseArray<Parcelable> container)498     protected void dispatchRestoreInstanceState(SparseArray<Parcelable> container) {
499         dispatchThawSelfOnly(container);
500     }
501 
502     @Override
onSaveInstanceState()503     protected Parcelable onSaveInstanceState() {
504         Parcelable superState = super.onSaveInstanceState();
505         return mDelegate.onSaveInstanceState(superState);
506     }
507 
508     @Override
onRestoreInstanceState(Parcelable state)509     protected void onRestoreInstanceState(Parcelable state) {
510         BaseSavedState ss = (BaseSavedState) state;
511         super.onRestoreInstanceState(ss.getSuperState());
512         mDelegate.onRestoreInstanceState(ss);
513     }
514 
515     /**
516      * A delegate interface that defined the public API of the DatePicker. Allows different
517      * DatePicker implementations. This would need to be implemented by the DatePicker delegates
518      * for the real behavior.
519      *
520      * @hide
521      */
522     interface DatePickerDelegate {
init(int year, int monthOfYear, int dayOfMonth, OnDateChangedListener onDateChangedListener)523         void init(int year, int monthOfYear, int dayOfMonth,
524                   OnDateChangedListener onDateChangedListener);
525 
setOnDateChangedListener(OnDateChangedListener onDateChangedListener)526         void setOnDateChangedListener(OnDateChangedListener onDateChangedListener);
setAutoFillChangeListener(OnDateChangedListener onDateChangedListener)527         void setAutoFillChangeListener(OnDateChangedListener onDateChangedListener);
528 
updateDate(int year, int month, int dayOfMonth)529         void updateDate(int year, int month, int dayOfMonth);
530 
getYear()531         int getYear();
getMonth()532         int getMonth();
getDayOfMonth()533         int getDayOfMonth();
534 
autofill(AutofillValue value)535         void autofill(AutofillValue value);
getAutofillValue()536         AutofillValue getAutofillValue();
537 
setFirstDayOfWeek(int firstDayOfWeek)538         void setFirstDayOfWeek(int firstDayOfWeek);
getFirstDayOfWeek()539         int getFirstDayOfWeek();
540 
setMinDate(long minDate)541         void setMinDate(long minDate);
getMinDate()542         Calendar getMinDate();
543 
setMaxDate(long maxDate)544         void setMaxDate(long maxDate);
getMaxDate()545         Calendar getMaxDate();
546 
setEnabled(boolean enabled)547         void setEnabled(boolean enabled);
isEnabled()548         boolean isEnabled();
549 
getCalendarView()550         CalendarView getCalendarView();
551 
setCalendarViewShown(boolean shown)552         void setCalendarViewShown(boolean shown);
getCalendarViewShown()553         boolean getCalendarViewShown();
554 
setSpinnersShown(boolean shown)555         void setSpinnersShown(boolean shown);
getSpinnersShown()556         boolean getSpinnersShown();
557 
setValidationCallback(ValidationCallback callback)558         void setValidationCallback(ValidationCallback callback);
559 
onConfigurationChanged(Configuration newConfig)560         void onConfigurationChanged(Configuration newConfig);
561 
onSaveInstanceState(Parcelable superState)562         Parcelable onSaveInstanceState(Parcelable superState);
onRestoreInstanceState(Parcelable state)563         void onRestoreInstanceState(Parcelable state);
564 
dispatchPopulateAccessibilityEvent(AccessibilityEvent event)565         boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event);
onPopulateAccessibilityEvent(AccessibilityEvent event)566         void onPopulateAccessibilityEvent(AccessibilityEvent event);
567     }
568 
569     /**
570      * An abstract class which can be used as a start for DatePicker implementations
571      */
572     abstract static class AbstractDatePickerDelegate implements DatePickerDelegate {
573         // The delegator
574         protected DatePicker mDelegator;
575 
576         // The context
577         protected Context mContext;
578 
579         // NOTE: when subclasses change this variable, they must call resetAutofilledValue().
580         protected Calendar mCurrentDate;
581 
582         // The current locale
583         protected Locale mCurrentLocale;
584 
585         // Callbacks
586         protected OnDateChangedListener mOnDateChangedListener;
587         protected OnDateChangedListener mAutoFillChangeListener;
588         protected ValidationCallback mValidationCallback;
589 
590         // The value that was passed to autofill() - it must be stored because it getAutofillValue()
591         // must return the exact same value that was autofilled, otherwise the widget will not be
592         // properly highlighted after autofill().
593         private long mAutofilledValue;
594 
AbstractDatePickerDelegate(DatePicker delegator, Context context)595         public AbstractDatePickerDelegate(DatePicker delegator, Context context) {
596             mDelegator = delegator;
597             mContext = context;
598 
599             setCurrentLocale(Locale.getDefault());
600         }
601 
setCurrentLocale(Locale locale)602         protected void setCurrentLocale(Locale locale) {
603             if (!locale.equals(mCurrentLocale)) {
604                 mCurrentLocale = locale;
605                 onLocaleChanged(locale);
606             }
607         }
608 
609         @Override
setOnDateChangedListener(OnDateChangedListener callback)610         public void setOnDateChangedListener(OnDateChangedListener callback) {
611             mOnDateChangedListener = callback;
612         }
613 
614         @Override
setAutoFillChangeListener(OnDateChangedListener callback)615         public void setAutoFillChangeListener(OnDateChangedListener callback) {
616             mAutoFillChangeListener = callback;
617         }
618 
619         @Override
setValidationCallback(ValidationCallback callback)620         public void setValidationCallback(ValidationCallback callback) {
621             mValidationCallback = callback;
622         }
623 
624         @Override
autofill(AutofillValue value)625         public final void autofill(AutofillValue value) {
626             if (value == null || !value.isDate()) {
627                 Log.w(LOG_TAG, value + " could not be autofilled into " + this);
628                 return;
629             }
630 
631             final long time = value.getDateValue();
632 
633             final Calendar cal = Calendar.getInstance(mCurrentLocale);
634             cal.setTimeInMillis(time);
635             updateDate(cal.get(Calendar.YEAR), cal.get(Calendar.MONTH),
636                     cal.get(Calendar.DAY_OF_MONTH));
637 
638             // Must set mAutofilledValue *after* calling subclass method to make sure the value
639             // returned by getAutofillValue() matches it.
640             mAutofilledValue = time;
641         }
642 
643         @Override
getAutofillValue()644         public final AutofillValue getAutofillValue() {
645             final long time = mAutofilledValue != 0
646                     ? mAutofilledValue
647                     : mCurrentDate.getTimeInMillis();
648             return AutofillValue.forDate(time);
649         }
650 
651         /**
652          * This method must be called every time the value of the year, month, and/or day is
653          * changed by a subclass method.
654          */
resetAutofilledValue()655         protected void resetAutofilledValue() {
656             mAutofilledValue = 0;
657         }
658 
onValidationChanged(boolean valid)659         protected void onValidationChanged(boolean valid) {
660             if (mValidationCallback != null) {
661                 mValidationCallback.onValidationChanged(valid);
662             }
663         }
664 
onLocaleChanged(Locale locale)665         protected void onLocaleChanged(Locale locale) {
666             // Stub.
667         }
668 
669         @Override
onPopulateAccessibilityEvent(AccessibilityEvent event)670         public void onPopulateAccessibilityEvent(AccessibilityEvent event) {
671             event.getText().add(getFormattedCurrentDate());
672         }
673 
getFormattedCurrentDate()674         protected String getFormattedCurrentDate() {
675            return DateUtils.formatDateTime(mContext, mCurrentDate.getTimeInMillis(),
676                    DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_SHOW_YEAR
677                            | DateUtils.FORMAT_SHOW_WEEKDAY);
678         }
679 
680         /**
681          * Class for managing state storing/restoring.
682          */
683         static class SavedState extends View.BaseSavedState {
684             private final int mSelectedYear;
685             private final int mSelectedMonth;
686             private final int mSelectedDay;
687             private final long mMinDate;
688             private final long mMaxDate;
689             private final int mCurrentView;
690             private final int mListPosition;
691             private final int mListPositionOffset;
692 
SavedState(Parcelable superState, int year, int month, int day, long minDate, long maxDate)693             public SavedState(Parcelable superState, int year, int month, int day, long minDate,
694                     long maxDate) {
695                 this(superState, year, month, day, minDate, maxDate, 0, 0, 0);
696             }
697 
698             /**
699              * Constructor called from {@link DatePicker#onSaveInstanceState()}
700              */
SavedState(Parcelable superState, int year, int month, int day, long minDate, long maxDate, int currentView, int listPosition, int listPositionOffset)701             public SavedState(Parcelable superState, int year, int month, int day, long minDate,
702                     long maxDate, int currentView, int listPosition, int listPositionOffset) {
703                 super(superState);
704                 mSelectedYear = year;
705                 mSelectedMonth = month;
706                 mSelectedDay = day;
707                 mMinDate = minDate;
708                 mMaxDate = maxDate;
709                 mCurrentView = currentView;
710                 mListPosition = listPosition;
711                 mListPositionOffset = listPositionOffset;
712             }
713 
714             /**
715              * Constructor called from {@link #CREATOR}
716              */
SavedState(Parcel in)717             private SavedState(Parcel in) {
718                 super(in);
719                 mSelectedYear = in.readInt();
720                 mSelectedMonth = in.readInt();
721                 mSelectedDay = in.readInt();
722                 mMinDate = in.readLong();
723                 mMaxDate = in.readLong();
724                 mCurrentView = in.readInt();
725                 mListPosition = in.readInt();
726                 mListPositionOffset = in.readInt();
727             }
728 
729             @Override
writeToParcel(Parcel dest, int flags)730             public void writeToParcel(Parcel dest, int flags) {
731                 super.writeToParcel(dest, flags);
732                 dest.writeInt(mSelectedYear);
733                 dest.writeInt(mSelectedMonth);
734                 dest.writeInt(mSelectedDay);
735                 dest.writeLong(mMinDate);
736                 dest.writeLong(mMaxDate);
737                 dest.writeInt(mCurrentView);
738                 dest.writeInt(mListPosition);
739                 dest.writeInt(mListPositionOffset);
740             }
741 
getSelectedDay()742             public int getSelectedDay() {
743                 return mSelectedDay;
744             }
745 
getSelectedMonth()746             public int getSelectedMonth() {
747                 return mSelectedMonth;
748             }
749 
getSelectedYear()750             public int getSelectedYear() {
751                 return mSelectedYear;
752             }
753 
getMinDate()754             public long getMinDate() {
755                 return mMinDate;
756             }
757 
getMaxDate()758             public long getMaxDate() {
759                 return mMaxDate;
760             }
761 
getCurrentView()762             public int getCurrentView() {
763                 return mCurrentView;
764             }
765 
getListPosition()766             public int getListPosition() {
767                 return mListPosition;
768             }
769 
getListPositionOffset()770             public int getListPositionOffset() {
771                 return mListPositionOffset;
772             }
773 
774             @SuppressWarnings("all")
775             // suppress unused and hiding
776             public static final Parcelable.Creator<SavedState> CREATOR = new Creator<SavedState>() {
777 
778                 public SavedState createFromParcel(Parcel in) {
779                     return new SavedState(in);
780                 }
781 
782                 public SavedState[] newArray(int size) {
783                     return new SavedState[size];
784                 }
785             };
786         }
787     }
788 
789     /**
790      * A callback interface for updating input validity when the date picker
791      * when included into a dialog.
792      *
793      * @hide
794      */
795     public interface ValidationCallback {
onValidationChanged(boolean valid)796         void onValidationChanged(boolean valid);
797     }
798 
799     @Override
dispatchProvideAutofillStructure(ViewStructure structure, int flags)800     public void dispatchProvideAutofillStructure(ViewStructure structure, int flags) {
801         // This view is self-sufficient for autofill, so it needs to call
802         // onProvideAutoFillStructure() to fill itself, but it does not need to call
803         // dispatchProvideAutoFillStructure() to fill its children.
804         structure.setAutofillId(getAutofillId());
805         onProvideAutofillStructure(structure, flags);
806     }
807 
808     @Override
autofill(AutofillValue value)809     public void autofill(AutofillValue value) {
810         if (!isEnabled()) return;
811 
812         mDelegate.autofill(value);
813     }
814 
815     @Override
getAutofillType()816     public @AutofillType int getAutofillType() {
817         return isEnabled() ? AUTOFILL_TYPE_DATE : AUTOFILL_TYPE_NONE;
818     }
819 
820     @Override
getAutofillValue()821     public AutofillValue getAutofillValue() {
822         return isEnabled() ? mDelegate.getAutofillValue() : null;
823     }
824 }
825