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