DatePickerCalendarDelegate.java revision 50eb025c2fea7e364e0be951ce8ba6ca605f901a
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 17package android.widget; 18 19import android.content.Context; 20import android.content.res.ColorStateList; 21import android.content.res.Configuration; 22import android.content.res.Resources; 23import android.content.res.TypedArray; 24import android.os.Parcel; 25import android.os.Parcelable; 26import android.text.format.DateFormat; 27import android.text.format.DateUtils; 28import android.util.AttributeSet; 29import android.view.HapticFeedbackConstants; 30import android.view.LayoutInflater; 31import android.view.View; 32import android.view.accessibility.AccessibilityEvent; 33import android.view.accessibility.AccessibilityNodeInfo; 34import android.view.animation.AlphaAnimation; 35import android.view.animation.Animation; 36 37import com.android.internal.R; 38import com.android.internal.widget.AccessibleDateAnimator; 39 40import java.text.SimpleDateFormat; 41import java.util.Calendar; 42import java.util.HashSet; 43import java.util.Locale; 44 45/** 46 * A delegate for picking up a date (day / month / year). 47 */ 48class DatePickerCalendarDelegate extends DatePicker.AbstractDatePickerDelegate implements 49 View.OnClickListener, DatePickerController { 50 private static final int USE_LOCALE = 0; 51 52 private static final int UNINITIALIZED = -1; 53 private static final int MONTH_AND_DAY_VIEW = 0; 54 private static final int YEAR_VIEW = 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 MONTH_INDEX = 0; 62 private static final int DAY_INDEX = 1; 63 private static final int YEAR_INDEX = 2; 64 65 private SimpleDateFormat mYearFormat = new SimpleDateFormat("y", Locale.getDefault()); 66 private SimpleDateFormat mDayFormat = new SimpleDateFormat("d", Locale.getDefault()); 67 68 private TextView mDayOfWeekView; 69 70 /** Layout that contains the current month, day, and year. */ 71 private LinearLayout mMonthDayYearLayout; 72 73 /** Clickable layout that contains the current day and year. */ 74 private LinearLayout mMonthAndDayLayout; 75 76 private TextView mHeaderMonthTextView; 77 private TextView mHeaderDayOfMonthTextView; 78 private TextView mHeaderYearTextView; 79 private DayPickerView mDayPickerView; 80 private YearPickerView mYearPickerView; 81 82 private boolean mIsEnabled = true; 83 84 // Accessibility strings. 85 private String mDayPickerDescription; 86 private String mSelectDay; 87 private String mYearPickerDescription; 88 private String mSelectYear; 89 90 private AccessibleDateAnimator mAnimator; 91 92 private DatePicker.OnDateChangedListener mDateChangedListener; 93 94 private int mCurrentView = UNINITIALIZED; 95 96 private Calendar mCurrentDate; 97 private Calendar mTempDate; 98 private Calendar mMinDate; 99 private Calendar mMaxDate; 100 101 private int mFirstDayOfWeek = USE_LOCALE; 102 103 private HashSet<OnDateChangedListener> mListeners = new HashSet<OnDateChangedListener>(); 104 105 public DatePickerCalendarDelegate(DatePicker delegator, Context context, AttributeSet attrs, 106 int defStyleAttr, int defStyleRes) { 107 super(delegator, context); 108 109 final Locale locale = Locale.getDefault(); 110 mMinDate = getCalendarForLocale(mMinDate, locale); 111 mMaxDate = getCalendarForLocale(mMaxDate, locale); 112 mTempDate = getCalendarForLocale(mMaxDate, locale); 113 mCurrentDate = getCalendarForLocale(mCurrentDate, locale); 114 115 mMinDate.set(DEFAULT_START_YEAR, 1, 1); 116 mMaxDate.set(DEFAULT_END_YEAR, 12, 31); 117 118 final Resources res = mDelegator.getResources(); 119 final TypedArray a = mContext.obtainStyledAttributes(attrs, 120 R.styleable.DatePicker, defStyleAttr, defStyleRes); 121 final LayoutInflater inflater = (LayoutInflater) mContext.getSystemService( 122 Context.LAYOUT_INFLATER_SERVICE); 123 final int layoutResourceId = a.getResourceId( 124 R.styleable.DatePicker_internalLayout, R.layout.date_picker_holo); 125 final View mainView = inflater.inflate(layoutResourceId, null); 126 mDelegator.addView(mainView); 127 128 mDayOfWeekView = (TextView) mainView.findViewById(R.id.date_picker_header); 129 130 // Layout that contains the current date and day name header. 131 final LinearLayout dateLayout = (LinearLayout) mainView.findViewById( 132 R.id.day_picker_selector_layout); 133 mMonthDayYearLayout = (LinearLayout) mainView.findViewById( 134 R.id.date_picker_month_day_year_layout); 135 mMonthAndDayLayout = (LinearLayout) mainView.findViewById( 136 R.id.date_picker_month_and_day_layout); 137 mMonthAndDayLayout.setOnClickListener(this); 138 mHeaderMonthTextView = (TextView) mainView.findViewById(R.id.date_picker_month); 139 mHeaderDayOfMonthTextView = (TextView) mainView.findViewById(R.id.date_picker_day); 140 mHeaderYearTextView = (TextView) mainView.findViewById(R.id.date_picker_year); 141 mHeaderYearTextView.setOnClickListener(this); 142 143 // Obtain default highlight color from the theme. 144 final int defaultHighlightColor = mHeaderYearTextView.getHighlightColor(); 145 146 // Use Theme attributes if possible 147 final int dayOfWeekTextAppearanceResId = a.getResourceId( 148 R.styleable.DatePicker_dayOfWeekTextAppearance, -1); 149 if (dayOfWeekTextAppearanceResId != -1) { 150 mDayOfWeekView.setTextAppearance(context, dayOfWeekTextAppearanceResId); 151 } 152 153 mDayOfWeekView.setBackground(a.getDrawable(R.styleable.DatePicker_dayOfWeekBackground)); 154 155 dateLayout.setBackground(a.getDrawable(R.styleable.DatePicker_headerBackground)); 156 157 final int headerSelectedTextColor = a.getColor( 158 R.styleable.DatePicker_headerSelectedTextColor, defaultHighlightColor); 159 final int monthTextAppearanceResId = a.getResourceId( 160 R.styleable.DatePicker_headerMonthTextAppearance, -1); 161 if (monthTextAppearanceResId != -1) { 162 mHeaderMonthTextView.setTextAppearance(context, monthTextAppearanceResId); 163 } 164 mHeaderMonthTextView.setTextColor(ColorStateList.addFirstIfMissing( 165 mHeaderMonthTextView.getTextColors(), R.attr.state_selected, 166 headerSelectedTextColor)); 167 168 final int dayOfMonthTextAppearanceResId = a.getResourceId( 169 R.styleable.DatePicker_headerDayOfMonthTextAppearance, -1); 170 if (dayOfMonthTextAppearanceResId != -1) { 171 mHeaderDayOfMonthTextView.setTextAppearance(context, dayOfMonthTextAppearanceResId); 172 } 173 mHeaderDayOfMonthTextView.setTextColor(ColorStateList.addFirstIfMissing( 174 mHeaderDayOfMonthTextView.getTextColors(), R.attr.state_selected, 175 headerSelectedTextColor)); 176 177 final int yearTextAppearanceResId = a.getResourceId( 178 R.styleable.DatePicker_headerYearTextAppearance, -1); 179 if (yearTextAppearanceResId != -1) { 180 mHeaderYearTextView.setTextAppearance(context, yearTextAppearanceResId); 181 } 182 mHeaderYearTextView.setTextColor(ColorStateList.addFirstIfMissing( 183 mHeaderYearTextView.getTextColors(), R.attr.state_selected, 184 headerSelectedTextColor)); 185 186 mDayPickerView = new DayPickerView(mContext, this); 187 mDayPickerView.setRange(mMinDate, mMaxDate); 188 189 mYearPickerView = new YearPickerView(mContext); 190 mYearPickerView.init(this); 191 192 final int yearSelectedCircleColor = a.getColor(R.styleable.DatePicker_yearListSelectorColor, 193 defaultHighlightColor); 194 mYearPickerView.setYearSelectedCircleColor(yearSelectedCircleColor); 195 196 final ColorStateList calendarTextColor = a.getColorStateList( 197 R.styleable.DatePicker_calendarTextColor); 198 final int calendarSelectedTextColor = a.getColor( 199 R.styleable.DatePicker_calendarSelectedTextColor, defaultHighlightColor); 200 mDayPickerView.setCalendarTextColor(ColorStateList.addFirstIfMissing( 201 calendarTextColor, R.attr.state_selected, calendarSelectedTextColor)); 202 203 mDayPickerDescription = res.getString(R.string.day_picker_description); 204 mSelectDay = res.getString(R.string.select_day); 205 mYearPickerDescription = res.getString(R.string.year_picker_description); 206 mSelectYear = res.getString(R.string.select_year); 207 208 mAnimator = (AccessibleDateAnimator) mainView.findViewById(R.id.animator); 209 mAnimator.addView(mDayPickerView); 210 mAnimator.addView(mYearPickerView); 211 mAnimator.setDateMillis(mCurrentDate.getTimeInMillis()); 212 213 final Animation animation = new AlphaAnimation(0.0f, 1.0f); 214 animation.setDuration(ANIMATION_DURATION); 215 mAnimator.setInAnimation(animation); 216 217 final Animation animation2 = new AlphaAnimation(1.0f, 0.0f); 218 animation2.setDuration(ANIMATION_DURATION); 219 mAnimator.setOutAnimation(animation2); 220 221 updateDisplay(false); 222 setCurrentView(MONTH_AND_DAY_VIEW); 223 } 224 225 /** 226 * Gets a calendar for locale bootstrapped with the value of a given calendar. 227 * 228 * @param oldCalendar The old calendar. 229 * @param locale The locale. 230 */ 231 private Calendar getCalendarForLocale(Calendar oldCalendar, Locale locale) { 232 if (oldCalendar == null) { 233 return Calendar.getInstance(locale); 234 } else { 235 final long currentTimeMillis = oldCalendar.getTimeInMillis(); 236 Calendar newCalendar = Calendar.getInstance(locale); 237 newCalendar.setTimeInMillis(currentTimeMillis); 238 return newCalendar; 239 } 240 } 241 242 /** 243 * Compute the array representing the order of Month / Day / Year views in their layout. 244 * Will be used for I18N purpose as the order of them depends on the Locale. 245 */ 246 private int[] getMonthDayYearIndexes(String pattern) { 247 int[] result = new int[3]; 248 249 final String filteredPattern = pattern.replaceAll("'.*?'", ""); 250 251 final int dayIndex = filteredPattern.indexOf('d'); 252 final int monthMIndex = filteredPattern.indexOf("M"); 253 final int monthIndex = (monthMIndex != -1) ? monthMIndex : filteredPattern.indexOf("L"); 254 final int yearIndex = filteredPattern.indexOf("y"); 255 256 if (yearIndex < monthIndex) { 257 result[YEAR_INDEX] = 0; 258 259 if (monthIndex < dayIndex) { 260 result[MONTH_INDEX] = 1; 261 result[DAY_INDEX] = 2; 262 } else { 263 result[MONTH_INDEX] = 2; 264 result[DAY_INDEX] = 1; 265 } 266 } else { 267 result[YEAR_INDEX] = 2; 268 269 if (monthIndex < dayIndex) { 270 result[MONTH_INDEX] = 0; 271 result[DAY_INDEX] = 1; 272 } else { 273 result[MONTH_INDEX] = 1; 274 result[DAY_INDEX] = 0; 275 } 276 } 277 return result; 278 } 279 280 private void updateDisplay(boolean announce) { 281 if (mDayOfWeekView != null) { 282 mDayOfWeekView.setText(mCurrentDate.getDisplayName(Calendar.DAY_OF_WEEK, Calendar.LONG, 283 Locale.getDefault())); 284 } 285 286 // Compute indices of Month, Day and Year views 287 final String bestDateTimePattern = 288 DateFormat.getBestDateTimePattern(mCurrentLocale, "yMMMd"); 289 final int[] viewIndices = getMonthDayYearIndexes(bestDateTimePattern); 290 291 // Position the Year and MonthAndDay views within the header. 292 mMonthDayYearLayout.removeAllViews(); 293 if (viewIndices[YEAR_INDEX] == 0) { 294 mMonthDayYearLayout.addView(mHeaderYearTextView); 295 mMonthDayYearLayout.addView(mMonthAndDayLayout); 296 } else { 297 mMonthDayYearLayout.addView(mMonthAndDayLayout); 298 mMonthDayYearLayout.addView(mHeaderYearTextView); 299 } 300 301 // Position Day and Month views within the MonthAndDay view. 302 mMonthAndDayLayout.removeAllViews(); 303 if (viewIndices[MONTH_INDEX] > viewIndices[DAY_INDEX]) { 304 mMonthAndDayLayout.addView(mHeaderDayOfMonthTextView); 305 mMonthAndDayLayout.addView(mHeaderMonthTextView); 306 } else { 307 mMonthAndDayLayout.addView(mHeaderMonthTextView); 308 mMonthAndDayLayout.addView(mHeaderDayOfMonthTextView); 309 } 310 311 mHeaderMonthTextView.setText(mCurrentDate.getDisplayName(Calendar.MONTH, Calendar.SHORT, 312 Locale.getDefault()).toUpperCase(Locale.getDefault())); 313 mHeaderDayOfMonthTextView.setText(mDayFormat.format(mCurrentDate.getTime())); 314 mHeaderYearTextView.setText(mYearFormat.format(mCurrentDate.getTime())); 315 316 // Accessibility. 317 long millis = mCurrentDate.getTimeInMillis(); 318 mAnimator.setDateMillis(millis); 319 int flags = DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_NO_YEAR; 320 String monthAndDayText = DateUtils.formatDateTime(mContext, millis, flags); 321 mMonthAndDayLayout.setContentDescription(monthAndDayText); 322 323 if (announce) { 324 flags = DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_SHOW_YEAR; 325 String fullDateText = DateUtils.formatDateTime(mContext, millis, flags); 326 mAnimator.announceForAccessibility(fullDateText); 327 } 328 updatePickers(); 329 } 330 331 private void setCurrentView(final int viewIndex) { 332 long millis = mCurrentDate.getTimeInMillis(); 333 334 switch (viewIndex) { 335 case MONTH_AND_DAY_VIEW: 336 mDayPickerView.onDateChanged(); 337 if (mCurrentView != viewIndex) { 338 mMonthAndDayLayout.setSelected(true); 339 mHeaderYearTextView.setSelected(false); 340 mAnimator.setDisplayedChild(MONTH_AND_DAY_VIEW); 341 mCurrentView = viewIndex; 342 } 343 344 final int flags = DateUtils.FORMAT_SHOW_DATE; 345 final String dayString = DateUtils.formatDateTime(mContext, millis, flags); 346 mAnimator.setContentDescription(mDayPickerDescription + ": " + dayString); 347 mAnimator.announceForAccessibility(mSelectDay); 348 break; 349 case YEAR_VIEW: 350 mYearPickerView.onDateChanged(); 351 if (mCurrentView != viewIndex) { 352 mMonthAndDayLayout.setSelected(false); 353 mHeaderYearTextView.setSelected(true); 354 mAnimator.setDisplayedChild(YEAR_VIEW); 355 mCurrentView = viewIndex; 356 } 357 358 final CharSequence yearString = mYearFormat.format(millis); 359 mAnimator.setContentDescription(mYearPickerDescription + ": " + yearString); 360 mAnimator.announceForAccessibility(mSelectYear); 361 break; 362 } 363 } 364 365 @Override 366 public void init(int year, int monthOfYear, int dayOfMonth, 367 DatePicker.OnDateChangedListener callBack) { 368 mDateChangedListener = callBack; 369 mCurrentDate.set(Calendar.YEAR, year); 370 mCurrentDate.set(Calendar.MONTH, monthOfYear); 371 mCurrentDate.set(Calendar.DAY_OF_MONTH, dayOfMonth); 372 updateDisplay(false); 373 } 374 375 @Override 376 public void updateDate(int year, int month, int dayOfMonth) { 377 mCurrentDate.set(Calendar.YEAR, year); 378 mCurrentDate.set(Calendar.MONTH, month); 379 mCurrentDate.set(Calendar.DAY_OF_MONTH, dayOfMonth); 380 if (mDateChangedListener != null) { 381 mDateChangedListener.onDateChanged(mDelegator, year, month, dayOfMonth); 382 } 383 updateDisplay(false); 384 } 385 386 @Override 387 public int getYear() { 388 return mCurrentDate.get(Calendar.YEAR); 389 } 390 391 @Override 392 public int getMonth() { 393 return mCurrentDate.get(Calendar.MONTH); 394 } 395 396 @Override 397 public int getDayOfMonth() { 398 return mCurrentDate.get(Calendar.DAY_OF_MONTH); 399 } 400 401 @Override 402 public void setMinDate(long minDate) { 403 mTempDate.setTimeInMillis(minDate); 404 if (mTempDate.get(Calendar.YEAR) == mMinDate.get(Calendar.YEAR) 405 && mTempDate.get(Calendar.DAY_OF_YEAR) != mMinDate.get(Calendar.DAY_OF_YEAR)) { 406 return; 407 } 408 if (mCurrentDate.before(mTempDate)) { 409 mCurrentDate.setTimeInMillis(minDate); 410 updatePickers(); 411 updateDisplay(false); 412 } 413 mMinDate.setTimeInMillis(minDate); 414 mDayPickerView.setRange(mMinDate, mMaxDate); 415 mYearPickerView.setRange(mMinDate, mMaxDate); 416 } 417 418 @Override 419 public Calendar getMinDate() { 420 return mMinDate; 421 } 422 423 @Override 424 public void setMaxDate(long maxDate) { 425 mTempDate.setTimeInMillis(maxDate); 426 if (mTempDate.get(Calendar.YEAR) == mMaxDate.get(Calendar.YEAR) 427 && mTempDate.get(Calendar.DAY_OF_YEAR) != mMaxDate.get(Calendar.DAY_OF_YEAR)) { 428 return; 429 } 430 if (mCurrentDate.after(mTempDate)) { 431 mCurrentDate.setTimeInMillis(maxDate); 432 updatePickers(); 433 updateDisplay(false); 434 } 435 mMaxDate.setTimeInMillis(maxDate); 436 mDayPickerView.setRange(mMinDate, mMaxDate); 437 mYearPickerView.setRange(mMinDate, mMaxDate); 438 } 439 440 @Override 441 public Calendar getMaxDate() { 442 return mMaxDate; 443 } 444 445 @Override 446 public void setFirstDayOfWeek(int firstDayOfWeek) { 447 mFirstDayOfWeek = firstDayOfWeek; 448 } 449 450 @Override 451 public int getFirstDayOfWeek() { 452 if (mFirstDayOfWeek != USE_LOCALE) { 453 return mFirstDayOfWeek; 454 } 455 return mCurrentDate.getFirstDayOfWeek(); 456 } 457 458 @Override 459 public void setEnabled(boolean enabled) { 460 mMonthAndDayLayout.setEnabled(enabled); 461 mHeaderYearTextView.setEnabled(enabled); 462 mAnimator.setEnabled(enabled); 463 mIsEnabled = enabled; 464 } 465 466 @Override 467 public boolean isEnabled() { 468 return mIsEnabled; 469 } 470 471 @Override 472 public CalendarView getCalendarView() { 473 throw new UnsupportedOperationException( 474 "CalendarView does not exists for the new DatePicker"); 475 } 476 477 @Override 478 public void setCalendarViewShown(boolean shown) { 479 // No-op for compatibility with the old DatePicker. 480 } 481 482 @Override 483 public boolean getCalendarViewShown() { 484 return false; 485 } 486 487 @Override 488 public void setSpinnersShown(boolean shown) { 489 // No-op for compatibility with the old DatePicker. 490 } 491 492 @Override 493 public boolean getSpinnersShown() { 494 return false; 495 } 496 497 @Override 498 public void onConfigurationChanged(Configuration newConfig) { 499 mYearFormat = new SimpleDateFormat("y", newConfig.locale); 500 mDayFormat = new SimpleDateFormat("d", newConfig.locale); 501 } 502 503 @Override 504 public Parcelable onSaveInstanceState(Parcelable superState) { 505 final int year = mCurrentDate.get(Calendar.YEAR); 506 final int month = mCurrentDate.get(Calendar.MONTH); 507 final int day = mCurrentDate.get(Calendar.DAY_OF_MONTH); 508 509 int listPosition = -1; 510 int listPositionOffset = -1; 511 512 if (mCurrentView == MONTH_AND_DAY_VIEW) { 513 listPosition = mDayPickerView.getMostVisiblePosition(); 514 } else if (mCurrentView == YEAR_VIEW) { 515 listPosition = mYearPickerView.getFirstVisiblePosition(); 516 listPositionOffset = mYearPickerView.getFirstPositionOffset(); 517 } 518 519 return new SavedState(superState, year, month, day, mMinDate.getTimeInMillis(), 520 mMaxDate.getTimeInMillis(), mCurrentView, listPosition, listPositionOffset); 521 } 522 523 @Override 524 public void onRestoreInstanceState(Parcelable state) { 525 SavedState ss = (SavedState) state; 526 527 mCurrentDate.set(ss.getSelectedYear(), ss.getSelectedMonth(), ss.getSelectedDay()); 528 mCurrentView = ss.getCurrentView(); 529 mMinDate.setTimeInMillis(ss.getMinDate()); 530 mMaxDate.setTimeInMillis(ss.getMaxDate()); 531 532 updateDisplay(false); 533 setCurrentView(mCurrentView); 534 535 final int listPosition = ss.getListPosition(); 536 if (listPosition != -1) { 537 if (mCurrentView == MONTH_AND_DAY_VIEW) { 538 mDayPickerView.postSetSelection(listPosition); 539 } else if (mCurrentView == YEAR_VIEW) { 540 mYearPickerView.postSetSelectionFromTop(listPosition, ss.getListPositionOffset()); 541 } 542 } 543 } 544 545 @Override 546 public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) { 547 onPopulateAccessibilityEvent(event); 548 return true; 549 } 550 551 @Override 552 public void onPopulateAccessibilityEvent(AccessibilityEvent event) { 553 event.getText().add(mCurrentDate.getTime().toString()); 554 } 555 556 @Override 557 public void onInitializeAccessibilityEvent(AccessibilityEvent event) { 558 event.setClassName(DatePicker.class.getName()); 559 } 560 561 @Override 562 public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) { 563 info.setClassName(DatePicker.class.getName()); 564 } 565 566 @Override 567 public void onYearSelected(int year) { 568 adjustDayInMonthIfNeeded(mCurrentDate.get(Calendar.MONTH), year); 569 mCurrentDate.set(Calendar.YEAR, year); 570 updatePickers(); 571 setCurrentView(MONTH_AND_DAY_VIEW); 572 updateDisplay(true); 573 } 574 575 // If the newly selected month / year does not contain the currently selected day number, 576 // change the selected day number to the last day of the selected month or year. 577 // e.g. Switching from Mar to Apr when Mar 31 is selected -> Apr 30 578 // e.g. Switching from 2012 to 2013 when Feb 29, 2012 is selected -> Feb 28, 2013 579 private void adjustDayInMonthIfNeeded(int month, int year) { 580 int day = mCurrentDate.get(Calendar.DAY_OF_MONTH); 581 int daysInMonth = getDaysInMonth(month, year); 582 if (day > daysInMonth) { 583 mCurrentDate.set(Calendar.DAY_OF_MONTH, daysInMonth); 584 } 585 } 586 587 public static int getDaysInMonth(int month, int year) { 588 switch (month) { 589 case Calendar.JANUARY: 590 case Calendar.MARCH: 591 case Calendar.MAY: 592 case Calendar.JULY: 593 case Calendar.AUGUST: 594 case Calendar.OCTOBER: 595 case Calendar.DECEMBER: 596 return 31; 597 case Calendar.APRIL: 598 case Calendar.JUNE: 599 case Calendar.SEPTEMBER: 600 case Calendar.NOVEMBER: 601 return 30; 602 case Calendar.FEBRUARY: 603 return (year % 4 == 0) ? 29 : 28; 604 default: 605 throw new IllegalArgumentException("Invalid Month"); 606 } 607 } 608 609 @Override 610 public void onDayOfMonthSelected(int year, int month, int day) { 611 mCurrentDate.set(Calendar.YEAR, year); 612 mCurrentDate.set(Calendar.MONTH, month); 613 mCurrentDate.set(Calendar.DAY_OF_MONTH, day); 614 updatePickers(); 615 updateDisplay(true); 616 } 617 618 private void updatePickers() { 619 for (OnDateChangedListener listener : mListeners) { 620 listener.onDateChanged(); 621 } 622 } 623 624 @Override 625 public void registerOnDateChangedListener(OnDateChangedListener listener) { 626 mListeners.add(listener); 627 } 628 629 @Override 630 public void unregisterOnDateChangedListener(OnDateChangedListener listener) { 631 mListeners.remove(listener); 632 } 633 634 @Override 635 public Calendar getSelectedDay() { 636 return mCurrentDate; 637 } 638 639 @Override 640 public void tryVibrate() { 641 mDelegator.performHapticFeedback(HapticFeedbackConstants.CALENDAR_DATE); 642 } 643 644 @Override 645 public void onClick(View v) { 646 tryVibrate(); 647 if (v.getId() == R.id.date_picker_year) { 648 setCurrentView(YEAR_VIEW); 649 } else if (v.getId() == R.id.date_picker_month_and_day_layout) { 650 setCurrentView(MONTH_AND_DAY_VIEW); 651 } 652 } 653 654 /** 655 * Class for managing state storing/restoring. 656 */ 657 private static class SavedState extends View.BaseSavedState { 658 659 private final int mSelectedYear; 660 private final int mSelectedMonth; 661 private final int mSelectedDay; 662 private final long mMinDate; 663 private final long mMaxDate; 664 private final int mCurrentView; 665 private final int mListPosition; 666 private final int mListPositionOffset; 667 668 /** 669 * Constructor called from {@link DatePicker#onSaveInstanceState()} 670 */ 671 private SavedState(Parcelable superState, int year, int month, int day, 672 long minDate, long maxDate, int currentView, int listPosition, 673 int listPositionOffset) { 674 super(superState); 675 mSelectedYear = year; 676 mSelectedMonth = month; 677 mSelectedDay = day; 678 mMinDate = minDate; 679 mMaxDate = maxDate; 680 mCurrentView = currentView; 681 mListPosition = listPosition; 682 mListPositionOffset = listPositionOffset; 683 } 684 685 /** 686 * Constructor called from {@link #CREATOR} 687 */ 688 private SavedState(Parcel in) { 689 super(in); 690 mSelectedYear = in.readInt(); 691 mSelectedMonth = in.readInt(); 692 mSelectedDay = in.readInt(); 693 mMinDate = in.readLong(); 694 mMaxDate = in.readLong(); 695 mCurrentView = in.readInt(); 696 mListPosition = in.readInt(); 697 mListPositionOffset = in.readInt(); 698 } 699 700 @Override 701 public void writeToParcel(Parcel dest, int flags) { 702 super.writeToParcel(dest, flags); 703 dest.writeInt(mSelectedYear); 704 dest.writeInt(mSelectedMonth); 705 dest.writeInt(mSelectedDay); 706 dest.writeLong(mMinDate); 707 dest.writeLong(mMaxDate); 708 dest.writeInt(mCurrentView); 709 dest.writeInt(mListPosition); 710 dest.writeInt(mListPositionOffset); 711 } 712 713 public int getSelectedDay() { 714 return mSelectedDay; 715 } 716 717 public int getSelectedMonth() { 718 return mSelectedMonth; 719 } 720 721 public int getSelectedYear() { 722 return mSelectedYear; 723 } 724 725 public long getMinDate() { 726 return mMinDate; 727 } 728 729 public long getMaxDate() { 730 return mMaxDate; 731 } 732 733 public int getCurrentView() { 734 return mCurrentView; 735 } 736 737 public int getListPosition() { 738 return mListPosition; 739 } 740 741 public int getListPositionOffset() { 742 return mListPositionOffset; 743 } 744 745 @SuppressWarnings("all") 746 // suppress unused and hiding 747 public static final Parcelable.Creator<SavedState> CREATOR = new Creator<SavedState>() { 748 749 public SavedState createFromParcel(Parcel in) { 750 return new SavedState(in); 751 } 752 753 public SavedState[] newArray(int size) { 754 return new SavedState[size]; 755 } 756 }; 757 } 758} 759