blob: ada4f00256eb2abf7e9dd4fdb126f4cc8a3cf1d1 [file] [log] [blame]
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001/*
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
17package android.widget;
18
Alan Viverette8817aa92016-09-22 11:16:22 -040019import android.annotation.IntDef;
Alan Viverette518ff0d2014-08-15 14:20:35 -070020import android.annotation.Nullable;
Alan Viverette8817aa92016-09-22 11:16:22 -040021import android.annotation.TestApi;
Mathew Inwood978c6e22018-08-21 15:58:55 +010022import android.annotation.UnsupportedAppUsage;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080023import android.annotation.Widget;
24import android.content.Context;
Svetoslav Ganovf5926962011-07-12 12:26:20 -070025import android.content.res.Configuration;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080026import android.content.res.TypedArray;
Alan Viverette68763be2016-05-25 11:42:42 -040027import android.icu.util.Calendar;
28import android.icu.util.TimeZone;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080029import android.os.Parcel;
30import android.os.Parcelable;
Andrei Stingaceanudd80f002016-06-16 12:00:39 +010031import android.text.format.DateUtils;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080032import android.util.AttributeSet;
Philip P. Moltmann96689032017-03-09 13:19:55 -080033import android.util.Log;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080034import android.util.SparseArray;
Svetoslav Ganovd11e6152012-03-20 12:13:02 -070035import android.view.View;
Felipe Leme305b72c2017-02-27 12:46:04 -080036import android.view.ViewStructure;
Svetoslav Ganov8a2a8952011-01-27 17:40:13 -080037import android.view.accessibility.AccessibilityEvent;
Felipe Leme640f30a2017-03-06 15:44:06 -080038import android.view.autofill.AutofillManager;
39import android.view.autofill.AutofillValue;
Ashley Rose55f9f922019-01-28 19:29:36 -050040import android.view.inspector.InspectableProperty;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080041
Aurimas Liutikas99441c52016-10-11 16:48:32 -070042import com.android.internal.R;
43
Alan Viverette8817aa92016-09-22 11:16:22 -040044import java.lang.annotation.Retention;
45import java.lang.annotation.RetentionPolicy;
Kenny Rootdddda8d2010-11-15 14:38:51 -080046import java.util.Locale;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080047
48/**
Alan Viveretted25eb9f2014-11-24 14:07:24 -080049 * Provides a widget for selecting a date.
50 * <p>
51 * When the {@link android.R.styleable#DatePicker_datePickerMode} attribute is
52 * set to {@code spinner}, the date can be selected using year, month, and day
53 * spinners or a {@link CalendarView}. The set of spinners and the calendar
54 * view are automatically synchronized. The client can customize whether only
55 * the spinners, or only the calendar view, or both to be displayed.
56 * </p>
57 * <p>
58 * When the {@link android.R.styleable#DatePicker_datePickerMode} attribute is
59 * set to {@code calendar}, the month and day can be selected using a
60 * calendar-style view while the year can be selected separately using a list.
61 * </p>
Svetoslav Ganov50f34d12010-12-03 16:05:40 -080062 * <p>
Scott Main4c359b72012-07-24 15:51:27 -070063 * See the <a href="{@docRoot}guide/topics/ui/controls/pickers.html">Pickers</a>
64 * guide.
Svetoslav Ganov50f34d12010-12-03 16:05:40 -080065 * </p>
Svetoslav Ganove9730bf2010-12-20 21:25:20 -080066 * <p>
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080067 * For a dialog using this view, see {@link android.app.DatePickerDialog}.
Svetoslav Ganove9730bf2010-12-20 21:25:20 -080068 * </p>
69 *
70 * @attr ref android.R.styleable#DatePicker_startYear
71 * @attr ref android.R.styleable#DatePicker_endYear
72 * @attr ref android.R.styleable#DatePicker_maxDate
73 * @attr ref android.R.styleable#DatePicker_minDate
74 * @attr ref android.R.styleable#DatePicker_spinnersShown
75 * @attr ref android.R.styleable#DatePicker_calendarViewShown
Alan Viveretteba9bf412014-09-03 20:14:21 -070076 * @attr ref android.R.styleable#DatePicker_dayOfWeekBackground
Alan Viverette60727e02014-07-28 16:56:32 -070077 * @attr ref android.R.styleable#DatePicker_dayOfWeekTextAppearance
Alan Viveretteba9bf412014-09-03 20:14:21 -070078 * @attr ref android.R.styleable#DatePicker_headerBackground
Alan Viverette60727e02014-07-28 16:56:32 -070079 * @attr ref android.R.styleable#DatePicker_headerMonthTextAppearance
80 * @attr ref android.R.styleable#DatePicker_headerDayOfMonthTextAppearance
81 * @attr ref android.R.styleable#DatePicker_headerYearTextAppearance
82 * @attr ref android.R.styleable#DatePicker_yearListItemTextAppearance
83 * @attr ref android.R.styleable#DatePicker_yearListSelectorColor
Fabrice Di Megliobd9152f2013-10-01 11:21:31 -070084 * @attr ref android.R.styleable#DatePicker_calendarTextColor
Alan Viveretted25eb9f2014-11-24 14:07:24 -080085 * @attr ref android.R.styleable#DatePicker_datePickerMode
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080086 */
87@Widget
88public class DatePicker extends FrameLayout {
Philip P. Moltmann96689032017-03-09 13:19:55 -080089 private static final String LOG_TAG = DatePicker.class.getSimpleName();
90
Alan Viverette8817aa92016-09-22 11:16:22 -040091 /**
92 * Presentation mode for the Holo-style date picker that uses a set of
93 * {@link android.widget.NumberPicker}s.
94 *
95 * @see #getMode()
96 * @hide Visible for testing only.
97 */
98 @TestApi
99 public static final int MODE_SPINNER = 1;
100
101 /**
102 * Presentation mode for the Material-style date picker that uses a
103 * calendar.
104 *
105 * @see #getMode()
106 * @hide Visible for testing only.
107 */
108 @TestApi
109 public static final int MODE_CALENDAR = 2;
110
111 /** @hide */
Jeff Sharkeyce8db992017-12-13 20:05:05 -0700112 @IntDef(prefix = { "MODE_" }, value = {
113 MODE_SPINNER,
114 MODE_CALENDAR
115 })
Alan Viverette8817aa92016-09-22 11:16:22 -0400116 @Retention(RetentionPolicy.SOURCE)
117 public @interface DatePickerMode {}
Chet Haase3053b2f2014-08-06 07:51:50 -0700118
Mathew Inwood978c6e22018-08-21 15:58:55 +0100119 @UnsupportedAppUsage
Alan Viverette60727e02014-07-28 16:56:32 -0700120 private final DatePickerDelegate mDelegate;
Fabrice Di Megliobd9152f2013-10-01 11:21:31 -0700121
Alan Viverette8817aa92016-09-22 11:16:22 -0400122 @DatePickerMode
123 private final int mMode;
124
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800125 /**
Alan Viverette0ef59ac2015-03-23 13:13:25 -0700126 * The callback used to indicate the user changed the date.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800127 */
128 public interface OnDateChangedListener {
129
130 /**
Svetoslav Ganove9730bf2010-12-20 21:25:20 -0800131 * Called upon a date change.
132 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800133 * @param view The view associated with this listener.
134 * @param year The year that was set.
135 * @param monthOfYear The month that was set (0-11) for compatibility
Svetoslav Ganov50f34d12010-12-03 16:05:40 -0800136 * with {@link java.util.Calendar}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800137 * @param dayOfMonth The day of the month that was set.
138 */
139 void onDateChanged(DatePicker view, int year, int monthOfYear, int dayOfMonth);
140 }
141
142 public DatePicker(Context context) {
143 this(context, null);
144 }
Svetoslav Ganov50f34d12010-12-03 16:05:40 -0800145
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800146 public DatePicker(Context context, AttributeSet attrs) {
Svetoslav Ganov4243dc32011-01-18 19:39:57 -0800147 this(context, attrs, R.attr.datePickerStyle);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800148 }
149
Alan Viverette617feb92013-09-09 18:09:13 -0700150 public DatePicker(Context context, AttributeSet attrs, int defStyleAttr) {
151 this(context, attrs, defStyleAttr, 0);
152 }
153
154 public DatePicker(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
155 super(context, attrs, defStyleAttr, defStyleRes);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800156
Felipe Lemed04a6972017-03-02 12:56:18 -0800157 // DatePicker is important by default, unless app developer overrode attribute.
158 if (getImportantForAutofill() == IMPORTANT_FOR_AUTOFILL_AUTO) {
159 setImportantForAutofill(IMPORTANT_FOR_AUTOFILL_YES);
160 }
161
Alan Viverette60727e02014-07-28 16:56:32 -0700162 final TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.DatePicker,
163 defStyleAttr, defStyleRes);
Aurimas Liutikasab324cf2019-02-07 16:46:38 -0800164 saveAttributeDataForStyleable(context, R.styleable.DatePicker,
165 attrs, a, defStyleAttr, defStyleRes);
Alan Viverette8817aa92016-09-22 11:16:22 -0400166 final boolean isDialogMode = a.getBoolean(R.styleable.DatePicker_dialogMode, false);
167 final int requestedMode = a.getInt(R.styleable.DatePicker_datePickerMode, MODE_SPINNER);
Alan Viverette0a04bb02014-09-03 20:48:02 -0700168 final int firstDayOfWeek = a.getInt(R.styleable.DatePicker_firstDayOfWeek, 0);
Fabrice Di Megliobd9152f2013-10-01 11:21:31 -0700169 a.recycle();
170
Alan Viverette8817aa92016-09-22 11:16:22 -0400171 if (requestedMode == MODE_CALENDAR && isDialogMode) {
172 // You want MODE_CALENDAR? YOU CAN'T HANDLE MODE_CALENDAR! Well,
173 // maybe you can depending on your screen size. Let's check...
174 mMode = context.getResources().getInteger(R.integer.date_picker_mode);
175 } else {
176 mMode = requestedMode;
177 }
178
179 switch (mMode) {
Chet Haase3053b2f2014-08-06 07:51:50 -0700180 case MODE_CALENDAR:
181 mDelegate = createCalendarUIDelegate(context, attrs, defStyleAttr, defStyleRes);
182 break;
183 case MODE_SPINNER:
184 default:
185 mDelegate = createSpinnerUIDelegate(context, attrs, defStyleAttr, defStyleRes);
186 break;
Fabrice Di Megliobd9152f2013-10-01 11:21:31 -0700187 }
Alan Viverette0a04bb02014-09-03 20:48:02 -0700188
189 if (firstDayOfWeek != 0) {
190 setFirstDayOfWeek(firstDayOfWeek);
191 }
Felipe Leme305b72c2017-02-27 12:46:04 -0800192
193 mDelegate.setAutoFillChangeListener((v, y, m, d) -> {
Felipe Leme640f30a2017-03-06 15:44:06 -0800194 final AutofillManager afm = context.getSystemService(AutofillManager.class);
Felipe Leme305b72c2017-02-27 12:46:04 -0800195 if (afm != null) {
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700196 afm.notifyValueChanged(this);
Felipe Leme305b72c2017-02-27 12:46:04 -0800197 }
198 });
Fabrice Di Megliobd9152f2013-10-01 11:21:31 -0700199 }
200
Chet Haase3053b2f2014-08-06 07:51:50 -0700201 private DatePickerDelegate createSpinnerUIDelegate(Context context, AttributeSet attrs,
Fabrice Di Megliobd9152f2013-10-01 11:21:31 -0700202 int defStyleAttr, int defStyleRes) {
Chet Haase3053b2f2014-08-06 07:51:50 -0700203 return new DatePickerSpinnerDelegate(this, context, attrs, defStyleAttr, defStyleRes);
Fabrice Di Megliobd9152f2013-10-01 11:21:31 -0700204 }
205
Chet Haase3053b2f2014-08-06 07:51:50 -0700206 private DatePickerDelegate createCalendarUIDelegate(Context context, AttributeSet attrs,
Fabrice Di Megliobd9152f2013-10-01 11:21:31 -0700207 int defStyleAttr, int defStyleRes) {
Chet Haase3053b2f2014-08-06 07:51:50 -0700208 return new DatePickerCalendarDelegate(this, context, attrs, defStyleAttr,
Fabrice Di Megliobd9152f2013-10-01 11:21:31 -0700209 defStyleRes);
210 }
211
212 /**
Alan Viverette8817aa92016-09-22 11:16:22 -0400213 * @return the picker's presentation mode, one of {@link #MODE_CALENDAR} or
214 * {@link #MODE_SPINNER}
215 * @attr ref android.R.styleable#DatePicker_datePickerMode
216 * @hide Visible for testing only.
217 */
Ashley Rose55f9f922019-01-28 19:29:36 -0500218 @InspectableProperty(name = "datePickerMode", enumMapping = {
219 @InspectableProperty.EnumMap(value = MODE_SPINNER, name = "spinner"),
220 @InspectableProperty.EnumMap(value = MODE_CALENDAR, name = "calendar")
221 })
Alan Viverette8817aa92016-09-22 11:16:22 -0400222 @DatePickerMode
223 @TestApi
224 public int getMode() {
225 return mMode;
226 }
227
228 /**
Svetoslav Ganov50f34d12010-12-03 16:05:40 -0800229 * Initialize the state. If the provided values designate an inconsistent
Svetoslav Ganove9730bf2010-12-20 21:25:20 -0800230 * date the values are normalized before updating the spinners.
Svetoslav Ganov50f34d12010-12-03 16:05:40 -0800231 *
232 * @param year The initial year.
233 * @param monthOfYear The initial month <strong>starting from zero</strong>.
234 * @param dayOfMonth The initial day of the month.
235 * @param onDateChangedListener How user is notified date is changed by
236 * user, can be null.
237 */
238 public void init(int year, int monthOfYear, int dayOfMonth,
Fabrice Di Meglio039a7842013-08-28 17:41:26 -0700239 OnDateChangedListener onDateChangedListener) {
240 mDelegate.init(year, monthOfYear, dayOfMonth, onDateChangedListener);
Svetoslav Ganov50f34d12010-12-03 16:05:40 -0800241 }
242
243 /**
Clara Bayarri68640b62016-06-02 14:56:18 +0100244 * Set the callback that indicates the date has been adjusted by the user.
245 *
246 * @param onDateChangedListener How user is notified date is changed by
247 * user, can be null.
248 */
249 public void setOnDateChangedListener(OnDateChangedListener onDateChangedListener) {
250 mDelegate.setOnDateChangedListener(onDateChangedListener);
251 }
252
253 /**
Fabrice Di Megliobd9152f2013-10-01 11:21:31 -0700254 * Update the current date.
Svetoslav Ganova911d4a2010-12-08 16:11:30 -0800255 *
Fabrice Di Meglio039a7842013-08-28 17:41:26 -0700256 * @param year The year.
257 * @param month The month which is <strong>starting from zero</strong>.
258 * @param dayOfMonth The day of the month.
Svetoslav Ganova911d4a2010-12-08 16:11:30 -0800259 */
Fabrice Di Meglio039a7842013-08-28 17:41:26 -0700260 public void updateDate(int year, int month, int dayOfMonth) {
261 mDelegate.updateDate(year, month, dayOfMonth);
Svetoslav Ganov50f34d12010-12-03 16:05:40 -0800262 }
263
264 /**
265 * @return The selected year.
266 */
Ashley Rose55f9f922019-01-28 19:29:36 -0500267 @InspectableProperty(hasAttributeId = false)
Svetoslav Ganov50f34d12010-12-03 16:05:40 -0800268 public int getYear() {
Fabrice Di Meglio039a7842013-08-28 17:41:26 -0700269 return mDelegate.getYear();
Svetoslav Ganov50f34d12010-12-03 16:05:40 -0800270 }
271
272 /**
273 * @return The selected month.
274 */
Ashley Rose55f9f922019-01-28 19:29:36 -0500275 @InspectableProperty(hasAttributeId = false)
Svetoslav Ganov50f34d12010-12-03 16:05:40 -0800276 public int getMonth() {
Fabrice Di Meglio039a7842013-08-28 17:41:26 -0700277 return mDelegate.getMonth();
Svetoslav Ganov50f34d12010-12-03 16:05:40 -0800278 }
279
280 /**
281 * @return The selected day of month.
282 */
Ashley Rose55f9f922019-01-28 19:29:36 -0500283 @InspectableProperty(hasAttributeId = false)
Svetoslav Ganov50f34d12010-12-03 16:05:40 -0800284 public int getDayOfMonth() {
Fabrice Di Meglio039a7842013-08-28 17:41:26 -0700285 return mDelegate.getDayOfMonth();
Svetoslav Ganov50f34d12010-12-03 16:05:40 -0800286 }
287
288 /**
Fabrice Di Meglio039a7842013-08-28 17:41:26 -0700289 * Gets the minimal date supported by this {@link DatePicker} in
290 * milliseconds since January 1, 1970 00:00:00 in
291 * {@link TimeZone#getDefault()} time zone.
292 * <p>
293 * Note: The default minimal date is 01/01/1900.
294 * <p>
Svetoslav Ganova53efe92011-09-08 18:08:36 -0700295 *
Fabrice Di Meglio039a7842013-08-28 17:41:26 -0700296 * @return The minimal supported date.
Svetoslav Ganova53efe92011-09-08 18:08:36 -0700297 */
Ashley Rose55f9f922019-01-28 19:29:36 -0500298 @InspectableProperty
Fabrice Di Meglio039a7842013-08-28 17:41:26 -0700299 public long getMinDate() {
Fabrice Di Megliobd9152f2013-10-01 11:21:31 -0700300 return mDelegate.getMinDate().getTimeInMillis();
Fabrice Di Meglio039a7842013-08-28 17:41:26 -0700301 }
302
303 /**
304 * Sets the minimal date supported by this {@link NumberPicker} in
305 * milliseconds since January 1, 1970 00:00:00 in
306 * {@link TimeZone#getDefault()} time zone.
307 *
308 * @param minDate The minimal supported date.
309 */
310 public void setMinDate(long minDate) {
311 mDelegate.setMinDate(minDate);
312 }
313
314 /**
315 * Gets the maximal date supported by this {@link DatePicker} in
316 * milliseconds since January 1, 1970 00:00:00 in
317 * {@link TimeZone#getDefault()} time zone.
318 * <p>
319 * Note: The default maximal date is 12/31/2100.
320 * <p>
321 *
322 * @return The maximal supported date.
323 */
Ashley Rose55f9f922019-01-28 19:29:36 -0500324 @InspectableProperty
Fabrice Di Meglio039a7842013-08-28 17:41:26 -0700325 public long getMaxDate() {
Fabrice Di Megliobd9152f2013-10-01 11:21:31 -0700326 return mDelegate.getMaxDate().getTimeInMillis();
Fabrice Di Meglio039a7842013-08-28 17:41:26 -0700327 }
328
329 /**
330 * Sets the maximal date supported by this {@link DatePicker} in
331 * milliseconds since January 1, 1970 00:00:00 in
332 * {@link TimeZone#getDefault()} time zone.
333 *
334 * @param maxDate The maximal supported date.
335 */
336 public void setMaxDate(long maxDate) {
337 mDelegate.setMaxDate(maxDate);
338 }
339
Alan Viverette518ff0d2014-08-15 14:20:35 -0700340 /**
341 * Sets the callback that indicates the current date is valid.
342 *
343 * @param callback the callback, may be null
344 * @hide
345 */
Mathew Inwood978c6e22018-08-21 15:58:55 +0100346 @UnsupportedAppUsage
Alan Viverette518ff0d2014-08-15 14:20:35 -0700347 public void setValidationCallback(@Nullable ValidationCallback callback) {
348 mDelegate.setValidationCallback(callback);
349 }
350
Fabrice Di Meglio039a7842013-08-28 17:41:26 -0700351 @Override
352 public void setEnabled(boolean enabled) {
353 if (mDelegate.isEnabled() == enabled) {
354 return;
Svetoslav Ganova53efe92011-09-08 18:08:36 -0700355 }
Fabrice Di Meglio039a7842013-08-28 17:41:26 -0700356 super.setEnabled(enabled);
357 mDelegate.setEnabled(enabled);
Svetoslav Ganova53efe92011-09-08 18:08:36 -0700358 }
359
Fabrice Di Meglio039a7842013-08-28 17:41:26 -0700360 @Override
361 public boolean isEnabled() {
362 return mDelegate.isEnabled();
Svetoslav Ganovd11e6152012-03-20 12:13:02 -0700363 }
364
Alan Viverettea54956a2015-01-07 16:05:02 -0800365 /** @hide */
Fabrice Di Meglio039a7842013-08-28 17:41:26 -0700366 @Override
Alan Viverettea54956a2015-01-07 16:05:02 -0800367 public boolean dispatchPopulateAccessibilityEventInternal(AccessibilityEvent event) {
Fabrice Di Meglio039a7842013-08-28 17:41:26 -0700368 return mDelegate.dispatchPopulateAccessibilityEvent(event);
369 }
370
Alan Viverettea54956a2015-01-07 16:05:02 -0800371 /** @hide */
Fabrice Di Meglio039a7842013-08-28 17:41:26 -0700372 @Override
Alan Viverettea54956a2015-01-07 16:05:02 -0800373 public void onPopulateAccessibilityEventInternal(AccessibilityEvent event) {
374 super.onPopulateAccessibilityEventInternal(event);
Fabrice Di Meglio039a7842013-08-28 17:41:26 -0700375 mDelegate.onPopulateAccessibilityEvent(event);
376 }
377
378 @Override
Dianne Hackborna7bb6fb2015-02-03 18:13:40 -0800379 public CharSequence getAccessibilityClassName() {
380 return DatePicker.class.getName();
Fabrice Di Meglio039a7842013-08-28 17:41:26 -0700381 }
382
383 @Override
384 protected void onConfigurationChanged(Configuration newConfig) {
385 super.onConfigurationChanged(newConfig);
386 mDelegate.onConfigurationChanged(newConfig);
387 }
388
389 /**
Alan Viverette0a04bb02014-09-03 20:48:02 -0700390 * Sets the first day of week.
391 *
392 * @param firstDayOfWeek The first day of the week conforming to the
393 * {@link CalendarView} APIs.
394 * @see Calendar#SUNDAY
395 * @see Calendar#MONDAY
396 * @see Calendar#TUESDAY
397 * @see Calendar#WEDNESDAY
398 * @see Calendar#THURSDAY
399 * @see Calendar#FRIDAY
400 * @see Calendar#SATURDAY
401 *
402 * @attr ref android.R.styleable#DatePicker_firstDayOfWeek
403 */
404 public void setFirstDayOfWeek(int firstDayOfWeek) {
405 if (firstDayOfWeek < Calendar.SUNDAY || firstDayOfWeek > Calendar.SATURDAY) {
406 throw new IllegalArgumentException("firstDayOfWeek must be between 1 and 7");
407 }
408 mDelegate.setFirstDayOfWeek(firstDayOfWeek);
409 }
410
411 /**
412 * Gets the first day of week.
413 *
414 * @return The first day of the week conforming to the {@link CalendarView}
415 * APIs.
416 * @see Calendar#SUNDAY
417 * @see Calendar#MONDAY
418 * @see Calendar#TUESDAY
419 * @see Calendar#WEDNESDAY
420 * @see Calendar#THURSDAY
421 * @see Calendar#FRIDAY
422 * @see Calendar#SATURDAY
423 *
424 * @attr ref android.R.styleable#DatePicker_firstDayOfWeek
425 */
Ashley Rose55f9f922019-01-28 19:29:36 -0500426 @InspectableProperty
Alan Viverette0a04bb02014-09-03 20:48:02 -0700427 public int getFirstDayOfWeek() {
428 return mDelegate.getFirstDayOfWeek();
429 }
430
431 /**
Alan Viverette452fe342015-03-23 14:26:09 -0700432 * Returns whether the {@link CalendarView} is shown.
433 * <p>
434 * <strong>Note:</strong> This method returns {@code false} when the
435 * {@link android.R.styleable#DatePicker_datePickerMode} attribute is set
436 * to {@code calendar}.
Fabrice Di Meglio039a7842013-08-28 17:41:26 -0700437 *
Alan Viverette452fe342015-03-23 14:26:09 -0700438 * @return {@code true} if the calendar view is shown
Fabrice Di Meglio039a7842013-08-28 17:41:26 -0700439 * @see #getCalendarView()
Alan Viverette0e672142016-04-06 15:56:11 -0400440 * @deprecated Not supported by Material-style {@code calendar} mode
Fabrice Di Meglio039a7842013-08-28 17:41:26 -0700441 */
Ashley Rose55f9f922019-01-28 19:29:36 -0500442 @InspectableProperty
Alan Viverette0e672142016-04-06 15:56:11 -0400443 @Deprecated
Fabrice Di Meglio039a7842013-08-28 17:41:26 -0700444 public boolean getCalendarViewShown() {
445 return mDelegate.getCalendarViewShown();
446 }
447
448 /**
Alan Viverette452fe342015-03-23 14:26:09 -0700449 * Returns the {@link CalendarView} used by this picker.
Alan Viveretted25eb9f2014-11-24 14:07:24 -0800450 * <p>
Alan Viverette0e672142016-04-06 15:56:11 -0400451 * <strong>Note:</strong> This method throws an
452 * {@link UnsupportedOperationException} when the
Alan Viveretted25eb9f2014-11-24 14:07:24 -0800453 * {@link android.R.styleable#DatePicker_datePickerMode} attribute is set
454 * to {@code calendar}.
Fabrice Di Meglio039a7842013-08-28 17:41:26 -0700455 *
Alan Viverette452fe342015-03-23 14:26:09 -0700456 * @return the calendar view
Fabrice Di Meglio039a7842013-08-28 17:41:26 -0700457 * @see #getCalendarViewShown()
Alan Viverette0e672142016-04-06 15:56:11 -0400458 * @deprecated Not supported by Material-style {@code calendar} mode
459 * @throws UnsupportedOperationException if called when the picker is
460 * displayed in {@code calendar} mode
Fabrice Di Meglio039a7842013-08-28 17:41:26 -0700461 */
Alan Viverette0e672142016-04-06 15:56:11 -0400462 @Deprecated
Alan Viverette0a04bb02014-09-03 20:48:02 -0700463 public CalendarView getCalendarView() {
Fabrice Di Meglio039a7842013-08-28 17:41:26 -0700464 return mDelegate.getCalendarView();
465 }
466
467 /**
468 * Sets whether the {@link CalendarView} is shown.
Alan Viveretted25eb9f2014-11-24 14:07:24 -0800469 * <p>
Alan Viverette452fe342015-03-23 14:26:09 -0700470 * <strong>Note:</strong> Calling this method has no effect when the
Alan Viveretted25eb9f2014-11-24 14:07:24 -0800471 * {@link android.R.styleable#DatePicker_datePickerMode} attribute is set
472 * to {@code calendar}.
Fabrice Di Meglio039a7842013-08-28 17:41:26 -0700473 *
Alan Viverette452fe342015-03-23 14:26:09 -0700474 * @param shown {@code true} to show the calendar view, {@code false} to
475 * hide it
Alan Viverette0e672142016-04-06 15:56:11 -0400476 * @deprecated Not supported by Material-style {@code calendar} mode
Fabrice Di Meglio039a7842013-08-28 17:41:26 -0700477 */
Alan Viverette0e672142016-04-06 15:56:11 -0400478 @Deprecated
Fabrice Di Meglio039a7842013-08-28 17:41:26 -0700479 public void setCalendarViewShown(boolean shown) {
480 mDelegate.setCalendarViewShown(shown);
481 }
482
483 /**
Alan Viverette452fe342015-03-23 14:26:09 -0700484 * Returns whether the spinners are shown.
485 * <p>
Alan Viverette835d7982015-03-23 14:54:14 -0700486 * <strong>Note:</strong> his method returns {@code false} when the
Alan Viverette452fe342015-03-23 14:26:09 -0700487 * {@link android.R.styleable#DatePicker_datePickerMode} attribute is set
488 * to {@code calendar}.
Fabrice Di Meglio039a7842013-08-28 17:41:26 -0700489 *
Alan Viverette452fe342015-03-23 14:26:09 -0700490 * @return {@code true} if the spinners are shown
Alan Viverette0e672142016-04-06 15:56:11 -0400491 * @deprecated Not supported by Material-style {@code calendar} mode
Fabrice Di Meglio039a7842013-08-28 17:41:26 -0700492 */
Ashley Rose55f9f922019-01-28 19:29:36 -0500493 @InspectableProperty
Alan Viverette0e672142016-04-06 15:56:11 -0400494 @Deprecated
Fabrice Di Meglio039a7842013-08-28 17:41:26 -0700495 public boolean getSpinnersShown() {
496 return mDelegate.getSpinnersShown();
497 }
498
499 /**
500 * Sets whether the spinners are shown.
Alan Viverette452fe342015-03-23 14:26:09 -0700501 * <p>
502 * Calling this method has no effect when the
503 * {@link android.R.styleable#DatePicker_datePickerMode} attribute is set
504 * to {@code calendar}.
Fabrice Di Meglio039a7842013-08-28 17:41:26 -0700505 *
Alan Viverette452fe342015-03-23 14:26:09 -0700506 * @param shown {@code true} to show the spinners, {@code false} to hide
507 * them
Alan Viverette0e672142016-04-06 15:56:11 -0400508 * @deprecated Not supported by Material-style {@code calendar} mode
Fabrice Di Meglio039a7842013-08-28 17:41:26 -0700509 */
Alan Viverette0e672142016-04-06 15:56:11 -0400510 @Deprecated
Fabrice Di Meglio039a7842013-08-28 17:41:26 -0700511 public void setSpinnersShown(boolean shown) {
512 mDelegate.setSpinnersShown(shown);
513 }
514
Fabrice Di Meglio039a7842013-08-28 17:41:26 -0700515 @Override
516 protected void dispatchRestoreInstanceState(SparseArray<Parcelable> container) {
Alan Viveretted015e342014-09-15 19:27:55 -0700517 dispatchThawSelfOnly(container);
Fabrice Di Meglio039a7842013-08-28 17:41:26 -0700518 }
519
520 @Override
521 protected Parcelable onSaveInstanceState() {
522 Parcelable superState = super.onSaveInstanceState();
523 return mDelegate.onSaveInstanceState(superState);
524 }
525
526 @Override
527 protected void onRestoreInstanceState(Parcelable state) {
Craig Mautnera67d9092014-09-16 15:38:47 -0700528 BaseSavedState ss = (BaseSavedState) state;
Fabrice Di Meglio039a7842013-08-28 17:41:26 -0700529 super.onRestoreInstanceState(ss.getSuperState());
530 mDelegate.onRestoreInstanceState(ss);
531 }
532
533 /**
534 * A delegate interface that defined the public API of the DatePicker. Allows different
535 * DatePicker implementations. This would need to be implemented by the DatePicker delegates
536 * for the real behavior.
Fabrice Di Megliobd9152f2013-10-01 11:21:31 -0700537 *
538 * @hide
Fabrice Di Meglio039a7842013-08-28 17:41:26 -0700539 */
540 interface DatePickerDelegate {
541 void init(int year, int monthOfYear, int dayOfMonth,
542 OnDateChangedListener onDateChangedListener);
543
Clara Bayarri68640b62016-06-02 14:56:18 +0100544 void setOnDateChangedListener(OnDateChangedListener onDateChangedListener);
Felipe Leme305b72c2017-02-27 12:46:04 -0800545 void setAutoFillChangeListener(OnDateChangedListener onDateChangedListener);
Clara Bayarri68640b62016-06-02 14:56:18 +0100546
Fabrice Di Meglio039a7842013-08-28 17:41:26 -0700547 void updateDate(int year, int month, int dayOfMonth);
548
549 int getYear();
550 int getMonth();
551 int getDayOfMonth();
Felipe Lemef480e8c2017-08-10 18:38:44 -0700552
553 void autofill(AutofillValue value);
554 AutofillValue getAutofillValue();
Fabrice Di Meglio039a7842013-08-28 17:41:26 -0700555
Alan Viverette0a04bb02014-09-03 20:48:02 -0700556 void setFirstDayOfWeek(int firstDayOfWeek);
557 int getFirstDayOfWeek();
558
Fabrice Di Meglio039a7842013-08-28 17:41:26 -0700559 void setMinDate(long minDate);
Fabrice Di Megliobd9152f2013-10-01 11:21:31 -0700560 Calendar getMinDate();
Fabrice Di Meglio039a7842013-08-28 17:41:26 -0700561
562 void setMaxDate(long maxDate);
Fabrice Di Megliobd9152f2013-10-01 11:21:31 -0700563 Calendar getMaxDate();
Fabrice Di Meglio039a7842013-08-28 17:41:26 -0700564
565 void setEnabled(boolean enabled);
566 boolean isEnabled();
567
Fabrice Di Megliobd9152f2013-10-01 11:21:31 -0700568 CalendarView getCalendarView();
Fabrice Di Meglio039a7842013-08-28 17:41:26 -0700569
570 void setCalendarViewShown(boolean shown);
571 boolean getCalendarViewShown();
572
573 void setSpinnersShown(boolean shown);
574 boolean getSpinnersShown();
575
Alan Viverette518ff0d2014-08-15 14:20:35 -0700576 void setValidationCallback(ValidationCallback callback);
Fabrice Di Megliobd9152f2013-10-01 11:21:31 -0700577
Fabrice Di Meglio039a7842013-08-28 17:41:26 -0700578 void onConfigurationChanged(Configuration newConfig);
579
Fabrice Di Meglio039a7842013-08-28 17:41:26 -0700580 Parcelable onSaveInstanceState(Parcelable superState);
581 void onRestoreInstanceState(Parcelable state);
582
583 boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event);
584 void onPopulateAccessibilityEvent(AccessibilityEvent event);
Fabrice Di Meglio039a7842013-08-28 17:41:26 -0700585 }
586
587 /**
588 * An abstract class which can be used as a start for DatePicker implementations
589 */
Fabrice Di Megliobd9152f2013-10-01 11:21:31 -0700590 abstract static class AbstractDatePickerDelegate implements DatePickerDelegate {
Fabrice Di Meglio039a7842013-08-28 17:41:26 -0700591 // The delegator
592 protected DatePicker mDelegator;
593
594 // The context
595 protected Context mContext;
596
Felipe Lemef480e8c2017-08-10 18:38:44 -0700597 // NOTE: when subclasses change this variable, they must call resetAutofilledValue().
Andrei Stingaceanudd80f002016-06-16 12:00:39 +0100598 protected Calendar mCurrentDate;
599
Fabrice Di Meglio039a7842013-08-28 17:41:26 -0700600 // The current locale
601 protected Locale mCurrentLocale;
602
603 // Callbacks
Alan Viverette518ff0d2014-08-15 14:20:35 -0700604 protected OnDateChangedListener mOnDateChangedListener;
Felipe Leme305b72c2017-02-27 12:46:04 -0800605 protected OnDateChangedListener mAutoFillChangeListener;
Alan Viverette518ff0d2014-08-15 14:20:35 -0700606 protected ValidationCallback mValidationCallback;
Fabrice Di Meglio039a7842013-08-28 17:41:26 -0700607
Felipe Lemef480e8c2017-08-10 18:38:44 -0700608 // The value that was passed to autofill() - it must be stored because it getAutofillValue()
609 // must return the exact same value that was autofilled, otherwise the widget will not be
610 // properly highlighted after autofill().
611 private long mAutofilledValue;
612
Fabrice Di Megliobd9152f2013-10-01 11:21:31 -0700613 public AbstractDatePickerDelegate(DatePicker delegator, Context context) {
Fabrice Di Meglio039a7842013-08-28 17:41:26 -0700614 mDelegator = delegator;
615 mContext = context;
616
Fabrice Di Meglio039a7842013-08-28 17:41:26 -0700617 setCurrentLocale(Locale.getDefault());
618 }
619
620 protected void setCurrentLocale(Locale locale) {
Alan Viverette0ef59ac2015-03-23 13:13:25 -0700621 if (!locale.equals(mCurrentLocale)) {
622 mCurrentLocale = locale;
623 onLocaleChanged(locale);
Fabrice Di Meglio039a7842013-08-28 17:41:26 -0700624 }
Svetoslav Ganovd11e6152012-03-20 12:13:02 -0700625 }
Alan Viverette518ff0d2014-08-15 14:20:35 -0700626
627 @Override
Clara Bayarri68640b62016-06-02 14:56:18 +0100628 public void setOnDateChangedListener(OnDateChangedListener callback) {
629 mOnDateChangedListener = callback;
630 }
631
632 @Override
Felipe Leme305b72c2017-02-27 12:46:04 -0800633 public void setAutoFillChangeListener(OnDateChangedListener callback) {
634 mAutoFillChangeListener = callback;
635 }
636
637 @Override
Alan Viverette518ff0d2014-08-15 14:20:35 -0700638 public void setValidationCallback(ValidationCallback callback) {
639 mValidationCallback = callback;
640 }
641
Felipe Leme305b72c2017-02-27 12:46:04 -0800642 @Override
Felipe Lemef480e8c2017-08-10 18:38:44 -0700643 public final void autofill(AutofillValue value) {
644 if (value == null || !value.isDate()) {
645 Log.w(LOG_TAG, value + " could not be autofilled into " + this);
646 return;
647 }
648
649 final long time = value.getDateValue();
650
651 final Calendar cal = Calendar.getInstance(mCurrentLocale);
652 cal.setTimeInMillis(time);
Felipe Leme305b72c2017-02-27 12:46:04 -0800653 updateDate(cal.get(Calendar.YEAR), cal.get(Calendar.MONTH),
654 cal.get(Calendar.DAY_OF_MONTH));
Felipe Lemef480e8c2017-08-10 18:38:44 -0700655
656 // Must set mAutofilledValue *after* calling subclass method to make sure the value
657 // returned by getAutofillValue() matches it.
658 mAutofilledValue = time;
Felipe Leme305b72c2017-02-27 12:46:04 -0800659 }
660
661 @Override
Felipe Lemef480e8c2017-08-10 18:38:44 -0700662 public final AutofillValue getAutofillValue() {
663 final long time = mAutofilledValue != 0
664 ? mAutofilledValue
665 : mCurrentDate.getTimeInMillis();
666 return AutofillValue.forDate(time);
667 }
668
669 /**
670 * This method must be called every time the value of the year, month, and/or day is
671 * changed by a subclass method.
672 */
673 protected void resetAutofilledValue() {
674 mAutofilledValue = 0;
Felipe Leme305b72c2017-02-27 12:46:04 -0800675 }
676
Alan Viverette518ff0d2014-08-15 14:20:35 -0700677 protected void onValidationChanged(boolean valid) {
678 if (mValidationCallback != null) {
679 mValidationCallback.onValidationChanged(valid);
680 }
681 }
Alan Viverette0ef59ac2015-03-23 13:13:25 -0700682
683 protected void onLocaleChanged(Locale locale) {
684 // Stub.
685 }
Alan Viverette6b3f85f2016-03-01 16:48:04 -0500686
Andrei Stingaceanudd80f002016-06-16 12:00:39 +0100687 @Override
688 public void onPopulateAccessibilityEvent(AccessibilityEvent event) {
689 event.getText().add(getFormattedCurrentDate());
690 }
691
692 protected String getFormattedCurrentDate() {
693 return DateUtils.formatDateTime(mContext, mCurrentDate.getTimeInMillis(),
694 DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_SHOW_YEAR
695 | DateUtils.FORMAT_SHOW_WEEKDAY);
696 }
697
Alan Viverette6b3f85f2016-03-01 16:48:04 -0500698 /**
699 * Class for managing state storing/restoring.
700 */
701 static class SavedState extends View.BaseSavedState {
702 private final int mSelectedYear;
703 private final int mSelectedMonth;
704 private final int mSelectedDay;
705 private final long mMinDate;
706 private final long mMaxDate;
707 private final int mCurrentView;
708 private final int mListPosition;
709 private final int mListPositionOffset;
710
711 public SavedState(Parcelable superState, int year, int month, int day, long minDate,
712 long maxDate) {
713 this(superState, year, month, day, minDate, maxDate, 0, 0, 0);
714 }
715
716 /**
717 * Constructor called from {@link DatePicker#onSaveInstanceState()}
718 */
719 public SavedState(Parcelable superState, int year, int month, int day, long minDate,
720 long maxDate, int currentView, int listPosition, int listPositionOffset) {
721 super(superState);
722 mSelectedYear = year;
723 mSelectedMonth = month;
724 mSelectedDay = day;
725 mMinDate = minDate;
726 mMaxDate = maxDate;
727 mCurrentView = currentView;
728 mListPosition = listPosition;
729 mListPositionOffset = listPositionOffset;
730 }
731
732 /**
733 * Constructor called from {@link #CREATOR}
734 */
735 private SavedState(Parcel in) {
736 super(in);
737 mSelectedYear = in.readInt();
738 mSelectedMonth = in.readInt();
739 mSelectedDay = in.readInt();
740 mMinDate = in.readLong();
741 mMaxDate = in.readLong();
742 mCurrentView = in.readInt();
743 mListPosition = in.readInt();
744 mListPositionOffset = in.readInt();
745 }
746
747 @Override
748 public void writeToParcel(Parcel dest, int flags) {
749 super.writeToParcel(dest, flags);
750 dest.writeInt(mSelectedYear);
751 dest.writeInt(mSelectedMonth);
752 dest.writeInt(mSelectedDay);
753 dest.writeLong(mMinDate);
754 dest.writeLong(mMaxDate);
755 dest.writeInt(mCurrentView);
756 dest.writeInt(mListPosition);
757 dest.writeInt(mListPositionOffset);
758 }
759
760 public int getSelectedDay() {
761 return mSelectedDay;
762 }
763
764 public int getSelectedMonth() {
765 return mSelectedMonth;
766 }
767
768 public int getSelectedYear() {
769 return mSelectedYear;
770 }
771
772 public long getMinDate() {
773 return mMinDate;
774 }
775
776 public long getMaxDate() {
777 return mMaxDate;
778 }
779
780 public int getCurrentView() {
781 return mCurrentView;
782 }
783
784 public int getListPosition() {
785 return mListPosition;
786 }
787
788 public int getListPositionOffset() {
789 return mListPositionOffset;
790 }
791
792 @SuppressWarnings("all")
793 // suppress unused and hiding
794 public static final Parcelable.Creator<SavedState> CREATOR = new Creator<SavedState>() {
795
796 public SavedState createFromParcel(Parcel in) {
797 return new SavedState(in);
798 }
799
800 public SavedState[] newArray(int size) {
801 return new SavedState[size];
802 }
803 };
804 }
Svetoslav Ganova53efe92011-09-08 18:08:36 -0700805 }
806
Fabrice Di Meglio039a7842013-08-28 17:41:26 -0700807 /**
Alan Viverette518ff0d2014-08-15 14:20:35 -0700808 * A callback interface for updating input validity when the date picker
809 * when included into a dialog.
Fabrice Di Megliobd9152f2013-10-01 11:21:31 -0700810 *
811 * @hide
812 */
Alan Viverette6b3f85f2016-03-01 16:48:04 -0500813 public interface ValidationCallback {
Alan Viverette518ff0d2014-08-15 14:20:35 -0700814 void onValidationChanged(boolean valid);
Fabrice Di Megliobd9152f2013-10-01 11:21:31 -0700815 }
Felipe Leme305b72c2017-02-27 12:46:04 -0800816
Felipe Leme305b72c2017-02-27 12:46:04 -0800817 @Override
Felipe Leme640f30a2017-03-06 15:44:06 -0800818 public void dispatchProvideAutofillStructure(ViewStructure structure, int flags) {
819 // This view is self-sufficient for autofill, so it needs to call
Felipe Leme305b72c2017-02-27 12:46:04 -0800820 // onProvideAutoFillStructure() to fill itself, but it does not need to call
821 // dispatchProvideAutoFillStructure() to fill its children.
Felipe Lemee4f30652017-04-25 10:21:45 -0700822 structure.setAutofillId(getAutofillId());
Felipe Leme640f30a2017-03-06 15:44:06 -0800823 onProvideAutofillStructure(structure, flags);
Felipe Leme305b72c2017-02-27 12:46:04 -0800824 }
825
826 @Override
Felipe Leme955e2522017-03-29 17:47:58 -0700827 public void autofill(AutofillValue value) {
828 if (!isEnabled()) return;
Felipe Leme305b72c2017-02-27 12:46:04 -0800829
Felipe Lemef480e8c2017-08-10 18:38:44 -0700830 mDelegate.autofill(value);
Felipe Leme305b72c2017-02-27 12:46:04 -0800831 }
832
833 @Override
Felipe Leme8931e302017-03-06 13:44:35 -0800834 public @AutofillType int getAutofillType() {
835 return isEnabled() ? AUTOFILL_TYPE_DATE : AUTOFILL_TYPE_NONE;
Felipe Leme305b72c2017-02-27 12:46:04 -0800836 }
837
838 @Override
Felipe Leme640f30a2017-03-06 15:44:06 -0800839 public AutofillValue getAutofillValue() {
Felipe Lemef480e8c2017-08-10 18:38:44 -0700840 return isEnabled() ? mDelegate.getAutofillValue() : null;
Felipe Leme305b72c2017-02-27 12:46:04 -0800841 }
Svetoslav Ganov3fec3fe2011-09-01 14:48:37 -0700842}