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