1 /* 2 * Copyright (C) 2014 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.Nullable; 20 import android.content.Context; 21 import android.content.res.ColorStateList; 22 import android.content.res.Configuration; 23 import android.content.res.Resources; 24 import android.content.res.TypedArray; 25 import android.os.Parcel; 26 import android.os.Parcelable; 27 import android.text.format.DateFormat; 28 import android.text.format.DateUtils; 29 import android.util.AttributeSet; 30 import android.util.StateSet; 31 import android.view.HapticFeedbackConstants; 32 import android.view.LayoutInflater; 33 import android.view.View; 34 import android.view.View.OnClickListener; 35 import android.view.ViewGroup; 36 import android.view.accessibility.AccessibilityEvent; 37 import android.widget.DayPickerView.OnDaySelectedListener; 38 import android.widget.YearPickerView.OnYearSelectedListener; 39 40 import com.android.internal.R; 41 42 import java.text.SimpleDateFormat; 43 import java.util.Calendar; 44 import java.util.Locale; 45 46 /** 47 * A delegate for picking up a date (day / month / year). 48 */ 49 class DatePickerCalendarDelegate extends DatePicker.AbstractDatePickerDelegate { 50 private static final int USE_LOCALE = 0; 51 52 private static final int UNINITIALIZED = -1; 53 private static final int VIEW_MONTH_DAY = 0; 54 private static final int VIEW_YEAR = 1; 55 56 private static final int DEFAULT_START_YEAR = 1900; 57 private static final int DEFAULT_END_YEAR = 2100; 58 59 private static final int ANIMATION_DURATION = 300; 60 61 private static final int[] ATTRS_TEXT_COLOR = new int[] { 62 com.android.internal.R.attr.textColor}; 63 private static final int[] ATTRS_DISABLED_ALPHA = new int[] { 64 com.android.internal.R.attr.disabledAlpha}; 65 66 private SimpleDateFormat mYearFormat; 67 private SimpleDateFormat mMonthDayFormat; 68 69 // Top-level container. 70 private ViewGroup mContainer; 71 72 // Header views. 73 private TextView mHeaderYear; 74 private TextView mHeaderMonthDay; 75 76 // Picker views. 77 private ViewAnimator mAnimator; 78 private DayPickerView mDayPickerView; 79 private YearPickerView mYearPickerView; 80 81 // Accessibility strings. 82 private String mSelectDay; 83 private String mSelectYear; 84 85 private DatePicker.OnDateChangedListener mDateChangedListener; 86 87 private int mCurrentView = UNINITIALIZED; 88 89 private final Calendar mCurrentDate; 90 private final Calendar mTempDate; 91 private final Calendar mMinDate; 92 private final Calendar mMaxDate; 93 94 private int mFirstDayOfWeek = USE_LOCALE; 95 DatePickerCalendarDelegate(DatePicker delegator, Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes)96 public DatePickerCalendarDelegate(DatePicker delegator, Context context, AttributeSet attrs, 97 int defStyleAttr, int defStyleRes) { 98 super(delegator, context); 99 100 final Locale locale = mCurrentLocale; 101 mCurrentDate = Calendar.getInstance(locale); 102 mTempDate = Calendar.getInstance(locale); 103 mMinDate = Calendar.getInstance(locale); 104 mMaxDate = Calendar.getInstance(locale); 105 106 mMinDate.set(DEFAULT_START_YEAR, Calendar.JANUARY, 1); 107 mMaxDate.set(DEFAULT_END_YEAR, Calendar.DECEMBER, 31); 108 109 final Resources res = mDelegator.getResources(); 110 final TypedArray a = mContext.obtainStyledAttributes(attrs, 111 R.styleable.DatePicker, defStyleAttr, defStyleRes); 112 final LayoutInflater inflater = (LayoutInflater) mContext.getSystemService( 113 Context.LAYOUT_INFLATER_SERVICE); 114 final int layoutResourceId = a.getResourceId( 115 R.styleable.DatePicker_internalLayout, R.layout.date_picker_material); 116 117 // Set up and attach container. 118 mContainer = (ViewGroup) inflater.inflate(layoutResourceId, mDelegator, false); 119 mDelegator.addView(mContainer); 120 121 // Set up header views. 122 final ViewGroup header = (ViewGroup) mContainer.findViewById(R.id.date_picker_header); 123 mHeaderYear = (TextView) header.findViewById(R.id.date_picker_header_year); 124 mHeaderYear.setOnClickListener(mOnHeaderClickListener); 125 mHeaderMonthDay = (TextView) header.findViewById(R.id.date_picker_header_date); 126 mHeaderMonthDay.setOnClickListener(mOnHeaderClickListener); 127 128 // For the sake of backwards compatibility, attempt to extract the text 129 // color from the header month text appearance. If it's set, we'll let 130 // that override the "real" header text color. 131 ColorStateList headerTextColor = null; 132 133 @SuppressWarnings("deprecation") 134 final int monthHeaderTextAppearance = a.getResourceId( 135 R.styleable.DatePicker_headerMonthTextAppearance, 0); 136 if (monthHeaderTextAppearance != 0) { 137 final TypedArray textAppearance = mContext.obtainStyledAttributes(null, 138 ATTRS_TEXT_COLOR, 0, monthHeaderTextAppearance); 139 final ColorStateList legacyHeaderTextColor = textAppearance.getColorStateList(0); 140 headerTextColor = applyLegacyColorFixes(legacyHeaderTextColor); 141 textAppearance.recycle(); 142 } 143 144 if (headerTextColor == null) { 145 headerTextColor = a.getColorStateList(R.styleable.DatePicker_headerTextColor); 146 } 147 148 if (headerTextColor != null) { 149 mHeaderYear.setTextColor(headerTextColor); 150 mHeaderMonthDay.setTextColor(headerTextColor); 151 } 152 153 // Set up header background, if available. 154 if (a.hasValueOrEmpty(R.styleable.DatePicker_headerBackground)) { 155 header.setBackground(a.getDrawable(R.styleable.DatePicker_headerBackground)); 156 } 157 158 a.recycle(); 159 160 // Set up picker container. 161 mAnimator = (ViewAnimator) mContainer.findViewById(R.id.animator); 162 163 // Set up day picker view. 164 mDayPickerView = (DayPickerView) mAnimator.findViewById(R.id.date_picker_day_picker); 165 mDayPickerView.setFirstDayOfWeek(mFirstDayOfWeek); 166 mDayPickerView.setMinDate(mMinDate.getTimeInMillis()); 167 mDayPickerView.setMaxDate(mMaxDate.getTimeInMillis()); 168 mDayPickerView.setDate(mCurrentDate.getTimeInMillis()); 169 mDayPickerView.setOnDaySelectedListener(mOnDaySelectedListener); 170 171 // Set up year picker view. 172 mYearPickerView = (YearPickerView) mAnimator.findViewById(R.id.date_picker_year_picker); 173 mYearPickerView.setRange(mMinDate, mMaxDate); 174 mYearPickerView.setDate(mCurrentDate.getTimeInMillis()); 175 mYearPickerView.setOnYearSelectedListener(mOnYearSelectedListener); 176 177 // Set up content descriptions. 178 mSelectDay = res.getString(R.string.select_day); 179 mSelectYear = res.getString(R.string.select_year); 180 181 // Initialize for current locale. This also initializes the date, so no 182 // need to call onDateChanged. 183 onLocaleChanged(mCurrentLocale); 184 185 setCurrentView(VIEW_MONTH_DAY); 186 } 187 188 /** 189 * The legacy text color might have been poorly defined. Ensures that it 190 * has an appropriate activated state, using the selected state if one 191 * exists or modifying the default text color otherwise. 192 * 193 * @param color a legacy text color, or {@code null} 194 * @return a color state list with an appropriate activated state, or 195 * {@code null} if a valid activated state could not be generated 196 */ 197 @Nullable applyLegacyColorFixes(@ullable ColorStateList color)198 private ColorStateList applyLegacyColorFixes(@Nullable ColorStateList color) { 199 if (color == null || color.hasState(R.attr.state_activated)) { 200 return color; 201 } 202 203 final int activatedColor; 204 final int defaultColor; 205 if (color.hasState(R.attr.state_selected)) { 206 activatedColor = color.getColorForState(StateSet.get( 207 StateSet.VIEW_STATE_ENABLED | StateSet.VIEW_STATE_SELECTED), 0); 208 defaultColor = color.getColorForState(StateSet.get( 209 StateSet.VIEW_STATE_ENABLED), 0); 210 } else { 211 activatedColor = color.getDefaultColor(); 212 213 // Generate a non-activated color using the disabled alpha. 214 final TypedArray ta = mContext.obtainStyledAttributes(ATTRS_DISABLED_ALPHA); 215 final float disabledAlpha = ta.getFloat(0, 0.30f); 216 defaultColor = multiplyAlphaComponent(activatedColor, disabledAlpha); 217 } 218 219 if (activatedColor == 0 || defaultColor == 0) { 220 // We somehow failed to obtain the colors. 221 return null; 222 } 223 224 final int[][] stateSet = new int[][] {{ R.attr.state_activated }, {}}; 225 final int[] colors = new int[] { activatedColor, defaultColor }; 226 return new ColorStateList(stateSet, colors); 227 } 228 multiplyAlphaComponent(int color, float alphaMod)229 private int multiplyAlphaComponent(int color, float alphaMod) { 230 final int srcRgb = color & 0xFFFFFF; 231 final int srcAlpha = (color >> 24) & 0xFF; 232 final int dstAlpha = (int) (srcAlpha * alphaMod + 0.5f); 233 return srcRgb | (dstAlpha << 24); 234 } 235 236 /** 237 * Listener called when the user selects a day in the day picker view. 238 */ 239 private final OnDaySelectedListener mOnDaySelectedListener = new OnDaySelectedListener() { 240 @Override 241 public void onDaySelected(DayPickerView view, Calendar day) { 242 mCurrentDate.setTimeInMillis(day.getTimeInMillis()); 243 onDateChanged(true, true); 244 } 245 }; 246 247 /** 248 * Listener called when the user selects a year in the year picker view. 249 */ 250 private final OnYearSelectedListener mOnYearSelectedListener = new OnYearSelectedListener() { 251 @Override 252 public void onYearChanged(YearPickerView view, int year) { 253 // If the newly selected month / year does not contain the 254 // currently selected day number, change the selected day number 255 // to the last day of the selected month or year. 256 // e.g. Switching from Mar to Apr when Mar 31 is selected -> Apr 30 257 // e.g. Switching from 2012 to 2013 when Feb 29, 2012 is selected -> Feb 28, 2013 258 final int day = mCurrentDate.get(Calendar.DAY_OF_MONTH); 259 final int month = mCurrentDate.get(Calendar.MONTH); 260 final int daysInMonth = getDaysInMonth(month, year); 261 if (day > daysInMonth) { 262 mCurrentDate.set(Calendar.DAY_OF_MONTH, daysInMonth); 263 } 264 265 mCurrentDate.set(Calendar.YEAR, year); 266 onDateChanged(true, true); 267 268 // Automatically switch to day picker. 269 setCurrentView(VIEW_MONTH_DAY); 270 } 271 }; 272 273 /** 274 * Listener called when the user clicks on a header item. 275 */ 276 private final OnClickListener mOnHeaderClickListener = new OnClickListener() { 277 @Override 278 public void onClick(View v) { 279 tryVibrate(); 280 281 switch (v.getId()) { 282 case R.id.date_picker_header_year: 283 setCurrentView(VIEW_YEAR); 284 break; 285 case R.id.date_picker_header_date: 286 setCurrentView(VIEW_MONTH_DAY); 287 break; 288 } 289 } 290 }; 291 292 @Override onLocaleChanged(Locale locale)293 protected void onLocaleChanged(Locale locale) { 294 final TextView headerYear = mHeaderYear; 295 if (headerYear == null) { 296 // Abort, we haven't initialized yet. This method will get called 297 // again later after everything has been set up. 298 return; 299 } 300 301 // Update the date formatter. 302 final String datePattern = DateFormat.getBestDateTimePattern(locale, "EMMMd"); 303 mMonthDayFormat = new SimpleDateFormat(datePattern, locale); 304 mYearFormat = new SimpleDateFormat("y", locale); 305 306 // Update the header text. 307 onCurrentDateChanged(false); 308 } 309 onCurrentDateChanged(boolean announce)310 private void onCurrentDateChanged(boolean announce) { 311 if (mHeaderYear == null) { 312 // Abort, we haven't initialized yet. This method will get called 313 // again later after everything has been set up. 314 return; 315 } 316 317 final String year = mYearFormat.format(mCurrentDate.getTime()); 318 mHeaderYear.setText(year); 319 320 final String monthDay = mMonthDayFormat.format(mCurrentDate.getTime()); 321 mHeaderMonthDay.setText(monthDay); 322 323 // TODO: This should use live regions. 324 if (announce) { 325 final long millis = mCurrentDate.getTimeInMillis(); 326 final int flags = DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_SHOW_YEAR; 327 final String fullDateText = DateUtils.formatDateTime(mContext, millis, flags); 328 mAnimator.announceForAccessibility(fullDateText); 329 } 330 } 331 setCurrentView(final int viewIndex)332 private void setCurrentView(final int viewIndex) { 333 switch (viewIndex) { 334 case VIEW_MONTH_DAY: 335 mDayPickerView.setDate(mCurrentDate.getTimeInMillis()); 336 337 if (mCurrentView != viewIndex) { 338 mHeaderMonthDay.setActivated(true); 339 mHeaderYear.setActivated(false); 340 mAnimator.setDisplayedChild(VIEW_MONTH_DAY); 341 mCurrentView = viewIndex; 342 } 343 344 mAnimator.announceForAccessibility(mSelectDay); 345 break; 346 case VIEW_YEAR: 347 mYearPickerView.setDate(mCurrentDate.getTimeInMillis()); 348 349 if (mCurrentView != viewIndex) { 350 mHeaderMonthDay.setActivated(false); 351 mHeaderYear.setActivated(true); 352 mAnimator.setDisplayedChild(VIEW_YEAR); 353 mCurrentView = viewIndex; 354 } 355 356 mAnimator.announceForAccessibility(mSelectYear); 357 break; 358 } 359 } 360 361 @Override init(int year, int monthOfYear, int dayOfMonth, DatePicker.OnDateChangedListener callBack)362 public void init(int year, int monthOfYear, int dayOfMonth, 363 DatePicker.OnDateChangedListener callBack) { 364 mCurrentDate.set(Calendar.YEAR, year); 365 mCurrentDate.set(Calendar.MONTH, monthOfYear); 366 mCurrentDate.set(Calendar.DAY_OF_MONTH, dayOfMonth); 367 368 mDateChangedListener = callBack; 369 370 onDateChanged(false, false); 371 } 372 373 @Override updateDate(int year, int month, int dayOfMonth)374 public void updateDate(int year, int month, int dayOfMonth) { 375 mCurrentDate.set(Calendar.YEAR, year); 376 mCurrentDate.set(Calendar.MONTH, month); 377 mCurrentDate.set(Calendar.DAY_OF_MONTH, dayOfMonth); 378 379 onDateChanged(false, true); 380 } 381 onDateChanged(boolean fromUser, boolean callbackToClient)382 private void onDateChanged(boolean fromUser, boolean callbackToClient) { 383 final int year = mCurrentDate.get(Calendar.YEAR); 384 385 if (callbackToClient && mDateChangedListener != null) { 386 final int monthOfYear = mCurrentDate.get(Calendar.MONTH); 387 final int dayOfMonth = mCurrentDate.get(Calendar.DAY_OF_MONTH); 388 mDateChangedListener.onDateChanged(mDelegator, year, monthOfYear, dayOfMonth); 389 } 390 391 mDayPickerView.setDate(mCurrentDate.getTimeInMillis()); 392 mYearPickerView.setYear(year); 393 394 onCurrentDateChanged(fromUser); 395 396 if (fromUser) { 397 tryVibrate(); 398 } 399 } 400 401 @Override getYear()402 public int getYear() { 403 return mCurrentDate.get(Calendar.YEAR); 404 } 405 406 @Override getMonth()407 public int getMonth() { 408 return mCurrentDate.get(Calendar.MONTH); 409 } 410 411 @Override getDayOfMonth()412 public int getDayOfMonth() { 413 return mCurrentDate.get(Calendar.DAY_OF_MONTH); 414 } 415 416 @Override setMinDate(long minDate)417 public void setMinDate(long minDate) { 418 mTempDate.setTimeInMillis(minDate); 419 if (mTempDate.get(Calendar.YEAR) == mMinDate.get(Calendar.YEAR) 420 && mTempDate.get(Calendar.DAY_OF_YEAR) != mMinDate.get(Calendar.DAY_OF_YEAR)) { 421 return; 422 } 423 if (mCurrentDate.before(mTempDate)) { 424 mCurrentDate.setTimeInMillis(minDate); 425 onDateChanged(false, true); 426 } 427 mMinDate.setTimeInMillis(minDate); 428 mDayPickerView.setMinDate(minDate); 429 mYearPickerView.setRange(mMinDate, mMaxDate); 430 } 431 432 @Override getMinDate()433 public Calendar getMinDate() { 434 return mMinDate; 435 } 436 437 @Override setMaxDate(long maxDate)438 public void setMaxDate(long maxDate) { 439 mTempDate.setTimeInMillis(maxDate); 440 if (mTempDate.get(Calendar.YEAR) == mMaxDate.get(Calendar.YEAR) 441 && mTempDate.get(Calendar.DAY_OF_YEAR) != mMaxDate.get(Calendar.DAY_OF_YEAR)) { 442 return; 443 } 444 if (mCurrentDate.after(mTempDate)) { 445 mCurrentDate.setTimeInMillis(maxDate); 446 onDateChanged(false, true); 447 } 448 mMaxDate.setTimeInMillis(maxDate); 449 mDayPickerView.setMaxDate(maxDate); 450 mYearPickerView.setRange(mMinDate, mMaxDate); 451 } 452 453 @Override getMaxDate()454 public Calendar getMaxDate() { 455 return mMaxDate; 456 } 457 458 @Override setFirstDayOfWeek(int firstDayOfWeek)459 public void setFirstDayOfWeek(int firstDayOfWeek) { 460 mFirstDayOfWeek = firstDayOfWeek; 461 462 mDayPickerView.setFirstDayOfWeek(firstDayOfWeek); 463 } 464 465 @Override getFirstDayOfWeek()466 public int getFirstDayOfWeek() { 467 if (mFirstDayOfWeek != USE_LOCALE) { 468 return mFirstDayOfWeek; 469 } 470 return mCurrentDate.getFirstDayOfWeek(); 471 } 472 473 @Override setEnabled(boolean enabled)474 public void setEnabled(boolean enabled) { 475 mContainer.setEnabled(enabled); 476 mDayPickerView.setEnabled(enabled); 477 mYearPickerView.setEnabled(enabled); 478 mHeaderYear.setEnabled(enabled); 479 mHeaderMonthDay.setEnabled(enabled); 480 } 481 482 @Override isEnabled()483 public boolean isEnabled() { 484 return mContainer.isEnabled(); 485 } 486 487 @Override getCalendarView()488 public CalendarView getCalendarView() { 489 throw new UnsupportedOperationException("Not supported by calendar-mode DatePicker"); 490 } 491 492 @Override setCalendarViewShown(boolean shown)493 public void setCalendarViewShown(boolean shown) { 494 // No-op for compatibility with the old DatePicker. 495 } 496 497 @Override getCalendarViewShown()498 public boolean getCalendarViewShown() { 499 return false; 500 } 501 502 @Override setSpinnersShown(boolean shown)503 public void setSpinnersShown(boolean shown) { 504 // No-op for compatibility with the old DatePicker. 505 } 506 507 @Override getSpinnersShown()508 public boolean getSpinnersShown() { 509 return false; 510 } 511 512 @Override onConfigurationChanged(Configuration newConfig)513 public void onConfigurationChanged(Configuration newConfig) { 514 setCurrentLocale(newConfig.locale); 515 } 516 517 @Override onSaveInstanceState(Parcelable superState)518 public Parcelable onSaveInstanceState(Parcelable superState) { 519 final int year = mCurrentDate.get(Calendar.YEAR); 520 final int month = mCurrentDate.get(Calendar.MONTH); 521 final int day = mCurrentDate.get(Calendar.DAY_OF_MONTH); 522 523 int listPosition = -1; 524 int listPositionOffset = -1; 525 526 if (mCurrentView == VIEW_MONTH_DAY) { 527 listPosition = mDayPickerView.getMostVisiblePosition(); 528 } else if (mCurrentView == VIEW_YEAR) { 529 listPosition = mYearPickerView.getFirstVisiblePosition(); 530 listPositionOffset = mYearPickerView.getFirstPositionOffset(); 531 } 532 533 return new SavedState(superState, year, month, day, mMinDate.getTimeInMillis(), 534 mMaxDate.getTimeInMillis(), mCurrentView, listPosition, listPositionOffset); 535 } 536 537 @Override onRestoreInstanceState(Parcelable state)538 public void onRestoreInstanceState(Parcelable state) { 539 final SavedState ss = (SavedState) state; 540 541 // TODO: Move instance state into DayPickerView, YearPickerView. 542 mCurrentDate.set(ss.getSelectedYear(), ss.getSelectedMonth(), ss.getSelectedDay()); 543 mMinDate.setTimeInMillis(ss.getMinDate()); 544 mMaxDate.setTimeInMillis(ss.getMaxDate()); 545 546 onCurrentDateChanged(false); 547 548 final int currentView = ss.getCurrentView(); 549 setCurrentView(currentView); 550 551 final int listPosition = ss.getListPosition(); 552 if (listPosition != -1) { 553 if (currentView == VIEW_MONTH_DAY) { 554 mDayPickerView.setPosition(listPosition); 555 } else if (currentView == VIEW_YEAR) { 556 final int listPositionOffset = ss.getListPositionOffset(); 557 mYearPickerView.setSelectionFromTop(listPosition, listPositionOffset); 558 } 559 } 560 } 561 562 @Override dispatchPopulateAccessibilityEvent(AccessibilityEvent event)563 public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) { 564 onPopulateAccessibilityEvent(event); 565 return true; 566 } 567 568 @Override onPopulateAccessibilityEvent(AccessibilityEvent event)569 public void onPopulateAccessibilityEvent(AccessibilityEvent event) { 570 event.getText().add(mCurrentDate.getTime().toString()); 571 } 572 getAccessibilityClassName()573 public CharSequence getAccessibilityClassName() { 574 return DatePicker.class.getName(); 575 } 576 getDaysInMonth(int month, int year)577 public static int getDaysInMonth(int month, int year) { 578 switch (month) { 579 case Calendar.JANUARY: 580 case Calendar.MARCH: 581 case Calendar.MAY: 582 case Calendar.JULY: 583 case Calendar.AUGUST: 584 case Calendar.OCTOBER: 585 case Calendar.DECEMBER: 586 return 31; 587 case Calendar.APRIL: 588 case Calendar.JUNE: 589 case Calendar.SEPTEMBER: 590 case Calendar.NOVEMBER: 591 return 30; 592 case Calendar.FEBRUARY: 593 return (year % 4 == 0) ? 29 : 28; 594 default: 595 throw new IllegalArgumentException("Invalid Month"); 596 } 597 } 598 tryVibrate()599 private void tryVibrate() { 600 mDelegator.performHapticFeedback(HapticFeedbackConstants.CALENDAR_DATE); 601 } 602 603 /** 604 * Class for managing state storing/restoring. 605 */ 606 private static class SavedState extends View.BaseSavedState { 607 private final int mSelectedYear; 608 private final int mSelectedMonth; 609 private final int mSelectedDay; 610 private final long mMinDate; 611 private final long mMaxDate; 612 private final int mCurrentView; 613 private final int mListPosition; 614 private final int mListPositionOffset; 615 616 /** 617 * Constructor called from {@link DatePicker#onSaveInstanceState()} 618 */ SavedState(Parcelable superState, int year, int month, int day, long minDate, long maxDate, int currentView, int listPosition, int listPositionOffset)619 private SavedState(Parcelable superState, int year, int month, int day, 620 long minDate, long maxDate, int currentView, int listPosition, 621 int listPositionOffset) { 622 super(superState); 623 mSelectedYear = year; 624 mSelectedMonth = month; 625 mSelectedDay = day; 626 mMinDate = minDate; 627 mMaxDate = maxDate; 628 mCurrentView = currentView; 629 mListPosition = listPosition; 630 mListPositionOffset = listPositionOffset; 631 } 632 633 /** 634 * Constructor called from {@link #CREATOR} 635 */ SavedState(Parcel in)636 private SavedState(Parcel in) { 637 super(in); 638 mSelectedYear = in.readInt(); 639 mSelectedMonth = in.readInt(); 640 mSelectedDay = in.readInt(); 641 mMinDate = in.readLong(); 642 mMaxDate = in.readLong(); 643 mCurrentView = in.readInt(); 644 mListPosition = in.readInt(); 645 mListPositionOffset = in.readInt(); 646 } 647 648 @Override writeToParcel(Parcel dest, int flags)649 public void writeToParcel(Parcel dest, int flags) { 650 super.writeToParcel(dest, flags); 651 dest.writeInt(mSelectedYear); 652 dest.writeInt(mSelectedMonth); 653 dest.writeInt(mSelectedDay); 654 dest.writeLong(mMinDate); 655 dest.writeLong(mMaxDate); 656 dest.writeInt(mCurrentView); 657 dest.writeInt(mListPosition); 658 dest.writeInt(mListPositionOffset); 659 } 660 getSelectedDay()661 public int getSelectedDay() { 662 return mSelectedDay; 663 } 664 getSelectedMonth()665 public int getSelectedMonth() { 666 return mSelectedMonth; 667 } 668 getSelectedYear()669 public int getSelectedYear() { 670 return mSelectedYear; 671 } 672 getMinDate()673 public long getMinDate() { 674 return mMinDate; 675 } 676 getMaxDate()677 public long getMaxDate() { 678 return mMaxDate; 679 } 680 getCurrentView()681 public int getCurrentView() { 682 return mCurrentView; 683 } 684 getListPosition()685 public int getListPosition() { 686 return mListPosition; 687 } 688 getListPositionOffset()689 public int getListPositionOffset() { 690 return mListPositionOffset; 691 } 692 693 @SuppressWarnings("all") 694 // suppress unused and hiding 695 public static final Parcelable.Creator<SavedState> CREATOR = new Creator<SavedState>() { 696 697 public SavedState createFromParcel(Parcel in) { 698 return new SavedState(in); 699 } 700 701 public SavedState[] newArray(int size) { 702 return new SavedState[size]; 703 } 704 }; 705 } 706 } 707