blob: f63573f4f3fe23ae61f3bde693414814c8c63cb7 [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;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080022import android.annotation.Widget;
23import android.content.Context;
Svetoslav Ganovf5926962011-07-12 12:26:20 -070024import android.content.res.Configuration;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080025import android.content.res.TypedArray;
Alan Viverette68763be2016-05-25 11:42:42 -040026import android.icu.util.Calendar;
27import android.icu.util.TimeZone;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080028import android.os.Parcel;
29import android.os.Parcelable;
Andrei Stingaceanudd80f002016-06-16 12:00:39 +010030import android.text.format.DateUtils;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080031import android.util.AttributeSet;
Philip P. Moltmann96689032017-03-09 13:19:55 -080032import android.util.Log;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080033import android.util.SparseArray;
Svetoslav Ganovd11e6152012-03-20 12:13:02 -070034import android.view.View;
Felipe Leme305b72c2017-02-27 12:46:04 -080035import android.view.ViewStructure;
Svetoslav Ganov8a2a8952011-01-27 17:40:13 -080036import android.view.accessibility.AccessibilityEvent;
Felipe Leme640f30a2017-03-06 15:44:06 -080037import android.view.autofill.AutofillManager;
38import android.view.autofill.AutofillValue;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080039
Aurimas Liutikas99441c52016-10-11 16:48:32 -070040import com.android.internal.R;
41
Alan Viverette8817aa92016-09-22 11:16:22 -040042import java.lang.annotation.Retention;
43import java.lang.annotation.RetentionPolicy;
Kenny Rootdddda8d2010-11-15 14:38:51 -080044import java.util.Locale;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080045
46/**
Alan Viveretted25eb9f2014-11-24 14:07:24 -080047 * 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>
Svetoslav Ganov50f34d12010-12-03 16:05:40 -080060 * <p>
Scott Main4c359b72012-07-24 15:51:27 -070061 * See the <a href="{@docRoot}guide/topics/ui/controls/pickers.html">Pickers</a>
62 * guide.
Svetoslav Ganov50f34d12010-12-03 16:05:40 -080063 * </p>
Svetoslav Ganove9730bf2010-12-20 21:25:20 -080064 * <p>
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080065 * For a dialog using this view, see {@link android.app.DatePickerDialog}.
Svetoslav Ganove9730bf2010-12-20 21:25:20 -080066 * </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
Alan Viveretteba9bf412014-09-03 20:14:21 -070074 * @attr ref android.R.styleable#DatePicker_dayOfWeekBackground
Alan Viverette60727e02014-07-28 16:56:32 -070075 * @attr ref android.R.styleable#DatePicker_dayOfWeekTextAppearance
Alan Viveretteba9bf412014-09-03 20:14:21 -070076 * @attr ref android.R.styleable#DatePicker_headerBackground
Alan Viverette60727e02014-07-28 16:56:32 -070077 * @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
Fabrice Di Megliobd9152f2013-10-01 11:21:31 -070082 * @attr ref android.R.styleable#DatePicker_calendarTextColor
Alan Viveretted25eb9f2014-11-24 14:07:24 -080083 * @attr ref android.R.styleable#DatePicker_datePickerMode
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080084 */
85@Widget
86public class DatePicker extends FrameLayout {
Philip P. Moltmann96689032017-03-09 13:19:55 -080087 private static final String LOG_TAG = DatePicker.class.getSimpleName();
88
Alan Viverette8817aa92016-09-22 11:16:22 -040089 /**
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 {}
Chet Haase3053b2f2014-08-06 07:51:50 -0700113
Alan Viverette60727e02014-07-28 16:56:32 -0700114 private final DatePickerDelegate mDelegate;
Fabrice Di Megliobd9152f2013-10-01 11:21:31 -0700115
Alan Viverette8817aa92016-09-22 11:16:22 -0400116 @DatePickerMode
117 private final int mMode;
118
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800119 /**
Alan Viverette0ef59ac2015-03-23 13:13:25 -0700120 * The callback used to indicate the user changed the date.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800121 */
122 public interface OnDateChangedListener {
123
124 /**
Svetoslav Ganove9730bf2010-12-20 21:25:20 -0800125 * Called upon a date change.
126 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800127 * @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
Svetoslav Ganov50f34d12010-12-03 16:05:40 -0800130 * with {@link java.util.Calendar}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800131 * @param dayOfMonth The day of the month that was set.
132 */
133 void onDateChanged(DatePicker view, int year, int monthOfYear, int dayOfMonth);
134 }
135
136 public DatePicker(Context context) {
137 this(context, null);
138 }
Svetoslav Ganov50f34d12010-12-03 16:05:40 -0800139
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800140 public DatePicker(Context context, AttributeSet attrs) {
Svetoslav Ganov4243dc32011-01-18 19:39:57 -0800141 this(context, attrs, R.attr.datePickerStyle);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800142 }
143
Alan Viverette617feb92013-09-09 18:09:13 -0700144 public DatePicker(Context context, AttributeSet attrs, int defStyleAttr) {
145 this(context, attrs, defStyleAttr, 0);
146 }
147
148 public DatePicker(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
149 super(context, attrs, defStyleAttr, defStyleRes);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800150
Felipe Lemed04a6972017-03-02 12:56:18 -0800151 // 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
Alan Viverette60727e02014-07-28 16:56:32 -0700156 final TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.DatePicker,
157 defStyleAttr, defStyleRes);
Alan Viverette8817aa92016-09-22 11:16:22 -0400158 final boolean isDialogMode = a.getBoolean(R.styleable.DatePicker_dialogMode, false);
159 final int requestedMode = a.getInt(R.styleable.DatePicker_datePickerMode, MODE_SPINNER);
Alan Viverette0a04bb02014-09-03 20:48:02 -0700160 final int firstDayOfWeek = a.getInt(R.styleable.DatePicker_firstDayOfWeek, 0);
Fabrice Di Megliobd9152f2013-10-01 11:21:31 -0700161 a.recycle();
162
Alan Viverette8817aa92016-09-22 11:16:22 -0400163 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) {
Chet Haase3053b2f2014-08-06 07:51:50 -0700172 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;
Fabrice Di Megliobd9152f2013-10-01 11:21:31 -0700179 }
Alan Viverette0a04bb02014-09-03 20:48:02 -0700180
181 if (firstDayOfWeek != 0) {
182 setFirstDayOfWeek(firstDayOfWeek);
183 }
Felipe Leme305b72c2017-02-27 12:46:04 -0800184
185 mDelegate.setAutoFillChangeListener((v, y, m, d) -> {
Felipe Leme640f30a2017-03-06 15:44:06 -0800186 final AutofillManager afm = context.getSystemService(AutofillManager.class);
Felipe Leme305b72c2017-02-27 12:46:04 -0800187 if (afm != null) {
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700188 afm.notifyValueChanged(this);
Felipe Leme305b72c2017-02-27 12:46:04 -0800189 }
190 });
Fabrice Di Megliobd9152f2013-10-01 11:21:31 -0700191 }
192
Chet Haase3053b2f2014-08-06 07:51:50 -0700193 private DatePickerDelegate createSpinnerUIDelegate(Context context, AttributeSet attrs,
Fabrice Di Megliobd9152f2013-10-01 11:21:31 -0700194 int defStyleAttr, int defStyleRes) {
Chet Haase3053b2f2014-08-06 07:51:50 -0700195 return new DatePickerSpinnerDelegate(this, context, attrs, defStyleAttr, defStyleRes);
Fabrice Di Megliobd9152f2013-10-01 11:21:31 -0700196 }
197
Chet Haase3053b2f2014-08-06 07:51:50 -0700198 private DatePickerDelegate createCalendarUIDelegate(Context context, AttributeSet attrs,
Fabrice Di Megliobd9152f2013-10-01 11:21:31 -0700199 int defStyleAttr, int defStyleRes) {
Chet Haase3053b2f2014-08-06 07:51:50 -0700200 return new DatePickerCalendarDelegate(this, context, attrs, defStyleAttr,
Fabrice Di Megliobd9152f2013-10-01 11:21:31 -0700201 defStyleRes);
202 }
203
204 /**
Alan Viverette8817aa92016-09-22 11:16:22 -0400205 * @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
212 public int getMode() {
213 return mMode;
214 }
215
216 /**
Svetoslav Ganov50f34d12010-12-03 16:05:40 -0800217 * Initialize the state. If the provided values designate an inconsistent
Svetoslav Ganove9730bf2010-12-20 21:25:20 -0800218 * date the values are normalized before updating the spinners.
Svetoslav Ganov50f34d12010-12-03 16:05:40 -0800219 *
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 */
226 public void init(int year, int monthOfYear, int dayOfMonth,
Fabrice Di Meglio039a7842013-08-28 17:41:26 -0700227 OnDateChangedListener onDateChangedListener) {
228 mDelegate.init(year, monthOfYear, dayOfMonth, onDateChangedListener);
Svetoslav Ganov50f34d12010-12-03 16:05:40 -0800229 }
230
231 /**
Clara Bayarri68640b62016-06-02 14:56:18 +0100232 * 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 */
237 public void setOnDateChangedListener(OnDateChangedListener onDateChangedListener) {
238 mDelegate.setOnDateChangedListener(onDateChangedListener);
239 }
240
241 /**
Fabrice Di Megliobd9152f2013-10-01 11:21:31 -0700242 * Update the current date.
Svetoslav Ganova911d4a2010-12-08 16:11:30 -0800243 *
Fabrice Di Meglio039a7842013-08-28 17:41:26 -0700244 * @param year The year.
245 * @param month The month which is <strong>starting from zero</strong>.
246 * @param dayOfMonth The day of the month.
Svetoslav Ganova911d4a2010-12-08 16:11:30 -0800247 */
Fabrice Di Meglio039a7842013-08-28 17:41:26 -0700248 public void updateDate(int year, int month, int dayOfMonth) {
249 mDelegate.updateDate(year, month, dayOfMonth);
Svetoslav Ganov50f34d12010-12-03 16:05:40 -0800250 }
251
252 /**
253 * @return The selected year.
254 */
255 public int getYear() {
Fabrice Di Meglio039a7842013-08-28 17:41:26 -0700256 return mDelegate.getYear();
Svetoslav Ganov50f34d12010-12-03 16:05:40 -0800257 }
258
259 /**
260 * @return The selected month.
261 */
262 public int getMonth() {
Fabrice Di Meglio039a7842013-08-28 17:41:26 -0700263 return mDelegate.getMonth();
Svetoslav Ganov50f34d12010-12-03 16:05:40 -0800264 }
265
266 /**
267 * @return The selected day of month.
268 */
269 public int getDayOfMonth() {
Fabrice Di Meglio039a7842013-08-28 17:41:26 -0700270 return mDelegate.getDayOfMonth();
Svetoslav Ganov50f34d12010-12-03 16:05:40 -0800271 }
272
273 /**
Fabrice Di Meglio039a7842013-08-28 17:41:26 -0700274 * 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>
Svetoslav Ganova53efe92011-09-08 18:08:36 -0700280 *
Fabrice Di Meglio039a7842013-08-28 17:41:26 -0700281 * @return The minimal supported date.
Svetoslav Ganova53efe92011-09-08 18:08:36 -0700282 */
Fabrice Di Meglio039a7842013-08-28 17:41:26 -0700283 public long getMinDate() {
Fabrice Di Megliobd9152f2013-10-01 11:21:31 -0700284 return mDelegate.getMinDate().getTimeInMillis();
Fabrice Di Meglio039a7842013-08-28 17:41:26 -0700285 }
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 */
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 */
308 public long getMaxDate() {
Fabrice Di Megliobd9152f2013-10-01 11:21:31 -0700309 return mDelegate.getMaxDate().getTimeInMillis();
Fabrice Di Meglio039a7842013-08-28 17:41:26 -0700310 }
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 */
319 public void setMaxDate(long maxDate) {
320 mDelegate.setMaxDate(maxDate);
321 }
322
Alan Viverette518ff0d2014-08-15 14:20:35 -0700323 /**
324 * Sets the callback that indicates the current date is valid.
325 *
326 * @param callback the callback, may be null
327 * @hide
328 */
329 public void setValidationCallback(@Nullable ValidationCallback callback) {
330 mDelegate.setValidationCallback(callback);
331 }
332
Fabrice Di Meglio039a7842013-08-28 17:41:26 -0700333 @Override
334 public void setEnabled(boolean enabled) {
335 if (mDelegate.isEnabled() == enabled) {
336 return;
Svetoslav Ganova53efe92011-09-08 18:08:36 -0700337 }
Fabrice Di Meglio039a7842013-08-28 17:41:26 -0700338 super.setEnabled(enabled);
339 mDelegate.setEnabled(enabled);
Svetoslav Ganova53efe92011-09-08 18:08:36 -0700340 }
341
Fabrice Di Meglio039a7842013-08-28 17:41:26 -0700342 @Override
343 public boolean isEnabled() {
344 return mDelegate.isEnabled();
Svetoslav Ganovd11e6152012-03-20 12:13:02 -0700345 }
346
Alan Viverettea54956a2015-01-07 16:05:02 -0800347 /** @hide */
Fabrice Di Meglio039a7842013-08-28 17:41:26 -0700348 @Override
Alan Viverettea54956a2015-01-07 16:05:02 -0800349 public boolean dispatchPopulateAccessibilityEventInternal(AccessibilityEvent event) {
Fabrice Di Meglio039a7842013-08-28 17:41:26 -0700350 return mDelegate.dispatchPopulateAccessibilityEvent(event);
351 }
352
Alan Viverettea54956a2015-01-07 16:05:02 -0800353 /** @hide */
Fabrice Di Meglio039a7842013-08-28 17:41:26 -0700354 @Override
Alan Viverettea54956a2015-01-07 16:05:02 -0800355 public void onPopulateAccessibilityEventInternal(AccessibilityEvent event) {
356 super.onPopulateAccessibilityEventInternal(event);
Fabrice Di Meglio039a7842013-08-28 17:41:26 -0700357 mDelegate.onPopulateAccessibilityEvent(event);
358 }
359
360 @Override
Dianne Hackborna7bb6fb2015-02-03 18:13:40 -0800361 public CharSequence getAccessibilityClassName() {
362 return DatePicker.class.getName();
Fabrice Di Meglio039a7842013-08-28 17:41:26 -0700363 }
364
365 @Override
366 protected void onConfigurationChanged(Configuration newConfig) {
367 super.onConfigurationChanged(newConfig);
368 mDelegate.onConfigurationChanged(newConfig);
369 }
370
371 /**
Alan Viverette0a04bb02014-09-03 20:48:02 -0700372 * 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 */
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 */
408 public int getFirstDayOfWeek() {
409 return mDelegate.getFirstDayOfWeek();
410 }
411
412 /**
Alan Viverette452fe342015-03-23 14:26:09 -0700413 * 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}.
Fabrice Di Meglio039a7842013-08-28 17:41:26 -0700418 *
Alan Viverette452fe342015-03-23 14:26:09 -0700419 * @return {@code true} if the calendar view is shown
Fabrice Di Meglio039a7842013-08-28 17:41:26 -0700420 * @see #getCalendarView()
Alan Viverette0e672142016-04-06 15:56:11 -0400421 * @deprecated Not supported by Material-style {@code calendar} mode
Fabrice Di Meglio039a7842013-08-28 17:41:26 -0700422 */
Alan Viverette0e672142016-04-06 15:56:11 -0400423 @Deprecated
Fabrice Di Meglio039a7842013-08-28 17:41:26 -0700424 public boolean getCalendarViewShown() {
425 return mDelegate.getCalendarViewShown();
426 }
427
428 /**
Alan Viverette452fe342015-03-23 14:26:09 -0700429 * Returns the {@link CalendarView} used by this picker.
Alan Viveretted25eb9f2014-11-24 14:07:24 -0800430 * <p>
Alan Viverette0e672142016-04-06 15:56:11 -0400431 * <strong>Note:</strong> This method throws an
432 * {@link UnsupportedOperationException} when the
Alan Viveretted25eb9f2014-11-24 14:07:24 -0800433 * {@link android.R.styleable#DatePicker_datePickerMode} attribute is set
434 * to {@code calendar}.
Fabrice Di Meglio039a7842013-08-28 17:41:26 -0700435 *
Alan Viverette452fe342015-03-23 14:26:09 -0700436 * @return the calendar view
Fabrice Di Meglio039a7842013-08-28 17:41:26 -0700437 * @see #getCalendarViewShown()
Alan Viverette0e672142016-04-06 15:56:11 -0400438 * @deprecated Not supported by Material-style {@code calendar} mode
439 * @throws UnsupportedOperationException if called when the picker is
440 * displayed in {@code calendar} mode
Fabrice Di Meglio039a7842013-08-28 17:41:26 -0700441 */
Alan Viverette0e672142016-04-06 15:56:11 -0400442 @Deprecated
Alan Viverette0a04bb02014-09-03 20:48:02 -0700443 public CalendarView getCalendarView() {
Fabrice Di Meglio039a7842013-08-28 17:41:26 -0700444 return mDelegate.getCalendarView();
445 }
446
447 /**
448 * Sets whether the {@link CalendarView} is shown.
Alan Viveretted25eb9f2014-11-24 14:07:24 -0800449 * <p>
Alan Viverette452fe342015-03-23 14:26:09 -0700450 * <strong>Note:</strong> Calling this method has no effect when the
Alan Viveretted25eb9f2014-11-24 14:07:24 -0800451 * {@link android.R.styleable#DatePicker_datePickerMode} attribute is set
452 * to {@code calendar}.
Fabrice Di Meglio039a7842013-08-28 17:41:26 -0700453 *
Alan Viverette452fe342015-03-23 14:26:09 -0700454 * @param shown {@code true} to show the calendar view, {@code false} to
455 * hide it
Alan Viverette0e672142016-04-06 15:56:11 -0400456 * @deprecated Not supported by Material-style {@code calendar} mode
Fabrice Di Meglio039a7842013-08-28 17:41:26 -0700457 */
Alan Viverette0e672142016-04-06 15:56:11 -0400458 @Deprecated
Fabrice Di Meglio039a7842013-08-28 17:41:26 -0700459 public void setCalendarViewShown(boolean shown) {
460 mDelegate.setCalendarViewShown(shown);
461 }
462
463 /**
Alan Viverette452fe342015-03-23 14:26:09 -0700464 * Returns whether the spinners are shown.
465 * <p>
Alan Viverette835d7982015-03-23 14:54:14 -0700466 * <strong>Note:</strong> his method returns {@code false} when the
Alan Viverette452fe342015-03-23 14:26:09 -0700467 * {@link android.R.styleable#DatePicker_datePickerMode} attribute is set
468 * to {@code calendar}.
Fabrice Di Meglio039a7842013-08-28 17:41:26 -0700469 *
Alan Viverette452fe342015-03-23 14:26:09 -0700470 * @return {@code true} if the spinners are shown
Alan Viverette0e672142016-04-06 15:56:11 -0400471 * @deprecated Not supported by Material-style {@code calendar} mode
Fabrice Di Meglio039a7842013-08-28 17:41:26 -0700472 */
Alan Viverette0e672142016-04-06 15:56:11 -0400473 @Deprecated
Fabrice Di Meglio039a7842013-08-28 17:41:26 -0700474 public boolean getSpinnersShown() {
475 return mDelegate.getSpinnersShown();
476 }
477
478 /**
479 * Sets whether the spinners are shown.
Alan Viverette452fe342015-03-23 14:26:09 -0700480 * <p>
481 * Calling this method has no effect when the
482 * {@link android.R.styleable#DatePicker_datePickerMode} attribute is set
483 * to {@code calendar}.
Fabrice Di Meglio039a7842013-08-28 17:41:26 -0700484 *
Alan Viverette452fe342015-03-23 14:26:09 -0700485 * @param shown {@code true} to show the spinners, {@code false} to hide
486 * them
Alan Viverette0e672142016-04-06 15:56:11 -0400487 * @deprecated Not supported by Material-style {@code calendar} mode
Fabrice Di Meglio039a7842013-08-28 17:41:26 -0700488 */
Alan Viverette0e672142016-04-06 15:56:11 -0400489 @Deprecated
Fabrice Di Meglio039a7842013-08-28 17:41:26 -0700490 public void setSpinnersShown(boolean shown) {
491 mDelegate.setSpinnersShown(shown);
492 }
493
Fabrice Di Meglio039a7842013-08-28 17:41:26 -0700494 @Override
495 protected void dispatchRestoreInstanceState(SparseArray<Parcelable> container) {
Alan Viveretted015e342014-09-15 19:27:55 -0700496 dispatchThawSelfOnly(container);
Fabrice Di Meglio039a7842013-08-28 17:41:26 -0700497 }
498
499 @Override
500 protected Parcelable onSaveInstanceState() {
501 Parcelable superState = super.onSaveInstanceState();
502 return mDelegate.onSaveInstanceState(superState);
503 }
504
505 @Override
506 protected void onRestoreInstanceState(Parcelable state) {
Craig Mautnera67d9092014-09-16 15:38:47 -0700507 BaseSavedState ss = (BaseSavedState) state;
Fabrice Di Meglio039a7842013-08-28 17:41:26 -0700508 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.
Fabrice Di Megliobd9152f2013-10-01 11:21:31 -0700516 *
517 * @hide
Fabrice Di Meglio039a7842013-08-28 17:41:26 -0700518 */
519 interface DatePickerDelegate {
520 void init(int year, int monthOfYear, int dayOfMonth,
521 OnDateChangedListener onDateChangedListener);
522
Clara Bayarri68640b62016-06-02 14:56:18 +0100523 void setOnDateChangedListener(OnDateChangedListener onDateChangedListener);
Felipe Leme305b72c2017-02-27 12:46:04 -0800524 void setAutoFillChangeListener(OnDateChangedListener onDateChangedListener);
Clara Bayarri68640b62016-06-02 14:56:18 +0100525
Fabrice Di Meglio039a7842013-08-28 17:41:26 -0700526 void updateDate(int year, int month, int dayOfMonth);
Felipe Leme305b72c2017-02-27 12:46:04 -0800527 void updateDate(long date);
Fabrice Di Meglio039a7842013-08-28 17:41:26 -0700528
529 int getYear();
530 int getMonth();
531 int getDayOfMonth();
Felipe Leme305b72c2017-02-27 12:46:04 -0800532 long getDate();
Fabrice Di Meglio039a7842013-08-28 17:41:26 -0700533
Alan Viverette0a04bb02014-09-03 20:48:02 -0700534 void setFirstDayOfWeek(int firstDayOfWeek);
535 int getFirstDayOfWeek();
536
Fabrice Di Meglio039a7842013-08-28 17:41:26 -0700537 void setMinDate(long minDate);
Fabrice Di Megliobd9152f2013-10-01 11:21:31 -0700538 Calendar getMinDate();
Fabrice Di Meglio039a7842013-08-28 17:41:26 -0700539
540 void setMaxDate(long maxDate);
Fabrice Di Megliobd9152f2013-10-01 11:21:31 -0700541 Calendar getMaxDate();
Fabrice Di Meglio039a7842013-08-28 17:41:26 -0700542
543 void setEnabled(boolean enabled);
544 boolean isEnabled();
545
Fabrice Di Megliobd9152f2013-10-01 11:21:31 -0700546 CalendarView getCalendarView();
Fabrice Di Meglio039a7842013-08-28 17:41:26 -0700547
548 void setCalendarViewShown(boolean shown);
549 boolean getCalendarViewShown();
550
551 void setSpinnersShown(boolean shown);
552 boolean getSpinnersShown();
553
Alan Viverette518ff0d2014-08-15 14:20:35 -0700554 void setValidationCallback(ValidationCallback callback);
Fabrice Di Megliobd9152f2013-10-01 11:21:31 -0700555
Fabrice Di Meglio039a7842013-08-28 17:41:26 -0700556 void onConfigurationChanged(Configuration newConfig);
557
Fabrice Di Meglio039a7842013-08-28 17:41:26 -0700558 Parcelable onSaveInstanceState(Parcelable superState);
559 void onRestoreInstanceState(Parcelable state);
560
561 boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event);
562 void onPopulateAccessibilityEvent(AccessibilityEvent event);
Fabrice Di Meglio039a7842013-08-28 17:41:26 -0700563 }
564
565 /**
566 * An abstract class which can be used as a start for DatePicker implementations
567 */
Fabrice Di Megliobd9152f2013-10-01 11:21:31 -0700568 abstract static class AbstractDatePickerDelegate implements DatePickerDelegate {
Fabrice Di Meglio039a7842013-08-28 17:41:26 -0700569 // The delegator
570 protected DatePicker mDelegator;
571
572 // The context
573 protected Context mContext;
574
Andrei Stingaceanudd80f002016-06-16 12:00:39 +0100575 protected Calendar mCurrentDate;
576
Fabrice Di Meglio039a7842013-08-28 17:41:26 -0700577 // The current locale
578 protected Locale mCurrentLocale;
579
580 // Callbacks
Alan Viverette518ff0d2014-08-15 14:20:35 -0700581 protected OnDateChangedListener mOnDateChangedListener;
Felipe Leme305b72c2017-02-27 12:46:04 -0800582 protected OnDateChangedListener mAutoFillChangeListener;
Alan Viverette518ff0d2014-08-15 14:20:35 -0700583 protected ValidationCallback mValidationCallback;
Fabrice Di Meglio039a7842013-08-28 17:41:26 -0700584
Fabrice Di Megliobd9152f2013-10-01 11:21:31 -0700585 public AbstractDatePickerDelegate(DatePicker delegator, Context context) {
Fabrice Di Meglio039a7842013-08-28 17:41:26 -0700586 mDelegator = delegator;
587 mContext = context;
588
Fabrice Di Meglio039a7842013-08-28 17:41:26 -0700589 setCurrentLocale(Locale.getDefault());
590 }
591
592 protected void setCurrentLocale(Locale locale) {
Alan Viverette0ef59ac2015-03-23 13:13:25 -0700593 if (!locale.equals(mCurrentLocale)) {
594 mCurrentLocale = locale;
595 onLocaleChanged(locale);
Fabrice Di Meglio039a7842013-08-28 17:41:26 -0700596 }
Svetoslav Ganovd11e6152012-03-20 12:13:02 -0700597 }
Alan Viverette518ff0d2014-08-15 14:20:35 -0700598
599 @Override
Clara Bayarri68640b62016-06-02 14:56:18 +0100600 public void setOnDateChangedListener(OnDateChangedListener callback) {
601 mOnDateChangedListener = callback;
602 }
603
604 @Override
Felipe Leme305b72c2017-02-27 12:46:04 -0800605 public void setAutoFillChangeListener(OnDateChangedListener callback) {
606 mAutoFillChangeListener = callback;
607 }
608
609 @Override
Alan Viverette518ff0d2014-08-15 14:20:35 -0700610 public void setValidationCallback(ValidationCallback callback) {
611 mValidationCallback = callback;
612 }
613
Felipe Leme305b72c2017-02-27 12:46:04 -0800614 @Override
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
623 public long getDate() {
624 return mCurrentDate.getTimeInMillis();
625 }
626
Alan Viverette518ff0d2014-08-15 14:20:35 -0700627 protected void onValidationChanged(boolean valid) {
628 if (mValidationCallback != null) {
629 mValidationCallback.onValidationChanged(valid);
630 }
631 }
Alan Viverette0ef59ac2015-03-23 13:13:25 -0700632
633 protected void onLocaleChanged(Locale locale) {
634 // Stub.
635 }
Alan Viverette6b3f85f2016-03-01 16:48:04 -0500636
Andrei Stingaceanudd80f002016-06-16 12:00:39 +0100637 @Override
638 public void onPopulateAccessibilityEvent(AccessibilityEvent event) {
639 event.getText().add(getFormattedCurrentDate());
640 }
641
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
Alan Viverette6b3f85f2016-03-01 16:48:04 -0500648 /**
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
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 */
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 */
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
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
710 public int getSelectedDay() {
711 return mSelectedDay;
712 }
713
714 public int getSelectedMonth() {
715 return mSelectedMonth;
716 }
717
718 public int getSelectedYear() {
719 return mSelectedYear;
720 }
721
722 public long getMinDate() {
723 return mMinDate;
724 }
725
726 public long getMaxDate() {
727 return mMaxDate;
728 }
729
730 public int getCurrentView() {
731 return mCurrentView;
732 }
733
734 public int getListPosition() {
735 return mListPosition;
736 }
737
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 }
Svetoslav Ganova53efe92011-09-08 18:08:36 -0700755 }
756
Fabrice Di Meglio039a7842013-08-28 17:41:26 -0700757 /**
Alan Viverette518ff0d2014-08-15 14:20:35 -0700758 * A callback interface for updating input validity when the date picker
759 * when included into a dialog.
Fabrice Di Megliobd9152f2013-10-01 11:21:31 -0700760 *
761 * @hide
762 */
Alan Viverette6b3f85f2016-03-01 16:48:04 -0500763 public interface ValidationCallback {
Alan Viverette518ff0d2014-08-15 14:20:35 -0700764 void onValidationChanged(boolean valid);
Fabrice Di Megliobd9152f2013-10-01 11:21:31 -0700765 }
Felipe Leme305b72c2017-02-27 12:46:04 -0800766
Felipe Leme640f30a2017-03-06 15:44:06 -0800767 // TODO(b/33197203): add unit/CTS tests for autofill methods (and make sure they handle enable)
Felipe Leme305b72c2017-02-27 12:46:04 -0800768
769 @Override
Felipe Leme640f30a2017-03-06 15:44:06 -0800770 public void dispatchProvideAutofillStructure(ViewStructure structure, int flags) {
771 // This view is self-sufficient for autofill, so it needs to call
Felipe Leme305b72c2017-02-27 12:46:04 -0800772 // onProvideAutoFillStructure() to fill itself, but it does not need to call
773 // dispatchProvideAutoFillStructure() to fill its children.
Felipe Leme640f30a2017-03-06 15:44:06 -0800774 onProvideAutofillStructure(structure, flags);
Felipe Leme305b72c2017-02-27 12:46:04 -0800775 }
776
777 @Override
Felipe Leme640f30a2017-03-06 15:44:06 -0800778 public void autofill(AutofillValue value) {
Felipe Leme305b72c2017-02-27 12:46:04 -0800779 if (!isEnabled()) return;
780
Philip P. Moltmann96689032017-03-09 13:19:55 -0800781 if (value.isDate()) {
782 mDelegate.updateDate(value.getDateValue());
783 } else {
784 Log.w(LOG_TAG, value + " could not be autofilled into " + this);
785 }
Felipe Leme305b72c2017-02-27 12:46:04 -0800786 }
787
788 @Override
Felipe Leme8931e302017-03-06 13:44:35 -0800789 public @AutofillType int getAutofillType() {
790 return isEnabled() ? AUTOFILL_TYPE_DATE : AUTOFILL_TYPE_NONE;
Felipe Leme305b72c2017-02-27 12:46:04 -0800791 }
792
793 @Override
Felipe Leme640f30a2017-03-06 15:44:06 -0800794 public AutofillValue getAutofillValue() {
795 return isEnabled() ? AutofillValue.forDate(mDelegate.getDate()) : null;
Felipe Leme305b72c2017-02-27 12:46:04 -0800796 }
Svetoslav Ganov3fec3fe2011-09-01 14:48:37 -0700797}