Home | History | Annotate | Download | only in widget
      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          */
    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