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
17package android.widget;
18
19import android.annotation.IntDef;
20import android.annotation.Nullable;
21import android.annotation.TestApi;
22import android.annotation.Widget;
23import android.content.Context;
24import android.content.res.Configuration;
25import android.content.res.TypedArray;
26import android.icu.util.Calendar;
27import android.icu.util.TimeZone;
28import android.os.Parcel;
29import android.os.Parcelable;
30import android.text.format.DateUtils;
31import android.util.AttributeSet;
32import android.util.Log;
33import android.util.SparseArray;
34import android.view.View;
35import android.view.ViewStructure;
36import android.view.accessibility.AccessibilityEvent;
37import android.view.autofill.AutofillManager;
38import android.view.autofill.AutofillValue;
39
40import com.android.internal.R;
41
42import java.lang.annotation.Retention;
43import java.lang.annotation.RetentionPolicy;
44import 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
86public 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         */
136        void onDateChanged(DatePicker view, int year, int monthOfYear, int dayOfMonth);
137    }
138
139    public DatePicker(Context context) {
140        this(context, null);
141    }
142
143    public DatePicker(Context context, AttributeSet attrs) {
144        this(context, attrs, R.attr.datePickerStyle);
145    }
146
147    public DatePicker(Context context, AttributeSet attrs, int defStyleAttr) {
148        this(context, attrs, defStyleAttr, 0);
149    }
150
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
196    private DatePickerDelegate createSpinnerUIDelegate(Context context, AttributeSet attrs,
197            int defStyleAttr, int defStyleRes) {
198        return new DatePickerSpinnerDelegate(this, context, attrs, defStyleAttr, defStyleRes);
199    }
200
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
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     */
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     */
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     */
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     */
258    public int getYear() {
259        return mDelegate.getYear();
260    }
261
262    /**
263     * @return The selected month.
264     */
265    public int getMonth() {
266        return mDelegate.getMonth();
267    }
268
269    /**
270     * @return The selected day of month.
271     */
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     */
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     */
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     */
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     */
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     */
332    public void setValidationCallback(@Nullable ValidationCallback callback) {
333        mDelegate.setValidationCallback(callback);
334    }
335
336    @Override
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
346    public boolean isEnabled() {
347        return mDelegate.isEnabled();
348    }
349
350    /** @hide */
351    @Override
352    public boolean dispatchPopulateAccessibilityEventInternal(AccessibilityEvent event) {
353        return mDelegate.dispatchPopulateAccessibilityEvent(event);
354    }
355
356    /** @hide */
357    @Override
358    public void onPopulateAccessibilityEventInternal(AccessibilityEvent event) {
359        super.onPopulateAccessibilityEventInternal(event);
360        mDelegate.onPopulateAccessibilityEvent(event);
361    }
362
363    @Override
364    public CharSequence getAccessibilityClassName() {
365        return DatePicker.class.getName();
366    }
367
368    @Override
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     */
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     */
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
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
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
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
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
493    public void setSpinnersShown(boolean shown) {
494        mDelegate.setSpinnersShown(shown);
495    }
496
497    @Override
498    protected void dispatchRestoreInstanceState(SparseArray<Parcelable> container) {
499        dispatchThawSelfOnly(container);
500    }
501
502    @Override
503    protected Parcelable onSaveInstanceState() {
504        Parcelable superState = super.onSaveInstanceState();
505        return mDelegate.onSaveInstanceState(superState);
506    }
507
508    @Override
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 {
523        void init(int year, int monthOfYear, int dayOfMonth,
524                  OnDateChangedListener onDateChangedListener);
525
526        void setOnDateChangedListener(OnDateChangedListener onDateChangedListener);
527        void setAutoFillChangeListener(OnDateChangedListener onDateChangedListener);
528
529        void updateDate(int year, int month, int dayOfMonth);
530
531        int getYear();
532        int getMonth();
533        int getDayOfMonth();
534
535        void autofill(AutofillValue value);
536        AutofillValue getAutofillValue();
537
538        void setFirstDayOfWeek(int firstDayOfWeek);
539        int getFirstDayOfWeek();
540
541        void setMinDate(long minDate);
542        Calendar getMinDate();
543
544        void setMaxDate(long maxDate);
545        Calendar getMaxDate();
546
547        void setEnabled(boolean enabled);
548        boolean isEnabled();
549
550        CalendarView getCalendarView();
551
552        void setCalendarViewShown(boolean shown);
553        boolean getCalendarViewShown();
554
555        void setSpinnersShown(boolean shown);
556        boolean getSpinnersShown();
557
558        void setValidationCallback(ValidationCallback callback);
559
560        void onConfigurationChanged(Configuration newConfig);
561
562        Parcelable onSaveInstanceState(Parcelable superState);
563        void onRestoreInstanceState(Parcelable state);
564
565        boolean dispatchPopulateAccessibilityEvent(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
595        public AbstractDatePickerDelegate(DatePicker delegator, Context context) {
596            mDelegator = delegator;
597            mContext = context;
598
599            setCurrentLocale(Locale.getDefault());
600        }
601
602        protected void setCurrentLocale(Locale locale) {
603            if (!locale.equals(mCurrentLocale)) {
604                mCurrentLocale = locale;
605                onLocaleChanged(locale);
606            }
607        }
608
609        @Override
610        public void setOnDateChangedListener(OnDateChangedListener callback) {
611            mOnDateChangedListener = callback;
612        }
613
614        @Override
615        public void setAutoFillChangeListener(OnDateChangedListener callback) {
616            mAutoFillChangeListener = callback;
617        }
618
619        @Override
620        public void setValidationCallback(ValidationCallback callback) {
621            mValidationCallback = callback;
622        }
623
624        @Override
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
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         */
655        protected void resetAutofilledValue() {
656            mAutofilledValue = 0;
657        }
658
659        protected void onValidationChanged(boolean valid) {
660            if (mValidationCallback != null) {
661                mValidationCallback.onValidationChanged(valid);
662            }
663        }
664
665        protected void onLocaleChanged(Locale locale) {
666            // Stub.
667        }
668
669        @Override
670        public void onPopulateAccessibilityEvent(AccessibilityEvent event) {
671            event.getText().add(getFormattedCurrentDate());
672        }
673
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
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             */
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             */
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
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
742            public int getSelectedDay() {
743                return mSelectedDay;
744            }
745
746            public int getSelectedMonth() {
747                return mSelectedMonth;
748            }
749
750            public int getSelectedYear() {
751                return mSelectedYear;
752            }
753
754            public long getMinDate() {
755                return mMinDate;
756            }
757
758            public long getMaxDate() {
759                return mMaxDate;
760            }
761
762            public int getCurrentView() {
763                return mCurrentView;
764            }
765
766            public int getListPosition() {
767                return mListPosition;
768            }
769
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 {
796        void onValidationChanged(boolean valid);
797    }
798
799    @Override
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
809    public void autofill(AutofillValue value) {
810        if (!isEnabled()) return;
811
812        mDelegate.autofill(value);
813    }
814
815    @Override
816    public @AutofillType int getAutofillType() {
817        return isEnabled() ? AUTOFILL_TYPE_DATE : AUTOFILL_TYPE_NONE;
818    }
819
820    @Override
821    public AutofillValue getAutofillValue() {
822        return isEnabled() ? mDelegate.getAutofillValue() : null;
823    }
824}
825