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