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 17 package android.widget; 18 19 import android.annotation.IntDef; 20 import android.annotation.Nullable; 21 import android.annotation.TestApi; 22 import android.annotation.Widget; 23 import android.content.Context; 24 import android.content.res.Configuration; 25 import android.content.res.TypedArray; 26 import android.icu.util.Calendar; 27 import android.icu.util.TimeZone; 28 import android.os.Parcel; 29 import android.os.Parcelable; 30 import android.text.format.DateUtils; 31 import android.util.AttributeSet; 32 import android.util.Log; 33 import android.util.SparseArray; 34 import android.view.View; 35 import android.view.ViewStructure; 36 import android.view.accessibility.AccessibilityEvent; 37 import android.view.autofill.AutofillManager; 38 import android.view.autofill.AutofillValue; 39 40 import com.android.internal.R; 41 42 import java.lang.annotation.Retention; 43 import java.lang.annotation.RetentionPolicy; 44 import java.util.Locale; 45 46 /** 47 * Provides a widget for selecting a date. 48 * <p> 49 * When the {@link android.R.styleable#DatePicker_datePickerMode} attribute is 50 * set to {@code spinner}, the date can be selected using year, month, and day 51 * spinners or a {@link CalendarView}. The set of spinners and the calendar 52 * view are automatically synchronized. The client can customize whether only 53 * the spinners, or only the calendar view, or both to be displayed. 54 * </p> 55 * <p> 56 * When the {@link android.R.styleable#DatePicker_datePickerMode} attribute is 57 * set to {@code calendar}, the month and day can be selected using a 58 * calendar-style view while the year can be selected separately using a list. 59 * </p> 60 * <p> 61 * See the <a href="{@docRoot}guide/topics/ui/controls/pickers.html">Pickers</a> 62 * guide. 63 * </p> 64 * <p> 65 * For a dialog using this view, see {@link android.app.DatePickerDialog}. 66 * </p> 67 * 68 * @attr ref android.R.styleable#DatePicker_startYear 69 * @attr ref android.R.styleable#DatePicker_endYear 70 * @attr ref android.R.styleable#DatePicker_maxDate 71 * @attr ref android.R.styleable#DatePicker_minDate 72 * @attr ref android.R.styleable#DatePicker_spinnersShown 73 * @attr ref android.R.styleable#DatePicker_calendarViewShown 74 * @attr ref android.R.styleable#DatePicker_dayOfWeekBackground 75 * @attr ref android.R.styleable#DatePicker_dayOfWeekTextAppearance 76 * @attr ref android.R.styleable#DatePicker_headerBackground 77 * @attr ref android.R.styleable#DatePicker_headerMonthTextAppearance 78 * @attr ref android.R.styleable#DatePicker_headerDayOfMonthTextAppearance 79 * @attr ref android.R.styleable#DatePicker_headerYearTextAppearance 80 * @attr ref android.R.styleable#DatePicker_yearListItemTextAppearance 81 * @attr ref android.R.styleable#DatePicker_yearListSelectorColor 82 * @attr ref android.R.styleable#DatePicker_calendarTextColor 83 * @attr ref android.R.styleable#DatePicker_datePickerMode 84 */ 85 @Widget 86 public class DatePicker extends FrameLayout { 87 private static final String LOG_TAG = DatePicker.class.getSimpleName(); 88 89 /** 90 * Presentation mode for the Holo-style date picker that uses a set of 91 * {@link android.widget.NumberPicker}s. 92 * 93 * @see #getMode() 94 * @hide Visible for testing only. 95 */ 96 @TestApi 97 public static final int MODE_SPINNER = 1; 98 99 /** 100 * Presentation mode for the Material-style date picker that uses a 101 * calendar. 102 * 103 * @see #getMode() 104 * @hide Visible for testing only. 105 */ 106 @TestApi 107 public static final int MODE_CALENDAR = 2; 108 109 /** @hide */ 110 @IntDef({MODE_SPINNER, MODE_CALENDAR}) 111 @Retention(RetentionPolicy.SOURCE) 112 public @interface DatePickerMode {} 113 114 private final DatePickerDelegate mDelegate; 115 116 @DatePickerMode 117 private final int mMode; 118 119 /** 120 * The callback used to indicate the user changed the date. 121 */ 122 public interface OnDateChangedListener { 123 124 /** 125 * Called upon a date change. 126 * 127 * @param view The view associated with this listener. 128 * @param year The year that was set. 129 * @param monthOfYear The month that was set (0-11) for compatibility 130 * with {@link java.util.Calendar}. 131 * @param dayOfMonth The day of the month that was set. 132 */ onDateChanged(DatePicker view, int year, int monthOfYear, int dayOfMonth)133 void onDateChanged(DatePicker view, int year, int monthOfYear, int dayOfMonth); 134 } 135 DatePicker(Context context)136 public DatePicker(Context context) { 137 this(context, null); 138 } 139 DatePicker(Context context, AttributeSet attrs)140 public DatePicker(Context context, AttributeSet attrs) { 141 this(context, attrs, R.attr.datePickerStyle); 142 } 143 DatePicker(Context context, AttributeSet attrs, int defStyleAttr)144 public DatePicker(Context context, AttributeSet attrs, int defStyleAttr) { 145 this(context, attrs, defStyleAttr, 0); 146 } 147 DatePicker(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes)148 public DatePicker(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { 149 super(context, attrs, defStyleAttr, defStyleRes); 150 151 // DatePicker is important by default, unless app developer overrode attribute. 152 if (getImportantForAutofill() == IMPORTANT_FOR_AUTOFILL_AUTO) { 153 setImportantForAutofill(IMPORTANT_FOR_AUTOFILL_YES); 154 } 155 156 final TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.DatePicker, 157 defStyleAttr, defStyleRes); 158 final boolean isDialogMode = a.getBoolean(R.styleable.DatePicker_dialogMode, false); 159 final int requestedMode = a.getInt(R.styleable.DatePicker_datePickerMode, MODE_SPINNER); 160 final int firstDayOfWeek = a.getInt(R.styleable.DatePicker_firstDayOfWeek, 0); 161 a.recycle(); 162 163 if (requestedMode == MODE_CALENDAR && isDialogMode) { 164 // You want MODE_CALENDAR? YOU CAN'T HANDLE MODE_CALENDAR! Well, 165 // maybe you can depending on your screen size. Let's check... 166 mMode = context.getResources().getInteger(R.integer.date_picker_mode); 167 } else { 168 mMode = requestedMode; 169 } 170 171 switch (mMode) { 172 case MODE_CALENDAR: 173 mDelegate = createCalendarUIDelegate(context, attrs, defStyleAttr, defStyleRes); 174 break; 175 case MODE_SPINNER: 176 default: 177 mDelegate = createSpinnerUIDelegate(context, attrs, defStyleAttr, defStyleRes); 178 break; 179 } 180 181 if (firstDayOfWeek != 0) { 182 setFirstDayOfWeek(firstDayOfWeek); 183 } 184 185 mDelegate.setAutoFillChangeListener((v, y, m, d) -> { 186 final AutofillManager afm = context.getSystemService(AutofillManager.class); 187 if (afm != null) { 188 afm.notifyValueChanged(this); 189 } 190 }); 191 } 192 createSpinnerUIDelegate(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes)193 private DatePickerDelegate createSpinnerUIDelegate(Context context, AttributeSet attrs, 194 int defStyleAttr, int defStyleRes) { 195 return new DatePickerSpinnerDelegate(this, context, attrs, defStyleAttr, defStyleRes); 196 } 197 createCalendarUIDelegate(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes)198 private DatePickerDelegate createCalendarUIDelegate(Context context, AttributeSet attrs, 199 int defStyleAttr, int defStyleRes) { 200 return new DatePickerCalendarDelegate(this, context, attrs, defStyleAttr, 201 defStyleRes); 202 } 203 204 /** 205 * @return the picker's presentation mode, one of {@link #MODE_CALENDAR} or 206 * {@link #MODE_SPINNER} 207 * @attr ref android.R.styleable#DatePicker_datePickerMode 208 * @hide Visible for testing only. 209 */ 210 @DatePickerMode 211 @TestApi getMode()212 public int getMode() { 213 return mMode; 214 } 215 216 /** 217 * Initialize the state. If the provided values designate an inconsistent 218 * date the values are normalized before updating the spinners. 219 * 220 * @param year The initial year. 221 * @param monthOfYear The initial month <strong>starting from zero</strong>. 222 * @param dayOfMonth The initial day of the month. 223 * @param onDateChangedListener How user is notified date is changed by 224 * user, can be null. 225 */ init(int year, int monthOfYear, int dayOfMonth, OnDateChangedListener onDateChangedListener)226 public void init(int year, int monthOfYear, int dayOfMonth, 227 OnDateChangedListener onDateChangedListener) { 228 mDelegate.init(year, monthOfYear, dayOfMonth, onDateChangedListener); 229 } 230 231 /** 232 * Set the callback that indicates the date has been adjusted by the user. 233 * 234 * @param onDateChangedListener How user is notified date is changed by 235 * user, can be null. 236 */ setOnDateChangedListener(OnDateChangedListener onDateChangedListener)237 public void setOnDateChangedListener(OnDateChangedListener onDateChangedListener) { 238 mDelegate.setOnDateChangedListener(onDateChangedListener); 239 } 240 241 /** 242 * Update the current date. 243 * 244 * @param year The year. 245 * @param month The month which is <strong>starting from zero</strong>. 246 * @param dayOfMonth The day of the month. 247 */ updateDate(int year, int month, int dayOfMonth)248 public void updateDate(int year, int month, int dayOfMonth) { 249 mDelegate.updateDate(year, month, dayOfMonth); 250 } 251 252 /** 253 * @return The selected year. 254 */ getYear()255 public int getYear() { 256 return mDelegate.getYear(); 257 } 258 259 /** 260 * @return The selected month. 261 */ getMonth()262 public int getMonth() { 263 return mDelegate.getMonth(); 264 } 265 266 /** 267 * @return The selected day of month. 268 */ getDayOfMonth()269 public int getDayOfMonth() { 270 return mDelegate.getDayOfMonth(); 271 } 272 273 /** 274 * Gets the minimal date supported by this {@link DatePicker} in 275 * milliseconds since January 1, 1970 00:00:00 in 276 * {@link TimeZone#getDefault()} time zone. 277 * <p> 278 * Note: The default minimal date is 01/01/1900. 279 * <p> 280 * 281 * @return The minimal supported date. 282 */ getMinDate()283 public long getMinDate() { 284 return mDelegate.getMinDate().getTimeInMillis(); 285 } 286 287 /** 288 * Sets the minimal date supported by this {@link NumberPicker} in 289 * milliseconds since January 1, 1970 00:00:00 in 290 * {@link TimeZone#getDefault()} time zone. 291 * 292 * @param minDate The minimal supported date. 293 */ setMinDate(long minDate)294 public void setMinDate(long minDate) { 295 mDelegate.setMinDate(minDate); 296 } 297 298 /** 299 * Gets the maximal date supported by this {@link DatePicker} in 300 * milliseconds since January 1, 1970 00:00:00 in 301 * {@link TimeZone#getDefault()} time zone. 302 * <p> 303 * Note: The default maximal date is 12/31/2100. 304 * <p> 305 * 306 * @return The maximal supported date. 307 */ getMaxDate()308 public long getMaxDate() { 309 return mDelegate.getMaxDate().getTimeInMillis(); 310 } 311 312 /** 313 * Sets the maximal date supported by this {@link DatePicker} in 314 * milliseconds since January 1, 1970 00:00:00 in 315 * {@link TimeZone#getDefault()} time zone. 316 * 317 * @param maxDate The maximal supported date. 318 */ setMaxDate(long maxDate)319 public void setMaxDate(long maxDate) { 320 mDelegate.setMaxDate(maxDate); 321 } 322 323 /** 324 * Sets the callback that indicates the current date is valid. 325 * 326 * @param callback the callback, may be null 327 * @hide 328 */ setValidationCallback(@ullable ValidationCallback callback)329 public void setValidationCallback(@Nullable ValidationCallback callback) { 330 mDelegate.setValidationCallback(callback); 331 } 332 333 @Override setEnabled(boolean enabled)334 public void setEnabled(boolean enabled) { 335 if (mDelegate.isEnabled() == enabled) { 336 return; 337 } 338 super.setEnabled(enabled); 339 mDelegate.setEnabled(enabled); 340 } 341 342 @Override isEnabled()343 public boolean isEnabled() { 344 return mDelegate.isEnabled(); 345 } 346 347 /** @hide */ 348 @Override dispatchPopulateAccessibilityEventInternal(AccessibilityEvent event)349 public boolean dispatchPopulateAccessibilityEventInternal(AccessibilityEvent event) { 350 return mDelegate.dispatchPopulateAccessibilityEvent(event); 351 } 352 353 /** @hide */ 354 @Override onPopulateAccessibilityEventInternal(AccessibilityEvent event)355 public void onPopulateAccessibilityEventInternal(AccessibilityEvent event) { 356 super.onPopulateAccessibilityEventInternal(event); 357 mDelegate.onPopulateAccessibilityEvent(event); 358 } 359 360 @Override getAccessibilityClassName()361 public CharSequence getAccessibilityClassName() { 362 return DatePicker.class.getName(); 363 } 364 365 @Override onConfigurationChanged(Configuration newConfig)366 protected void onConfigurationChanged(Configuration newConfig) { 367 super.onConfigurationChanged(newConfig); 368 mDelegate.onConfigurationChanged(newConfig); 369 } 370 371 /** 372 * Sets the first day of week. 373 * 374 * @param firstDayOfWeek The first day of the week conforming to the 375 * {@link CalendarView} APIs. 376 * @see Calendar#SUNDAY 377 * @see Calendar#MONDAY 378 * @see Calendar#TUESDAY 379 * @see Calendar#WEDNESDAY 380 * @see Calendar#THURSDAY 381 * @see Calendar#FRIDAY 382 * @see Calendar#SATURDAY 383 * 384 * @attr ref android.R.styleable#DatePicker_firstDayOfWeek 385 */ setFirstDayOfWeek(int firstDayOfWeek)386 public void setFirstDayOfWeek(int firstDayOfWeek) { 387 if (firstDayOfWeek < Calendar.SUNDAY || firstDayOfWeek > Calendar.SATURDAY) { 388 throw new IllegalArgumentException("firstDayOfWeek must be between 1 and 7"); 389 } 390 mDelegate.setFirstDayOfWeek(firstDayOfWeek); 391 } 392 393 /** 394 * Gets the first day of week. 395 * 396 * @return The first day of the week conforming to the {@link CalendarView} 397 * APIs. 398 * @see Calendar#SUNDAY 399 * @see Calendar#MONDAY 400 * @see Calendar#TUESDAY 401 * @see Calendar#WEDNESDAY 402 * @see Calendar#THURSDAY 403 * @see Calendar#FRIDAY 404 * @see Calendar#SATURDAY 405 * 406 * @attr ref android.R.styleable#DatePicker_firstDayOfWeek 407 */ getFirstDayOfWeek()408 public int getFirstDayOfWeek() { 409 return mDelegate.getFirstDayOfWeek(); 410 } 411 412 /** 413 * Returns whether the {@link CalendarView} is shown. 414 * <p> 415 * <strong>Note:</strong> This method returns {@code false} when the 416 * {@link android.R.styleable#DatePicker_datePickerMode} attribute is set 417 * to {@code calendar}. 418 * 419 * @return {@code true} if the calendar view is shown 420 * @see #getCalendarView() 421 * @deprecated Not supported by Material-style {@code calendar} mode 422 */ 423 @Deprecated getCalendarViewShown()424 public boolean getCalendarViewShown() { 425 return mDelegate.getCalendarViewShown(); 426 } 427 428 /** 429 * Returns the {@link CalendarView} used by this picker. 430 * <p> 431 * <strong>Note:</strong> This method throws an 432 * {@link UnsupportedOperationException} when the 433 * {@link android.R.styleable#DatePicker_datePickerMode} attribute is set 434 * to {@code calendar}. 435 * 436 * @return the calendar view 437 * @see #getCalendarViewShown() 438 * @deprecated Not supported by Material-style {@code calendar} mode 439 * @throws UnsupportedOperationException if called when the picker is 440 * displayed in {@code calendar} mode 441 */ 442 @Deprecated getCalendarView()443 public CalendarView getCalendarView() { 444 return mDelegate.getCalendarView(); 445 } 446 447 /** 448 * Sets whether the {@link CalendarView} is shown. 449 * <p> 450 * <strong>Note:</strong> Calling this method has no effect when the 451 * {@link android.R.styleable#DatePicker_datePickerMode} attribute is set 452 * to {@code calendar}. 453 * 454 * @param shown {@code true} to show the calendar view, {@code false} to 455 * hide it 456 * @deprecated Not supported by Material-style {@code calendar} mode 457 */ 458 @Deprecated setCalendarViewShown(boolean shown)459 public void setCalendarViewShown(boolean shown) { 460 mDelegate.setCalendarViewShown(shown); 461 } 462 463 /** 464 * Returns whether the spinners are shown. 465 * <p> 466 * <strong>Note:</strong> his method returns {@code false} when the 467 * {@link android.R.styleable#DatePicker_datePickerMode} attribute is set 468 * to {@code calendar}. 469 * 470 * @return {@code true} if the spinners are shown 471 * @deprecated Not supported by Material-style {@code calendar} mode 472 */ 473 @Deprecated getSpinnersShown()474 public boolean getSpinnersShown() { 475 return mDelegate.getSpinnersShown(); 476 } 477 478 /** 479 * Sets whether the spinners are shown. 480 * <p> 481 * Calling this method has no effect when the 482 * {@link android.R.styleable#DatePicker_datePickerMode} attribute is set 483 * to {@code calendar}. 484 * 485 * @param shown {@code true} to show the spinners, {@code false} to hide 486 * them 487 * @deprecated Not supported by Material-style {@code calendar} mode 488 */ 489 @Deprecated setSpinnersShown(boolean shown)490 public void setSpinnersShown(boolean shown) { 491 mDelegate.setSpinnersShown(shown); 492 } 493 494 @Override dispatchRestoreInstanceState(SparseArray<Parcelable> container)495 protected void dispatchRestoreInstanceState(SparseArray<Parcelable> container) { 496 dispatchThawSelfOnly(container); 497 } 498 499 @Override onSaveInstanceState()500 protected Parcelable onSaveInstanceState() { 501 Parcelable superState = super.onSaveInstanceState(); 502 return mDelegate.onSaveInstanceState(superState); 503 } 504 505 @Override onRestoreInstanceState(Parcelable state)506 protected void onRestoreInstanceState(Parcelable state) { 507 BaseSavedState ss = (BaseSavedState) state; 508 super.onRestoreInstanceState(ss.getSuperState()); 509 mDelegate.onRestoreInstanceState(ss); 510 } 511 512 /** 513 * A delegate interface that defined the public API of the DatePicker. Allows different 514 * DatePicker implementations. This would need to be implemented by the DatePicker delegates 515 * for the real behavior. 516 * 517 * @hide 518 */ 519 interface DatePickerDelegate { init(int year, int monthOfYear, int dayOfMonth, OnDateChangedListener onDateChangedListener)520 void init(int year, int monthOfYear, int dayOfMonth, 521 OnDateChangedListener onDateChangedListener); 522 setOnDateChangedListener(OnDateChangedListener onDateChangedListener)523 void setOnDateChangedListener(OnDateChangedListener onDateChangedListener); setAutoFillChangeListener(OnDateChangedListener onDateChangedListener)524 void setAutoFillChangeListener(OnDateChangedListener onDateChangedListener); 525 updateDate(int year, int month, int dayOfMonth)526 void updateDate(int year, int month, int dayOfMonth); updateDate(long date)527 void updateDate(long date); 528 getYear()529 int getYear(); getMonth()530 int getMonth(); getDayOfMonth()531 int getDayOfMonth(); getDate()532 long getDate(); 533 setFirstDayOfWeek(int firstDayOfWeek)534 void setFirstDayOfWeek(int firstDayOfWeek); getFirstDayOfWeek()535 int getFirstDayOfWeek(); 536 setMinDate(long minDate)537 void setMinDate(long minDate); getMinDate()538 Calendar getMinDate(); 539 setMaxDate(long maxDate)540 void setMaxDate(long maxDate); getMaxDate()541 Calendar getMaxDate(); 542 setEnabled(boolean enabled)543 void setEnabled(boolean enabled); isEnabled()544 boolean isEnabled(); 545 getCalendarView()546 CalendarView getCalendarView(); 547 setCalendarViewShown(boolean shown)548 void setCalendarViewShown(boolean shown); getCalendarViewShown()549 boolean getCalendarViewShown(); 550 setSpinnersShown(boolean shown)551 void setSpinnersShown(boolean shown); getSpinnersShown()552 boolean getSpinnersShown(); 553 setValidationCallback(ValidationCallback callback)554 void setValidationCallback(ValidationCallback callback); 555 onConfigurationChanged(Configuration newConfig)556 void onConfigurationChanged(Configuration newConfig); 557 onSaveInstanceState(Parcelable superState)558 Parcelable onSaveInstanceState(Parcelable superState); onRestoreInstanceState(Parcelable state)559 void onRestoreInstanceState(Parcelable state); 560 dispatchPopulateAccessibilityEvent(AccessibilityEvent event)561 boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event); onPopulateAccessibilityEvent(AccessibilityEvent event)562 void onPopulateAccessibilityEvent(AccessibilityEvent event); 563 } 564 565 /** 566 * An abstract class which can be used as a start for DatePicker implementations 567 */ 568 abstract static class AbstractDatePickerDelegate implements DatePickerDelegate { 569 // The delegator 570 protected DatePicker mDelegator; 571 572 // The context 573 protected Context mContext; 574 575 protected Calendar mCurrentDate; 576 577 // The current locale 578 protected Locale mCurrentLocale; 579 580 // Callbacks 581 protected OnDateChangedListener mOnDateChangedListener; 582 protected OnDateChangedListener mAutoFillChangeListener; 583 protected ValidationCallback mValidationCallback; 584 AbstractDatePickerDelegate(DatePicker delegator, Context context)585 public AbstractDatePickerDelegate(DatePicker delegator, Context context) { 586 mDelegator = delegator; 587 mContext = context; 588 589 setCurrentLocale(Locale.getDefault()); 590 } 591 setCurrentLocale(Locale locale)592 protected void setCurrentLocale(Locale locale) { 593 if (!locale.equals(mCurrentLocale)) { 594 mCurrentLocale = locale; 595 onLocaleChanged(locale); 596 } 597 } 598 599 @Override setOnDateChangedListener(OnDateChangedListener callback)600 public void setOnDateChangedListener(OnDateChangedListener callback) { 601 mOnDateChangedListener = callback; 602 } 603 604 @Override setAutoFillChangeListener(OnDateChangedListener callback)605 public void setAutoFillChangeListener(OnDateChangedListener callback) { 606 mAutoFillChangeListener = callback; 607 } 608 609 @Override setValidationCallback(ValidationCallback callback)610 public void setValidationCallback(ValidationCallback callback) { 611 mValidationCallback = callback; 612 } 613 614 @Override updateDate(long date)615 public void updateDate(long date) { 616 Calendar cal = Calendar.getInstance(mCurrentLocale); 617 cal.setTimeInMillis(date); 618 updateDate(cal.get(Calendar.YEAR), cal.get(Calendar.MONTH), 619 cal.get(Calendar.DAY_OF_MONTH)); 620 } 621 622 @Override getDate()623 public long getDate() { 624 return mCurrentDate.getTimeInMillis(); 625 } 626 onValidationChanged(boolean valid)627 protected void onValidationChanged(boolean valid) { 628 if (mValidationCallback != null) { 629 mValidationCallback.onValidationChanged(valid); 630 } 631 } 632 onLocaleChanged(Locale locale)633 protected void onLocaleChanged(Locale locale) { 634 // Stub. 635 } 636 637 @Override onPopulateAccessibilityEvent(AccessibilityEvent event)638 public void onPopulateAccessibilityEvent(AccessibilityEvent event) { 639 event.getText().add(getFormattedCurrentDate()); 640 } 641 getFormattedCurrentDate()642 protected String getFormattedCurrentDate() { 643 return DateUtils.formatDateTime(mContext, mCurrentDate.getTimeInMillis(), 644 DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_SHOW_YEAR 645 | DateUtils.FORMAT_SHOW_WEEKDAY); 646 } 647 648 /** 649 * Class for managing state storing/restoring. 650 */ 651 static class SavedState extends View.BaseSavedState { 652 private final int mSelectedYear; 653 private final int mSelectedMonth; 654 private final int mSelectedDay; 655 private final long mMinDate; 656 private final long mMaxDate; 657 private final int mCurrentView; 658 private final int mListPosition; 659 private final int mListPositionOffset; 660 SavedState(Parcelable superState, int year, int month, int day, long minDate, long maxDate)661 public SavedState(Parcelable superState, int year, int month, int day, long minDate, 662 long maxDate) { 663 this(superState, year, month, day, minDate, maxDate, 0, 0, 0); 664 } 665 666 /** 667 * Constructor called from {@link DatePicker#onSaveInstanceState()} 668 */ SavedState(Parcelable superState, int year, int month, int day, long minDate, long maxDate, int currentView, int listPosition, int listPositionOffset)669 public SavedState(Parcelable superState, int year, int month, int day, long minDate, 670 long maxDate, int currentView, int listPosition, int listPositionOffset) { 671 super(superState); 672 mSelectedYear = year; 673 mSelectedMonth = month; 674 mSelectedDay = day; 675 mMinDate = minDate; 676 mMaxDate = maxDate; 677 mCurrentView = currentView; 678 mListPosition = listPosition; 679 mListPositionOffset = listPositionOffset; 680 } 681 682 /** 683 * Constructor called from {@link #CREATOR} 684 */ SavedState(Parcel in)685 private SavedState(Parcel in) { 686 super(in); 687 mSelectedYear = in.readInt(); 688 mSelectedMonth = in.readInt(); 689 mSelectedDay = in.readInt(); 690 mMinDate = in.readLong(); 691 mMaxDate = in.readLong(); 692 mCurrentView = in.readInt(); 693 mListPosition = in.readInt(); 694 mListPositionOffset = in.readInt(); 695 } 696 697 @Override writeToParcel(Parcel dest, int flags)698 public void writeToParcel(Parcel dest, int flags) { 699 super.writeToParcel(dest, flags); 700 dest.writeInt(mSelectedYear); 701 dest.writeInt(mSelectedMonth); 702 dest.writeInt(mSelectedDay); 703 dest.writeLong(mMinDate); 704 dest.writeLong(mMaxDate); 705 dest.writeInt(mCurrentView); 706 dest.writeInt(mListPosition); 707 dest.writeInt(mListPositionOffset); 708 } 709 getSelectedDay()710 public int getSelectedDay() { 711 return mSelectedDay; 712 } 713 getSelectedMonth()714 public int getSelectedMonth() { 715 return mSelectedMonth; 716 } 717 getSelectedYear()718 public int getSelectedYear() { 719 return mSelectedYear; 720 } 721 getMinDate()722 public long getMinDate() { 723 return mMinDate; 724 } 725 getMaxDate()726 public long getMaxDate() { 727 return mMaxDate; 728 } 729 getCurrentView()730 public int getCurrentView() { 731 return mCurrentView; 732 } 733 getListPosition()734 public int getListPosition() { 735 return mListPosition; 736 } 737 getListPositionOffset()738 public int getListPositionOffset() { 739 return mListPositionOffset; 740 } 741 742 @SuppressWarnings("all") 743 // suppress unused and hiding 744 public static final Parcelable.Creator<SavedState> CREATOR = new Creator<SavedState>() { 745 746 public SavedState createFromParcel(Parcel in) { 747 return new SavedState(in); 748 } 749 750 public SavedState[] newArray(int size) { 751 return new SavedState[size]; 752 } 753 }; 754 } 755 } 756 757 /** 758 * A callback interface for updating input validity when the date picker 759 * when included into a dialog. 760 * 761 * @hide 762 */ 763 public interface ValidationCallback { onValidationChanged(boolean valid)764 void onValidationChanged(boolean valid); 765 } 766 767 @Override dispatchProvideAutofillStructure(ViewStructure structure, int flags)768 public void dispatchProvideAutofillStructure(ViewStructure structure, int flags) { 769 // This view is self-sufficient for autofill, so it needs to call 770 // onProvideAutoFillStructure() to fill itself, but it does not need to call 771 // dispatchProvideAutoFillStructure() to fill its children. 772 structure.setAutofillId(getAutofillId()); 773 onProvideAutofillStructure(structure, flags); 774 } 775 776 @Override autofill(AutofillValue value)777 public void autofill(AutofillValue value) { 778 if (!isEnabled()) return; 779 780 if (!value.isDate()) { 781 Log.w(LOG_TAG, value + " could not be autofilled into " + this); 782 return; 783 } 784 785 mDelegate.updateDate(value.getDateValue()); 786 } 787 788 @Override getAutofillType()789 public @AutofillType int getAutofillType() { 790 return isEnabled() ? AUTOFILL_TYPE_DATE : AUTOFILL_TYPE_NONE; 791 } 792 793 @Override getAutofillValue()794 public AutofillValue getAutofillValue() { 795 return isEnabled() ? AutofillValue.forDate(mDelegate.getDate()) : null; 796 } 797 } 798