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 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 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 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 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 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 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 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 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 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 402 public int getYear() { 403 return mCurrentDate.get(Calendar.YEAR); 404 } 405 406 @Override 407 public int getMonth() { 408 return mCurrentDate.get(Calendar.MONTH); 409 } 410 411 @Override 412 public int getDayOfMonth() { 413 return mCurrentDate.get(Calendar.DAY_OF_MONTH); 414 } 415 416 @Override 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 433 public Calendar getMinDate() { 434 return mMinDate; 435 } 436 437 @Override 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 454 public Calendar getMaxDate() { 455 return mMaxDate; 456 } 457 458 @Override 459 public void setFirstDayOfWeek(int firstDayOfWeek) { 460 mFirstDayOfWeek = firstDayOfWeek; 461 462 mDayPickerView.setFirstDayOfWeek(firstDayOfWeek); 463 } 464 465 @Override 466 public int getFirstDayOfWeek() { 467 if (mFirstDayOfWeek != USE_LOCALE) { 468 return mFirstDayOfWeek; 469 } 470 return mCurrentDate.getFirstDayOfWeek(); 471 } 472 473 @Override 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 483 public boolean isEnabled() { 484 return mContainer.isEnabled(); 485 } 486 487 @Override 488 public CalendarView getCalendarView() { 489 throw new UnsupportedOperationException("Not supported by calendar-mode DatePicker"); 490 } 491 492 @Override 493 public void setCalendarViewShown(boolean shown) { 494 // No-op for compatibility with the old DatePicker. 495 } 496 497 @Override 498 public boolean getCalendarViewShown() { 499 return false; 500 } 501 502 @Override 503 public void setSpinnersShown(boolean shown) { 504 // No-op for compatibility with the old DatePicker. 505 } 506 507 @Override 508 public boolean getSpinnersShown() { 509 return false; 510 } 511 512 @Override 513 public void onConfigurationChanged(Configuration newConfig) { 514 setCurrentLocale(newConfig.locale); 515 } 516 517 @Override 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 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 563 public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) { 564 onPopulateAccessibilityEvent(event); 565 return true; 566 } 567 568 @Override 569 public void onPopulateAccessibilityEvent(AccessibilityEvent event) { 570 event.getText().add(mCurrentDate.getTime().toString()); 571 } 572 573 public CharSequence getAccessibilityClassName() { 574 return DatePicker.class.getName(); 575 } 576 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 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 */ 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 */ 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 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 661 public int getSelectedDay() { 662 return mSelectedDay; 663 } 664 665 public int getSelectedMonth() { 666 return mSelectedMonth; 667 } 668 669 public int getSelectedYear() { 670 return mSelectedYear; 671 } 672 673 public long getMinDate() { 674 return mMinDate; 675 } 676 677 public long getMaxDate() { 678 return mMaxDate; 679 } 680 681 public int getCurrentView() { 682 return mCurrentView; 683 } 684 685 public int getListPosition() { 686 return mListPosition; 687 } 688 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