DatePicker.java revision a53efe9923bedab4fe5d578f32eaff308e5b9e76
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.Widget;
20import android.content.Context;
21import android.content.res.Configuration;
22import android.content.res.TypedArray;
23import android.os.Parcel;
24import android.os.Parcelable;
25import android.text.TextUtils;
26import android.text.format.DateFormat;
27import android.text.format.DateUtils;
28import android.util.AttributeSet;
29import android.util.Log;
30import android.util.SparseArray;
31import android.view.LayoutInflater;
32import android.view.accessibility.AccessibilityEvent;
33import android.view.accessibility.AccessibilityManager;
34import android.view.inputmethod.EditorInfo;
35import android.widget.NumberPicker.OnValueChangeListener;
36
37import com.android.internal.R;
38
39import java.text.ParseException;
40import java.text.SimpleDateFormat;
41import java.util.Arrays;
42import java.util.Calendar;
43import java.util.Locale;
44import java.util.TimeZone;
45
46/**
47 * This class is a widget for selecting a date. The date can be selected by a
48 * year, month, and day spinners or a {@link CalendarView}. The set of spinners
49 * and the calendar view are automatically synchronized. The client can
50 * customize whether only the spinners, or only the calendar view, or both to be
51 * displayed. Also the minimal and maximal date from which dates to be selected
52 * can be customized.
53 * <p>
54 * See the <a href="{@docRoot}resources/tutorials/views/hello-datepicker.html">Date
55 * Picker tutorial</a>.
56 * </p>
57 * <p>
58 * For a dialog using this view, see {@link android.app.DatePickerDialog}.
59 * </p>
60 *
61 * @attr ref android.R.styleable#DatePicker_startYear
62 * @attr ref android.R.styleable#DatePicker_endYear
63 * @attr ref android.R.styleable#DatePicker_maxDate
64 * @attr ref android.R.styleable#DatePicker_minDate
65 * @attr ref android.R.styleable#DatePicker_spinnersShown
66 * @attr ref android.R.styleable#DatePicker_calendarViewShown
67 */
68@Widget
69public class DatePicker extends FrameLayout {
70
71    private static final String LOG_TAG = DatePicker.class.getSimpleName();
72
73    private static final String DATE_FORMAT = "MM/dd/yyyy";
74
75    private static final int DEFAULT_START_YEAR = 1900;
76
77    private static final int DEFAULT_END_YEAR = 2100;
78
79    private static final boolean DEFAULT_CALENDAR_VIEW_SHOWN = true;
80
81    private static final boolean DEFAULT_SPINNERS_SHOWN = true;
82
83    private static final boolean DEFAULT_ENABLED_STATE = true;
84
85    private final LinearLayout mSpinners;
86
87    private final NumberPicker mDaySpinner;
88
89    private final NumberPicker mMonthSpinner;
90
91    private final NumberPicker mYearSpinner;
92
93    private final CalendarView mCalendarView;
94
95    private Locale mCurrentLocale;
96
97    private OnDateChangedListener mOnDateChangedListener;
98
99    private String[] mShortMonths;
100
101    private final java.text.DateFormat mDateFormat = new SimpleDateFormat(DATE_FORMAT);
102
103    private int mNumberOfMonths;
104
105    private Calendar mTempDate;
106
107    private Calendar mMinDate;
108
109    private Calendar mMaxDate;
110
111    private Calendar mCurrentDate;
112
113    private boolean mIsEnabled = DEFAULT_ENABLED_STATE;
114
115    /**
116     * The callback used to indicate the user changes\d the date.
117     */
118    public interface OnDateChangedListener {
119
120        /**
121         * Called upon a date change.
122         *
123         * @param view The view associated with this listener.
124         * @param year The year that was set.
125         * @param monthOfYear The month that was set (0-11) for compatibility
126         *            with {@link java.util.Calendar}.
127         * @param dayOfMonth The day of the month that was set.
128         */
129        void onDateChanged(DatePicker view, int year, int monthOfYear, int dayOfMonth);
130    }
131
132    public DatePicker(Context context) {
133        this(context, null);
134    }
135
136    public DatePicker(Context context, AttributeSet attrs) {
137        this(context, attrs, R.attr.datePickerStyle);
138    }
139
140    public DatePicker(Context context, AttributeSet attrs, int defStyle) {
141        super(context, attrs, defStyle);
142
143        // initialization based on locale
144        setCurrentLocale(Locale.getDefault());
145
146        TypedArray attributesArray = context.obtainStyledAttributes(attrs, R.styleable.DatePicker,
147                defStyle, 0);
148        boolean spinnersShown = attributesArray.getBoolean(R.styleable.DatePicker_spinnersShown,
149                DEFAULT_SPINNERS_SHOWN);
150        boolean calendarViewShown = attributesArray.getBoolean(
151                R.styleable.DatePicker_calendarViewShown, DEFAULT_CALENDAR_VIEW_SHOWN);
152        int startYear = attributesArray.getInt(R.styleable.DatePicker_startYear,
153                DEFAULT_START_YEAR);
154        int endYear = attributesArray.getInt(R.styleable.DatePicker_endYear, DEFAULT_END_YEAR);
155        String minDate = attributesArray.getString(R.styleable.DatePicker_minDate);
156        String maxDate = attributesArray.getString(R.styleable.DatePicker_maxDate);
157        int layoutResourceId = attributesArray.getResourceId(R.styleable.DatePicker_layout,
158                R.layout.date_picker);
159        attributesArray.recycle();
160
161        LayoutInflater inflater = (LayoutInflater) context
162                .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
163        inflater.inflate(layoutResourceId, this, true);
164
165        OnValueChangeListener onChangeListener = new OnValueChangeListener() {
166            public void onValueChange(NumberPicker picker, int oldVal, int newVal) {
167                mTempDate.setTimeInMillis(mCurrentDate.getTimeInMillis());
168                // take care of wrapping of days and months to update greater fields
169                if (picker == mDaySpinner) {
170                    int maxDayOfMonth = mTempDate.getActualMaximum(Calendar.DAY_OF_MONTH);
171                    if (oldVal == maxDayOfMonth && newVal == 1) {
172                        mTempDate.add(Calendar.DAY_OF_MONTH, 1);
173                    } else if (oldVal == 1 && newVal == maxDayOfMonth) {
174                        mTempDate.add(Calendar.DAY_OF_MONTH, -1);
175                    } else {
176                        mTempDate.add(Calendar.DAY_OF_MONTH, newVal - oldVal);
177                    }
178                } else if (picker == mMonthSpinner) {
179                    if (oldVal == 11 && newVal == 0) {
180                        mTempDate.add(Calendar.MONTH, 1);
181                    } else if (oldVal == 0 && newVal == 11) {
182                        mTempDate.add(Calendar.MONTH, -1);
183                    } else {
184                        mTempDate.add(Calendar.MONTH, newVal - oldVal);
185                    }
186                } else if (picker == mYearSpinner) {
187                    mTempDate.set(Calendar.YEAR, newVal);
188                } else {
189                    throw new IllegalArgumentException();
190                }
191                // now set the date to the adjusted one
192                setDate(mTempDate.get(Calendar.YEAR), mTempDate.get(Calendar.MONTH),
193                        mTempDate.get(Calendar.DAY_OF_MONTH));
194                updateSpinners();
195                updateCalendarView();
196                notifyDateChanged();
197            }
198        };
199
200        mSpinners = (LinearLayout) findViewById(R.id.pickers);
201
202        // calendar view day-picker
203        mCalendarView = (CalendarView) findViewById(R.id.calendar_view);
204        mCalendarView.setOnDateChangeListener(new CalendarView.OnDateChangeListener() {
205            public void onSelectedDayChange(CalendarView view, int year, int month, int monthDay) {
206                setDate(year, month, monthDay);
207                updateSpinners();
208                notifyDateChanged();
209            }
210        });
211
212        // day
213        mDaySpinner = (NumberPicker) findViewById(R.id.day);
214        mDaySpinner.setFormatter(NumberPicker.TWO_DIGIT_FORMATTER);
215        mDaySpinner.setOnLongPressUpdateInterval(100);
216        mDaySpinner.setOnValueChangedListener(onChangeListener);
217
218        // month
219        mMonthSpinner = (NumberPicker) findViewById(R.id.month);
220        mMonthSpinner.setMinValue(0);
221        mMonthSpinner.setMaxValue(mNumberOfMonths - 1);
222        mMonthSpinner.setDisplayedValues(mShortMonths);
223        mMonthSpinner.setOnLongPressUpdateInterval(200);
224        mMonthSpinner.setOnValueChangedListener(onChangeListener);
225
226        // year
227        mYearSpinner = (NumberPicker) findViewById(R.id.year);
228        mYearSpinner.setOnLongPressUpdateInterval(100);
229        mYearSpinner.setOnValueChangedListener(onChangeListener);
230
231        // show only what the user required but make sure we
232        // show something and the spinners have higher priority
233        if (!spinnersShown && !calendarViewShown) {
234            setSpinnersShown(true);
235        } else {
236            setSpinnersShown(spinnersShown);
237            setCalendarViewShown(calendarViewShown);
238        }
239
240        // set the min date giving priority of the minDate over startYear
241        mTempDate.clear();
242        if (!TextUtils.isEmpty(minDate)) {
243            if (!parseDate(minDate, mTempDate)) {
244                mTempDate.set(startYear, 0, 1);
245            }
246        } else {
247            mTempDate.set(startYear, 0, 1);
248        }
249        setMinDate(mTempDate.getTimeInMillis());
250
251        // set the max date giving priority of the maxDate over endYear
252        mTempDate.clear();
253        if (!TextUtils.isEmpty(maxDate)) {
254            if (!parseDate(maxDate, mTempDate)) {
255                mTempDate.set(endYear, 11, 31);
256            }
257        } else {
258            mTempDate.set(endYear, 11, 31);
259        }
260        setMaxDate(mTempDate.getTimeInMillis());
261
262        // initialize to current date
263        mCurrentDate.setTimeInMillis(System.currentTimeMillis());
264        init(mCurrentDate.get(Calendar.YEAR), mCurrentDate.get(Calendar.MONTH), mCurrentDate
265                .get(Calendar.DAY_OF_MONTH), null);
266
267        // re-order the number spinners to match the current date format
268        reorderSpinners();
269
270        // set content descriptions
271        if (AccessibilityManager.getInstance(mContext).isEnabled()) {
272            setContentDescriptions();
273        }
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 mCalendarView.getMinDate();
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        mTempDate.setTimeInMillis(minDate);
299        if (mTempDate.get(Calendar.YEAR) == mMinDate.get(Calendar.YEAR)
300                && mTempDate.get(Calendar.DAY_OF_YEAR) != mMinDate.get(Calendar.DAY_OF_YEAR)) {
301            return;
302        }
303        mMinDate.setTimeInMillis(minDate);
304        mCalendarView.setMinDate(minDate);
305        if (mCurrentDate.before(mMinDate)) {
306            mCurrentDate.setTimeInMillis(mMinDate.getTimeInMillis());
307            updateCalendarView();
308        }
309        updateSpinners();
310    }
311
312    /**
313     * Gets 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     * <p>
317     * Note: The default maximal date is 12/31/2100.
318     * <p>
319     *
320     * @return The maximal supported date.
321     */
322    public long getMaxDate() {
323        return mCalendarView.getMaxDate();
324    }
325
326    /**
327     * Sets the maximal date supported by this {@link DatePicker} in
328     * milliseconds since January 1, 1970 00:00:00 in
329     * {@link TimeZone#getDefault()} time zone.
330     *
331     * @param maxDate The maximal supported date.
332     */
333    public void setMaxDate(long maxDate) {
334        mTempDate.setTimeInMillis(maxDate);
335        if (mTempDate.get(Calendar.YEAR) == mMaxDate.get(Calendar.YEAR)
336                && mTempDate.get(Calendar.DAY_OF_YEAR) != mMaxDate.get(Calendar.DAY_OF_YEAR)) {
337            return;
338        }
339        mMaxDate.setTimeInMillis(maxDate);
340        mCalendarView.setMaxDate(maxDate);
341        if (mCurrentDate.after(mMaxDate)) {
342            mCurrentDate.setTimeInMillis(mMaxDate.getTimeInMillis());
343            updateCalendarView();
344        }
345        updateSpinners();
346    }
347
348    @Override
349    public void setEnabled(boolean enabled) {
350        if (mIsEnabled == enabled) {
351            return;
352        }
353        super.setEnabled(enabled);
354        mDaySpinner.setEnabled(enabled);
355        mMonthSpinner.setEnabled(enabled);
356        mYearSpinner.setEnabled(enabled);
357        mCalendarView.setEnabled(enabled);
358        mIsEnabled = enabled;
359    }
360
361    @Override
362    public boolean isEnabled() {
363        return mIsEnabled;
364    }
365
366    @Override
367    public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {
368        onPopulateAccessibilityEvent(event);
369        return true;
370    }
371
372    @Override
373    public void onPopulateAccessibilityEvent(AccessibilityEvent event) {
374        super.onPopulateAccessibilityEvent(event);
375
376        final int flags = DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_SHOW_YEAR;
377        String selectedDateUtterance = DateUtils.formatDateTime(mContext,
378                mCurrentDate.getTimeInMillis(), flags);
379        event.getText().add(selectedDateUtterance);
380    }
381
382    @Override
383    protected void onConfigurationChanged(Configuration newConfig) {
384        super.onConfigurationChanged(newConfig);
385        setCurrentLocale(newConfig.locale);
386    }
387
388    /**
389     * Gets whether the {@link CalendarView} is shown.
390     *
391     * @return True if the calendar view is shown.
392     * @see #getCalendarView()
393     */
394    public boolean getCalendarViewShown() {
395        return mCalendarView.isShown();
396    }
397
398    /**
399     * Gets the {@link CalendarView}.
400     *
401     * @return The calendar view.
402     * @see #getCalendarViewShown()
403     */
404    public CalendarView getCalendarView () {
405        return mCalendarView;
406    }
407
408    /**
409     * Sets whether the {@link CalendarView} is shown.
410     *
411     * @param shown True if the calendar view is to be shown.
412     */
413    public void setCalendarViewShown(boolean shown) {
414        mCalendarView.setVisibility(shown ? VISIBLE : GONE);
415    }
416
417    /**
418     * Gets whether the spinners are shown.
419     *
420     * @return True if the spinners are shown.
421     */
422    public boolean getSpinnersShown() {
423        return mSpinners.isShown();
424    }
425
426    /**
427     * Sets whether the spinners are shown.
428     *
429     * @param shown True if the spinners are to be shown.
430     */
431    public void setSpinnersShown(boolean shown) {
432        mSpinners.setVisibility(shown ? VISIBLE : GONE);
433    }
434
435    /**
436     * Sets the current locale.
437     *
438     * @param locale The current locale.
439     */
440    private void setCurrentLocale(Locale locale) {
441        if (locale.equals(mCurrentLocale)) {
442            return;
443        }
444
445        mCurrentLocale = locale;
446
447        mTempDate = getCalendarForLocale(mTempDate, locale);
448        mMinDate = getCalendarForLocale(mMinDate, locale);
449        mMaxDate = getCalendarForLocale(mMaxDate, locale);
450        mCurrentDate = getCalendarForLocale(mCurrentDate, locale);
451
452        mNumberOfMonths = mTempDate.getActualMaximum(Calendar.MONTH) + 1;
453        mShortMonths = new String[mNumberOfMonths];
454        for (int i = 0; i < mNumberOfMonths; i++) {
455            mShortMonths[i] = DateUtils.getMonthString(Calendar.JANUARY + i,
456                    DateUtils.LENGTH_MEDIUM);
457        }
458    }
459
460    /**
461     * Gets a calendar for locale bootstrapped with the value of a given calendar.
462     *
463     * @param oldCalendar The old calendar.
464     * @param locale The locale.
465     */
466    private Calendar getCalendarForLocale(Calendar oldCalendar, Locale locale) {
467        if (oldCalendar == null) {
468            return Calendar.getInstance(locale);
469        } else {
470            final long currentTimeMillis = oldCalendar.getTimeInMillis();
471            Calendar newCalendar = Calendar.getInstance(locale);
472            newCalendar.setTimeInMillis(currentTimeMillis);
473            return newCalendar;
474        }
475    }
476
477    /**
478     * Reorders the spinners according to the date format that is
479     * explicitly set by the user and if no such is set fall back
480     * to the current locale's default format.
481     */
482    private void reorderSpinners() {
483        mSpinners.removeAllViews();
484        char[] order = DateFormat.getDateFormatOrder(getContext());
485        final int spinnerCount = order.length;
486        for (int i = 0; i < spinnerCount; i++) {
487            switch (order[i]) {
488                case DateFormat.DATE:
489                    mSpinners.addView(mDaySpinner);
490                    setImeOptions(mDaySpinner, spinnerCount, i);
491                    break;
492                case DateFormat.MONTH:
493                    mSpinners.addView(mMonthSpinner);
494                    setImeOptions(mMonthSpinner, spinnerCount, i);
495                    break;
496                case DateFormat.YEAR:
497                    mSpinners.addView(mYearSpinner);
498                    setImeOptions(mYearSpinner, spinnerCount, i);
499                    break;
500                default:
501                    throw new IllegalArgumentException();
502            }
503        }
504    }
505
506    /**
507     * Updates the current date.
508     *
509     * @param year The year.
510     * @param month The month which is <strong>starting from zero</strong>.
511     * @param dayOfMonth The day of the month.
512     */
513    public void updateDate(int year, int month, int dayOfMonth) {
514        if (!isNewDate(year, month, dayOfMonth)) {
515            return;
516        }
517        setDate(year, month, dayOfMonth);
518        updateSpinners();
519        updateCalendarView();
520        notifyDateChanged();
521    }
522
523    // Override so we are in complete control of save / restore for this widget.
524    @Override
525    protected void dispatchRestoreInstanceState(SparseArray<Parcelable> container) {
526        dispatchThawSelfOnly(container);
527    }
528
529    @Override
530    protected Parcelable onSaveInstanceState() {
531        Parcelable superState = super.onSaveInstanceState();
532        return new SavedState(superState, getYear(), getMonth(), getDayOfMonth());
533    }
534
535    @Override
536    protected void onRestoreInstanceState(Parcelable state) {
537        SavedState ss = (SavedState) state;
538        super.onRestoreInstanceState(ss.getSuperState());
539        setDate(ss.mYear, ss.mMonth, ss.mDay);
540        updateSpinners();
541        updateCalendarView();
542    }
543
544    /**
545     * Initialize the state. If the provided values designate an inconsistent
546     * date the values are normalized before updating the spinners.
547     *
548     * @param year The initial year.
549     * @param monthOfYear The initial month <strong>starting from zero</strong>.
550     * @param dayOfMonth The initial day of the month.
551     * @param onDateChangedListener How user is notified date is changed by
552     *            user, can be null.
553     */
554    public void init(int year, int monthOfYear, int dayOfMonth,
555            OnDateChangedListener onDateChangedListener) {
556        setDate(year, monthOfYear, dayOfMonth);
557        updateSpinners();
558        updateCalendarView();
559        mOnDateChangedListener = onDateChangedListener;
560    }
561
562    /**
563     * Parses the given <code>date</code> and in case of success sets the result
564     * to the <code>outDate</code>.
565     *
566     * @return True if the date was parsed.
567     */
568    private boolean parseDate(String date, Calendar outDate) {
569        try {
570            outDate.setTime(mDateFormat.parse(date));
571            return true;
572        } catch (ParseException e) {
573            Log.w(LOG_TAG, "Date: " + date + " not in format: " + DATE_FORMAT);
574            return false;
575        }
576    }
577
578    private boolean isNewDate(int year, int month, int dayOfMonth) {
579        return (mCurrentDate.get(Calendar.YEAR) != year
580                || mCurrentDate.get(Calendar.MONTH) != dayOfMonth
581                || mCurrentDate.get(Calendar.DAY_OF_MONTH) != month);
582    }
583
584    private void setDate(int year, int month, int dayOfMonth) {
585        mCurrentDate.set(year, month, dayOfMonth);
586        if (mCurrentDate.before(mMinDate)) {
587            mCurrentDate.setTimeInMillis(mMinDate.getTimeInMillis());
588        } else if (mCurrentDate.after(mMaxDate)) {
589            mCurrentDate.setTimeInMillis(mMaxDate.getTimeInMillis());
590        }
591    }
592
593    private void updateSpinners() {
594        // set the spinner ranges respecting the min and max dates
595        if (mCurrentDate.equals(mMinDate)) {
596            mDaySpinner.setMinValue(mCurrentDate.get(Calendar.DAY_OF_MONTH));
597            mDaySpinner.setMaxValue(mCurrentDate.getActualMaximum(Calendar.DAY_OF_MONTH));
598            mDaySpinner.setWrapSelectorWheel(false);
599            mMonthSpinner.setDisplayedValues(null);
600            mMonthSpinner.setMinValue(mCurrentDate.get(Calendar.MONTH));
601            mMonthSpinner.setMaxValue(mCurrentDate.getActualMaximum(Calendar.MONTH));
602            mMonthSpinner.setWrapSelectorWheel(false);
603        } else if (mCurrentDate.equals(mMaxDate)) {
604            mDaySpinner.setMinValue(mCurrentDate.getActualMinimum(Calendar.DAY_OF_MONTH));
605            mDaySpinner.setMaxValue(mCurrentDate.get(Calendar.DAY_OF_MONTH));
606            mDaySpinner.setWrapSelectorWheel(false);
607            mMonthSpinner.setDisplayedValues(null);
608            mMonthSpinner.setMinValue(mCurrentDate.getActualMinimum(Calendar.MONTH));
609            mMonthSpinner.setMaxValue(mCurrentDate.get(Calendar.MONTH));
610            mMonthSpinner.setWrapSelectorWheel(false);
611        } else {
612            mDaySpinner.setMinValue(1);
613            mDaySpinner.setMaxValue(mCurrentDate.getActualMaximum(Calendar.DAY_OF_MONTH));
614            mDaySpinner.setWrapSelectorWheel(true);
615            mMonthSpinner.setDisplayedValues(null);
616            mMonthSpinner.setMinValue(0);
617            mMonthSpinner.setMaxValue(11);
618            mMonthSpinner.setWrapSelectorWheel(true);
619        }
620
621        // make sure the month names are a zero based array
622        // with the months in the month spinner
623        String[] displayedValues = Arrays.copyOfRange(mShortMonths,
624                mMonthSpinner.getMinValue(), mMonthSpinner.getMaxValue() + 1);
625        mMonthSpinner.setDisplayedValues(displayedValues);
626
627        // year spinner range does not change based on the current date
628        mYearSpinner.setMinValue(mMinDate.get(Calendar.YEAR));
629        mYearSpinner.setMaxValue(mMaxDate.get(Calendar.YEAR));
630        mYearSpinner.setWrapSelectorWheel(false);
631
632        // set the spinner values
633        mYearSpinner.setValue(mCurrentDate.get(Calendar.YEAR));
634        mMonthSpinner.setValue(mCurrentDate.get(Calendar.MONTH));
635        mDaySpinner.setValue(mCurrentDate.get(Calendar.DAY_OF_MONTH));
636    }
637
638    /**
639     * Updates the calendar view with the current date.
640     */
641    private void updateCalendarView() {
642         mCalendarView.setDate(mCurrentDate.getTimeInMillis(), false, false);
643    }
644
645    /**
646     * @return The selected year.
647     */
648    public int getYear() {
649        return mCurrentDate.get(Calendar.YEAR);
650    }
651
652    /**
653     * @return The selected month.
654     */
655    public int getMonth() {
656        return mCurrentDate.get(Calendar.MONTH);
657    }
658
659    /**
660     * @return The selected day of month.
661     */
662    public int getDayOfMonth() {
663        return mCurrentDate.get(Calendar.DAY_OF_MONTH);
664    }
665
666    /**
667     * Notifies the listener, if such, for a change in the selected date.
668     */
669    private void notifyDateChanged() {
670        sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_SELECTED);
671        if (mOnDateChangedListener != null) {
672            mOnDateChangedListener.onDateChanged(this, getYear(), getMonth(), getDayOfMonth());
673        }
674    }
675
676    /**
677     * Sets the IME options for a spinner based on its ordering.
678     *
679     * @param spinner The spinner.
680     * @param spinnerCount The total spinner count.
681     * @param spinnerIndex The index of the given spinner.
682     */
683    private void setImeOptions(NumberPicker spinner, int spinnerCount, int spinnerIndex) {
684        final int imeOptions;
685        if (spinnerIndex < spinnerCount - 1) {
686            imeOptions = EditorInfo.IME_ACTION_NEXT;
687        } else {
688            imeOptions = EditorInfo.IME_ACTION_DONE;
689        }
690        TextView input = (TextView) spinner.findViewById(R.id.numberpicker_input);
691        input.setImeOptions(imeOptions);
692    }
693
694    private void setContentDescriptions() {
695        // Day
696        String text = mContext.getString(R.string.date_picker_increment_day_button);
697        mDaySpinner.findViewById(R.id.increment).setContentDescription(text);
698        text = mContext.getString(R.string.date_picker_decrement_day_button);
699        mDaySpinner.findViewById(R.id.decrement).setContentDescription(text);
700        // Month
701        text = mContext.getString(R.string.date_picker_increment_month_button);
702        mMonthSpinner.findViewById(R.id.increment).setContentDescription(text);
703        text = mContext.getString(R.string.date_picker_decrement_month_button);
704        mMonthSpinner.findViewById(R.id.decrement).setContentDescription(text);
705        // Year
706        text = mContext.getString(R.string.date_picker_increment_year_button);
707        mYearSpinner.findViewById(R.id.increment).setContentDescription(text);
708        text = mContext.getString(R.string.date_picker_decrement_year_button);
709        mYearSpinner.findViewById(R.id.decrement).setContentDescription(text);
710    }
711
712    /**
713     * Class for managing state storing/restoring.
714     */
715    private static class SavedState extends BaseSavedState {
716
717        private final int mYear;
718
719        private final int mMonth;
720
721        private final int mDay;
722
723        /**
724         * Constructor called from {@link DatePicker#onSaveInstanceState()}
725         */
726        private SavedState(Parcelable superState, int year, int month, int day) {
727            super(superState);
728            mYear = year;
729            mMonth = month;
730            mDay = day;
731        }
732
733        /**
734         * Constructor called from {@link #CREATOR}
735         */
736        private SavedState(Parcel in) {
737            super(in);
738            mYear = in.readInt();
739            mMonth = in.readInt();
740            mDay = in.readInt();
741        }
742
743        @Override
744        public void writeToParcel(Parcel dest, int flags) {
745            super.writeToParcel(dest, flags);
746            dest.writeInt(mYear);
747            dest.writeInt(mMonth);
748            dest.writeInt(mDay);
749        }
750
751        @SuppressWarnings("all")
752        // suppress unused and hiding
753        public static final Parcelable.Creator<SavedState> CREATOR = new Creator<SavedState>() {
754
755            public SavedState createFromParcel(Parcel in) {
756                return new SavedState(in);
757            }
758
759            public SavedState[] newArray(int size) {
760                return new SavedState[size];
761            }
762        };
763    }
764}
765