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