blob: c7a298016d446bbf4f75f2abe5f506c7269a3450 [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 Viverettef0ac2ba2016-08-04 13:23:33 -040020import android.annotation.IntRange;
Alan Viverette646a0f82015-03-18 13:24:07 -070021import android.annotation.NonNull;
Andrei Stingaceanuf87b0e12016-07-04 17:40:14 +010022import android.annotation.TestApi;
Mathew Inwood978c6e22018-08-21 15:58:55 +010023import android.annotation.UnsupportedAppUsage;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080024import android.annotation.Widget;
25import android.content.Context;
Svetoslav Ganov4243dc32011-01-18 19:39:57 -080026import android.content.res.TypedArray;
Felipe Leme305b72c2017-02-27 12:46:04 -080027import android.icu.util.Calendar;
Alan Viverette6b3f85f2016-03-01 16:48:04 -050028import android.os.Parcel;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080029import android.os.Parcelable;
30import android.util.AttributeSet;
Philip P. Moltmann96689032017-03-09 13:19:55 -080031import android.util.Log;
Alan Viverettef0ac2ba2016-08-04 13:23:33 -040032import android.util.MathUtils;
Alan Viverette6b3f85f2016-03-01 16:48:04 -050033import android.view.View;
Felipe Leme305b72c2017-02-27 12:46:04 -080034import android.view.ViewStructure;
Svetoslav Ganov8a2a8952011-01-27 17:40:13 -080035import android.view.accessibility.AccessibilityEvent;
Felipe Leme640f30a2017-03-06 15:44:06 -080036import android.view.autofill.AutofillManager;
37import android.view.autofill.AutofillValue;
Ashley Rose55f9f922019-01-28 19:29:36 -050038import android.view.inspector.InspectableProperty;
Svetoslav Ganova53efe92011-09-08 18:08:36 -070039
Aurimas Liutikas99441c52016-10-11 16:48:32 -070040import com.android.internal.R;
41
42import libcore.icu.LocaleData;
43
Alan Viverette8817aa92016-09-22 11:16:22 -040044import java.lang.annotation.Retention;
45import java.lang.annotation.RetentionPolicy;
Svetoslav Ganovf5926962011-07-12 12:26:20 -070046import java.util.Locale;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080047
48/**
Alan Viverette646a0f82015-03-18 13:24:07 -070049 * A widget for selecting the time of day, in either 24-hour or AM/PM mode.
Alan Viverette51344782014-07-16 17:39:27 -070050 * <p>
Alan Viverette646a0f82015-03-18 13:24:07 -070051 * For a dialog using this view, see {@link android.app.TimePickerDialog}. See
52 * the <a href="{@docRoot}guide/topics/ui/controls/pickers.html">Pickers</a>
53 * guide for more information.
54 *
55 * @attr ref android.R.styleable#TimePicker_timePickerMode
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080056 */
57@Widget
58public class TimePicker extends FrameLayout {
Philip P. Moltmann96689032017-03-09 13:19:55 -080059 private static final String LOG_TAG = TimePicker.class.getSimpleName();
60
Alan Viverette8817aa92016-09-22 11:16:22 -040061 /**
62 * Presentation mode for the Holo-style time picker that uses a set of
63 * {@link android.widget.NumberPicker}s.
64 *
65 * @see #getMode()
66 * @hide Visible for testing only.
67 */
68 @TestApi
69 public static final int MODE_SPINNER = 1;
70
71 /**
72 * Presentation mode for the Material-style time picker that uses a clock
73 * face.
74 *
75 * @see #getMode()
76 * @hide Visible for testing only.
77 */
78 @TestApi
79 public static final int MODE_CLOCK = 2;
80
81 /** @hide */
Jeff Sharkeyce8db992017-12-13 20:05:05 -070082 @IntDef(prefix = { "MODE_" }, value = {
83 MODE_SPINNER,
84 MODE_CLOCK
85 })
Alan Viverette8817aa92016-09-22 11:16:22 -040086 @Retention(RetentionPolicy.SOURCE)
87 public @interface TimePickerMode {}
Chet Haase3053b2f2014-08-06 07:51:50 -070088
Mathew Inwood978c6e22018-08-21 15:58:55 +010089 @UnsupportedAppUsage
Alan Viverette51344782014-07-16 17:39:27 -070090 private final TimePickerDelegate mDelegate;
Fabrice Di Meglioeeff63a2013-08-05 12:07:24 -070091
Alan Viverette8817aa92016-09-22 11:16:22 -040092 @TimePickerMode
93 private final int mMode;
94
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080095 /**
96 * The callback interface used to indicate the time has been adjusted.
97 */
98 public interface OnTimeChangedListener {
99
100 /**
101 * @param view The view associated with this listener.
102 * @param hourOfDay The current hour.
103 * @param minute The current minute.
104 */
105 void onTimeChanged(TimePicker view, int hourOfDay, int minute);
106 }
107
108 public TimePicker(Context context) {
109 this(context, null);
110 }
Svetoslav Ganov4243dc32011-01-18 19:39:57 -0800111
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800112 public TimePicker(Context context, AttributeSet attrs) {
Svetoslav Ganov4243dc32011-01-18 19:39:57 -0800113 this(context, attrs, R.attr.timePickerStyle);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800114 }
115
Alan Viverette617feb92013-09-09 18:09:13 -0700116 public TimePicker(Context context, AttributeSet attrs, int defStyleAttr) {
117 this(context, attrs, defStyleAttr, 0);
118 }
119
120 public TimePicker(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
121 super(context, attrs, defStyleAttr, defStyleRes);
Fabrice Di Meglioeeff63a2013-08-05 12:07:24 -0700122
Felipe Lemed04a6972017-03-02 12:56:18 -0800123 // DatePicker is important by default, unless app developer overrode attribute.
124 if (getImportantForAutofill() == IMPORTANT_FOR_AUTOFILL_AUTO) {
125 setImportantForAutofill(IMPORTANT_FOR_AUTOFILL_YES);
126 }
127
Alan Viverette51344782014-07-16 17:39:27 -0700128 final TypedArray a = context.obtainStyledAttributes(
129 attrs, R.styleable.TimePicker, defStyleAttr, defStyleRes);
Aurimas Liutikasab324cf2019-02-07 16:46:38 -0800130 saveAttributeDataForStyleable(context, R.styleable.TimePicker,
131 attrs, a, defStyleAttr, defStyleRes);
Alan Viverette8817aa92016-09-22 11:16:22 -0400132 final boolean isDialogMode = a.getBoolean(R.styleable.TimePicker_dialogMode, false);
133 final int requestedMode = a.getInt(R.styleable.TimePicker_timePickerMode, MODE_SPINNER);
Fabrice Di Megliodfaa6c72014-07-10 19:33:33 -0700134 a.recycle();
135
Alan Viverette8817aa92016-09-22 11:16:22 -0400136 if (requestedMode == MODE_CLOCK && isDialogMode) {
137 // You want MODE_CLOCK? YOU CAN'T HANDLE MODE_CLOCK! Well, maybe
138 // you can depending on your screen size. Let's check...
139 mMode = context.getResources().getInteger(R.integer.time_picker_mode);
140 } else {
141 mMode = requestedMode;
142 }
143
144 switch (mMode) {
Chet Haase3053b2f2014-08-06 07:51:50 -0700145 case MODE_CLOCK:
Alan Viverettedaf33ed2014-10-23 13:34:17 -0700146 mDelegate = new TimePickerClockDelegate(
Chet Haase3053b2f2014-08-06 07:51:50 -0700147 this, context, attrs, defStyleAttr, defStyleRes);
148 break;
149 case MODE_SPINNER:
150 default:
Alan Viverettedaf33ed2014-10-23 13:34:17 -0700151 mDelegate = new TimePickerSpinnerDelegate(
Chet Haase3053b2f2014-08-06 07:51:50 -0700152 this, context, attrs, defStyleAttr, defStyleRes);
153 break;
Fabrice Di Megliodfaa6c72014-07-10 19:33:33 -0700154 }
Felipe Leme305b72c2017-02-27 12:46:04 -0800155 mDelegate.setAutoFillChangeListener((v, h, m) -> {
Felipe Leme640f30a2017-03-06 15:44:06 -0800156 final AutofillManager afm = context.getSystemService(AutofillManager.class);
Felipe Leme305b72c2017-02-27 12:46:04 -0800157 if (afm != null) {
Svet Ganov2f8fb1f2017-03-13 00:21:04 -0700158 afm.notifyValueChanged(this);
Felipe Leme305b72c2017-02-27 12:46:04 -0800159 }
160 });
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800161 }
Svetoslav Ganov50f34d12010-12-03 16:05:40 -0800162
Fabrice Di Meglio9e4009e2013-08-19 13:16:46 -0700163 /**
Alan Viverette8817aa92016-09-22 11:16:22 -0400164 * @return the picker's presentation mode, one of {@link #MODE_CLOCK} or
165 * {@link #MODE_SPINNER}
166 * @attr ref android.R.styleable#TimePicker_timePickerMode
167 * @hide Visible for testing only.
168 */
169 @TimePickerMode
170 @TestApi
Ashley Rose55f9f922019-01-28 19:29:36 -0500171 @InspectableProperty(name = "timePickerMode", enumMapping = {
172 @InspectableProperty.EnumMap(name = "clock", value = MODE_CLOCK),
173 @InspectableProperty.EnumMap(name = "spinner", value = MODE_SPINNER)
174 })
Alan Viverette8817aa92016-09-22 11:16:22 -0400175 public int getMode() {
176 return mMode;
177 }
178
179 /**
Alan Viverette646a0f82015-03-18 13:24:07 -0700180 * Sets the currently selected hour using 24-hour time.
181 *
182 * @param hour the hour to set, in the range (0-23)
183 * @see #getHour()
Fabrice Di Meglio9e4009e2013-08-19 13:16:46 -0700184 */
Alan Viverettef0ac2ba2016-08-04 13:23:33 -0400185 public void setHour(@IntRange(from = 0, to = 23) int hour) {
186 mDelegate.setHour(MathUtils.constrain(hour, 0, 23));
Fabrice Di Meglio64902bd2013-08-15 17:49:49 -0700187 }
188
Fabrice Di Meglio9e4009e2013-08-19 13:16:46 -0700189 /**
Alan Viverette646a0f82015-03-18 13:24:07 -0700190 * Returns the currently selected hour using 24-hour time.
191 *
192 * @return the currently selected hour, in the range (0-23)
193 * @see #setHour(int)
Fabrice Di Meglio9e4009e2013-08-19 13:16:46 -0700194 */
Ashley Rose55f9f922019-01-28 19:29:36 -0500195 @InspectableProperty(hasAttributeId = false)
Alan Viverette646a0f82015-03-18 13:24:07 -0700196 public int getHour() {
Alan Viverette4420ae82015-11-16 16:10:56 -0500197 return mDelegate.getHour();
Alan Viverette646a0f82015-03-18 13:24:07 -0700198 }
199
200 /**
Alan Viverettef0ac2ba2016-08-04 13:23:33 -0400201 * Sets the currently selected minute.
Alan Viverette646a0f82015-03-18 13:24:07 -0700202 *
203 * @param minute the minute to set, in the range (0-59)
204 * @see #getMinute()
205 */
Alan Viverettef0ac2ba2016-08-04 13:23:33 -0400206 public void setMinute(@IntRange(from = 0, to = 59) int minute) {
207 mDelegate.setMinute(MathUtils.constrain(minute, 0, 59));
Alan Viverette646a0f82015-03-18 13:24:07 -0700208 }
209
210 /**
211 * Returns the currently selected minute.
212 *
213 * @return the currently selected minute, in the range (0-59)
214 * @see #setMinute(int)
215 */
Ashley Rose55f9f922019-01-28 19:29:36 -0500216 @InspectableProperty(hasAttributeId = false)
Alan Viverette646a0f82015-03-18 13:24:07 -0700217 public int getMinute() {
Alan Viverette4420ae82015-11-16 16:10:56 -0500218 return mDelegate.getMinute();
Alan Viverette646a0f82015-03-18 13:24:07 -0700219 }
220
221 /**
Alan Viverettef0ac2ba2016-08-04 13:23:33 -0400222 * Sets the currently selected hour using 24-hour time.
Alan Viverette646a0f82015-03-18 13:24:07 -0700223 *
Alan Viverettef0ac2ba2016-08-04 13:23:33 -0400224 * @param currentHour the hour to set, in the range (0-23)
Alan Viverette646a0f82015-03-18 13:24:07 -0700225 * @deprecated Use {@link #setHour(int)}
226 */
227 @Deprecated
228 public void setCurrentHour(@NonNull Integer currentHour) {
229 setHour(currentHour);
230 }
231
232 /**
Alan Viverettef0ac2ba2016-08-04 13:23:33 -0400233 * @return the currently selected hour, in the range (0-23)
Alan Viverette646a0f82015-03-18 13:24:07 -0700234 * @deprecated Use {@link #getHour()}
235 */
236 @NonNull
237 @Deprecated
Fabrice Di Meglio9e4009e2013-08-19 13:16:46 -0700238 public Integer getCurrentHour() {
Alan Viverettef0ac2ba2016-08-04 13:23:33 -0400239 return getHour();
Fabrice Di Meglio9e4009e2013-08-19 13:16:46 -0700240 }
Fabrice Di Meglio64902bd2013-08-15 17:49:49 -0700241
Fabrice Di Meglio9e4009e2013-08-19 13:16:46 -0700242 /**
Alan Viverettef0ac2ba2016-08-04 13:23:33 -0400243 * Sets the currently selected minute.
Alan Viverette646a0f82015-03-18 13:24:07 -0700244 *
Alan Viverettef0ac2ba2016-08-04 13:23:33 -0400245 * @param currentMinute the minute to set, in the range (0-59)
Alan Viverette646a0f82015-03-18 13:24:07 -0700246 * @deprecated Use {@link #setMinute(int)}
Fabrice Di Meglio9e4009e2013-08-19 13:16:46 -0700247 */
Alan Viverette646a0f82015-03-18 13:24:07 -0700248 @Deprecated
249 public void setCurrentMinute(@NonNull Integer currentMinute) {
Alan Viverettef0ac2ba2016-08-04 13:23:33 -0400250 setMinute(currentMinute);
Fabrice Di Meglio9e4009e2013-08-19 13:16:46 -0700251 }
252
253 /**
Alan Viverettef0ac2ba2016-08-04 13:23:33 -0400254 * @return the currently selected minute, in the range (0-59)
Alan Viverette646a0f82015-03-18 13:24:07 -0700255 * @deprecated Use {@link #getMinute()}
Fabrice Di Meglio9e4009e2013-08-19 13:16:46 -0700256 */
Alan Viverette646a0f82015-03-18 13:24:07 -0700257 @NonNull
258 @Deprecated
Fabrice Di Meglio9e4009e2013-08-19 13:16:46 -0700259 public Integer getCurrentMinute() {
Alan Viverettef0ac2ba2016-08-04 13:23:33 -0400260 return getMinute();
Fabrice Di Meglio9e4009e2013-08-19 13:16:46 -0700261 }
262
263 /**
Alan Viverette646a0f82015-03-18 13:24:07 -0700264 * Sets whether this widget displays time in 24-hour mode or 12-hour mode
265 * with an AM/PM picker.
Fabrice Di Meglio9e4009e2013-08-19 13:16:46 -0700266 *
Alan Viverette646a0f82015-03-18 13:24:07 -0700267 * @param is24HourView {@code true} to display in 24-hour mode,
268 * {@code false} for 12-hour mode with AM/PM
269 * @see #is24HourView()
Fabrice Di Meglio9e4009e2013-08-19 13:16:46 -0700270 */
Alan Viverette646a0f82015-03-18 13:24:07 -0700271 public void setIs24HourView(@NonNull Boolean is24HourView) {
272 if (is24HourView == null) {
273 return;
274 }
275
Alan Viverette4420ae82015-11-16 16:10:56 -0500276 mDelegate.setIs24Hour(is24HourView);
Fabrice Di Meglio9e4009e2013-08-19 13:16:46 -0700277 }
278
279 /**
Alan Viverette646a0f82015-03-18 13:24:07 -0700280 * @return {@code true} if this widget displays time in 24-hour mode,
281 * {@code false} otherwise}
282 * @see #setIs24HourView(Boolean)
Fabrice Di Meglio9e4009e2013-08-19 13:16:46 -0700283 */
Ashley Rose55f9f922019-01-28 19:29:36 -0500284 @InspectableProperty(hasAttributeId = false, name = "24Hour")
Fabrice Di Meglio9e4009e2013-08-19 13:16:46 -0700285 public boolean is24HourView() {
Alan Viverette4420ae82015-11-16 16:10:56 -0500286 return mDelegate.is24Hour();
Fabrice Di Meglio9e4009e2013-08-19 13:16:46 -0700287 }
288
289 /**
290 * Set the callback that indicates the time has been adjusted by the user.
291 *
292 * @param onTimeChangedListener the callback, should not be null.
293 */
294 public void setOnTimeChangedListener(OnTimeChangedListener onTimeChangedListener) {
295 mDelegate.setOnTimeChangedListener(onTimeChangedListener);
Fabrice Di Meglio64902bd2013-08-15 17:49:49 -0700296 }
297
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800298 @Override
299 public void setEnabled(boolean enabled) {
300 super.setEnabled(enabled);
Fabrice Di Meglio9e4009e2013-08-19 13:16:46 -0700301 mDelegate.setEnabled(enabled);
Svetoslav Ganov51c52ed2010-12-28 13:45:03 -0800302 }
303
304 @Override
305 public boolean isEnabled() {
Fabrice Di Meglio9e4009e2013-08-19 13:16:46 -0700306 return mDelegate.isEnabled();
307 }
308
309 @Override
310 public int getBaseline() {
311 return mDelegate.getBaseline();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800312 }
313
Aurimas Liutikasab14d822017-01-24 17:46:10 -0800314 /**
315 * Validates whether current input by the user is a valid time based on the locale. TimePicker
316 * will show an error message to the user if the time is not valid.
317 *
318 * @return {@code true} if the input is valid, {@code false} otherwise
319 */
320 public boolean validateInput() {
321 return mDelegate.validateInput();
322 }
323
Svetoslav Ganovf5926962011-07-12 12:26:20 -0700324 @Override
Fabrice Di Meglio9e4009e2013-08-19 13:16:46 -0700325 protected Parcelable onSaveInstanceState() {
326 Parcelable superState = super.onSaveInstanceState();
327 return mDelegate.onSaveInstanceState(superState);
328 }
329
330 @Override
331 protected void onRestoreInstanceState(Parcelable state) {
Fabrice Di Meglioeeff63a2013-08-05 12:07:24 -0700332 BaseSavedState ss = (BaseSavedState) state;
Fabrice Di Meglio9e4009e2013-08-19 13:16:46 -0700333 super.onRestoreInstanceState(ss.getSuperState());
334 mDelegate.onRestoreInstanceState(ss);
335 }
336
Dianne Hackborna7bb6fb2015-02-03 18:13:40 -0800337 @Override
338 public CharSequence getAccessibilityClassName() {
339 return TimePicker.class.getName();
340 }
341
Alan Viverettea54956a2015-01-07 16:05:02 -0800342 /** @hide */
Fabrice Di Meglio9e4009e2013-08-19 13:16:46 -0700343 @Override
Alan Viverettea54956a2015-01-07 16:05:02 -0800344 public boolean dispatchPopulateAccessibilityEventInternal(AccessibilityEvent event) {
Fabrice Di Meglio9e4009e2013-08-19 13:16:46 -0700345 return mDelegate.dispatchPopulateAccessibilityEvent(event);
346 }
347
Andrei Stingaceanuf87b0e12016-07-04 17:40:14 +0100348 /** @hide */
349 @TestApi
350 public View getHourView() {
351 return mDelegate.getHourView();
352 }
353
354 /** @hide */
355 @TestApi
356 public View getMinuteView() {
357 return mDelegate.getMinuteView();
358 }
359
360 /** @hide */
361 @TestApi
362 public View getAmView() {
363 return mDelegate.getAmView();
364 }
365
366 /** @hide */
367 @TestApi
368 public View getPmView() {
369 return mDelegate.getPmView();
370 }
371
Svetoslav Ganovf5926962011-07-12 12:26:20 -0700372 /**
Fabrice Di Meglio9e4009e2013-08-19 13:16:46 -0700373 * A delegate interface that defined the public API of the TimePicker. Allows different
374 * TimePicker implementations. This would need to be implemented by the TimePicker delegates
375 * for the real behavior.
Svetoslav Ganovf5926962011-07-12 12:26:20 -0700376 */
Fabrice Di Meglioeeff63a2013-08-05 12:07:24 -0700377 interface TimePickerDelegate {
Alan Viverettef0ac2ba2016-08-04 13:23:33 -0400378 void setHour(@IntRange(from = 0, to = 23) int hour);
Alan Viverette4420ae82015-11-16 16:10:56 -0500379 int getHour();
Fabrice Di Meglio9e4009e2013-08-19 13:16:46 -0700380
Alan Viverettef0ac2ba2016-08-04 13:23:33 -0400381 void setMinute(@IntRange(from = 0, to = 59) int minute);
Alan Viverette4420ae82015-11-16 16:10:56 -0500382 int getMinute();
Fabrice Di Meglio9e4009e2013-08-19 13:16:46 -0700383
Felipe Lemef480e8c2017-08-10 18:38:44 -0700384 void setDate(@IntRange(from = 0, to = 23) int hour,
385 @IntRange(from = 0, to = 59) int minute);
386
387 void autofill(AutofillValue value);
388 AutofillValue getAutofillValue();
Felipe Leme305b72c2017-02-27 12:46:04 -0800389
Alan Viverette4420ae82015-11-16 16:10:56 -0500390 void setIs24Hour(boolean is24Hour);
391 boolean is24Hour();
Fabrice Di Meglio9e4009e2013-08-19 13:16:46 -0700392
Aurimas Liutikasab14d822017-01-24 17:46:10 -0800393 boolean validateInput();
394
Fabrice Di Meglio9e4009e2013-08-19 13:16:46 -0700395 void setOnTimeChangedListener(OnTimeChangedListener onTimeChangedListener);
Felipe Leme305b72c2017-02-27 12:46:04 -0800396 void setAutoFillChangeListener(OnTimeChangedListener autoFillChangeListener);
Fabrice Di Meglio9e4009e2013-08-19 13:16:46 -0700397
398 void setEnabled(boolean enabled);
399 boolean isEnabled();
400
401 int getBaseline();
402
Fabrice Di Meglio9e4009e2013-08-19 13:16:46 -0700403 Parcelable onSaveInstanceState(Parcelable superState);
404 void onRestoreInstanceState(Parcelable state);
405
406 boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event);
407 void onPopulateAccessibilityEvent(AccessibilityEvent event);
Andrei Stingaceanuf87b0e12016-07-04 17:40:14 +0100408
409 /** @hide */
410 @TestApi View getHourView();
411
412 /** @hide */
413 @TestApi View getMinuteView();
414
415 /** @hide */
416 @TestApi View getAmView();
417
418 /** @hide */
419 @TestApi View getPmView();
Fabrice Di Meglio9e4009e2013-08-19 13:16:46 -0700420 }
421
Alan Viveretteb3f24632015-10-22 16:01:48 -0400422 static String[] getAmPmStrings(Context context) {
423 final Locale locale = context.getResources().getConfiguration().locale;
424 final LocaleData d = LocaleData.get(locale);
425
426 final String[] result = new String[2];
427 result[0] = d.amPm[0].length() > 4 ? d.narrowAm : d.amPm[0];
428 result[1] = d.amPm[1].length() > 4 ? d.narrowPm : d.amPm[1];
429 return result;
430 }
431
Fabrice Di Meglioeeff63a2013-08-05 12:07:24 -0700432 /**
433 * An abstract class which can be used as a start for TimePicker implementations
434 */
435 abstract static class AbstractTimePickerDelegate implements TimePickerDelegate {
Alan Viverette4420ae82015-11-16 16:10:56 -0500436 protected final TimePicker mDelegator;
437 protected final Context mContext;
438 protected final Locale mLocale;
Fabrice Di Meglio9e4009e2013-08-19 13:16:46 -0700439
Alan Viverette518ff0d2014-08-15 14:20:35 -0700440 protected OnTimeChangedListener mOnTimeChangedListener;
Felipe Leme305b72c2017-02-27 12:46:04 -0800441 protected OnTimeChangedListener mAutoFillChangeListener;
Fabrice Di Meglio9e4009e2013-08-19 13:16:46 -0700442
Felipe Lemef480e8c2017-08-10 18:38:44 -0700443 // The value that was passed to autofill() - it must be stored because it getAutofillValue()
444 // must return the exact same value that was autofilled, otherwise the widget will not be
445 // properly highlighted after autofill().
446 private long mAutofilledValue;
447
Alan Viverette4420ae82015-11-16 16:10:56 -0500448 public AbstractTimePickerDelegate(@NonNull TimePicker delegator, @NonNull Context context) {
Fabrice Di Meglio9e4009e2013-08-19 13:16:46 -0700449 mDelegator = delegator;
Fabrice Di Meglioeeff63a2013-08-05 12:07:24 -0700450 mContext = context;
Alan Viverette4420ae82015-11-16 16:10:56 -0500451 mLocale = context.getResources().getConfiguration().locale;
Alan Viverette518ff0d2014-08-15 14:20:35 -0700452 }
Alan Viverette6b3f85f2016-03-01 16:48:04 -0500453
Clara Bayarri68640b62016-06-02 14:56:18 +0100454 @Override
455 public void setOnTimeChangedListener(OnTimeChangedListener callback) {
456 mOnTimeChangedListener = callback;
457 }
458
Felipe Leme305b72c2017-02-27 12:46:04 -0800459 @Override
460 public void setAutoFillChangeListener(OnTimeChangedListener callback) {
461 mAutoFillChangeListener = callback;
462 }
463
464 @Override
Felipe Lemef480e8c2017-08-10 18:38:44 -0700465 public final void autofill(AutofillValue value) {
466 if (value == null || !value.isDate()) {
467 Log.w(LOG_TAG, value + " could not be autofilled into " + this);
468 return;
469 }
470
471 final long time = value.getDateValue();
472
473 final Calendar cal = Calendar.getInstance(mLocale);
474 cal.setTimeInMillis(time);
475 setDate(cal.get(Calendar.HOUR_OF_DAY), cal.get(Calendar.MINUTE));
476
477 // Must set mAutofilledValue *after* calling subclass method to make sure the value
478 // returned by getAutofillValue() matches it.
479 mAutofilledValue = time;
Felipe Leme305b72c2017-02-27 12:46:04 -0800480 }
481
482 @Override
Felipe Lemef480e8c2017-08-10 18:38:44 -0700483 public final AutofillValue getAutofillValue() {
484 if (mAutofilledValue != 0) {
485 return AutofillValue.forDate(mAutofilledValue);
486 }
487
488 final Calendar cal = Calendar.getInstance(mLocale);
Felipe Leme305b72c2017-02-27 12:46:04 -0800489 cal.set(Calendar.HOUR_OF_DAY, getHour());
490 cal.set(Calendar.MINUTE, getMinute());
Felipe Lemef480e8c2017-08-10 18:38:44 -0700491 return AutofillValue.forDate(cal.getTimeInMillis());
492 }
493
494 /**
495 * This method must be called every time the value of the hour and/or minute is changed by
496 * a subclass method.
497 */
498 protected void resetAutofilledValue() {
499 mAutofilledValue = 0;
Felipe Leme305b72c2017-02-27 12:46:04 -0800500 }
501
Alan Viverette6b3f85f2016-03-01 16:48:04 -0500502 protected static class SavedState extends View.BaseSavedState {
503 private final int mHour;
504 private final int mMinute;
505 private final boolean mIs24HourMode;
506 private final int mCurrentItemShowing;
507
508 public SavedState(Parcelable superState, int hour, int minute, boolean is24HourMode) {
509 this(superState, hour, minute, is24HourMode, 0);
510 }
511
512 public SavedState(Parcelable superState, int hour, int minute, boolean is24HourMode,
513 int currentItemShowing) {
514 super(superState);
515 mHour = hour;
516 mMinute = minute;
517 mIs24HourMode = is24HourMode;
518 mCurrentItemShowing = currentItemShowing;
519 }
520
521 private SavedState(Parcel in) {
522 super(in);
523 mHour = in.readInt();
524 mMinute = in.readInt();
525 mIs24HourMode = (in.readInt() == 1);
526 mCurrentItemShowing = in.readInt();
527 }
528
529 public int getHour() {
530 return mHour;
531 }
532
533 public int getMinute() {
534 return mMinute;
535 }
536
537 public boolean is24HourMode() {
538 return mIs24HourMode;
539 }
540
541 public int getCurrentItemShowing() {
542 return mCurrentItemShowing;
543 }
544
545 @Override
546 public void writeToParcel(Parcel dest, int flags) {
547 super.writeToParcel(dest, flags);
548 dest.writeInt(mHour);
549 dest.writeInt(mMinute);
550 dest.writeInt(mIs24HourMode ? 1 : 0);
551 dest.writeInt(mCurrentItemShowing);
552 }
553
554 @SuppressWarnings({"unused", "hiding"})
Jeff Sharkey9e8f83d2019-02-28 12:06:45 -0700555 public static final @android.annotation.NonNull Creator<SavedState> CREATOR = new Creator<SavedState>() {
Alan Viverette6b3f85f2016-03-01 16:48:04 -0500556 public SavedState createFromParcel(Parcel in) {
557 return new SavedState(in);
558 }
559
560 public SavedState[] newArray(int size) {
561 return new SavedState[size];
562 }
563 };
564 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800565 }
Felipe Leme305b72c2017-02-27 12:46:04 -0800566
Felipe Leme305b72c2017-02-27 12:46:04 -0800567 @Override
Felipe Leme640f30a2017-03-06 15:44:06 -0800568 public void dispatchProvideAutofillStructure(ViewStructure structure, int flags) {
569 // This view is self-sufficient for autofill, so it needs to call
Felipe Leme305b72c2017-02-27 12:46:04 -0800570 // onProvideAutoFillStructure() to fill itself, but it does not need to call
571 // dispatchProvideAutoFillStructure() to fill its children.
Felipe Lemee4f30652017-04-25 10:21:45 -0700572 structure.setAutofillId(getAutofillId());
Felipe Leme640f30a2017-03-06 15:44:06 -0800573 onProvideAutofillStructure(structure, flags);
Felipe Leme305b72c2017-02-27 12:46:04 -0800574 }
575
576 @Override
Felipe Leme955e2522017-03-29 17:47:58 -0700577 public void autofill(AutofillValue value) {
578 if (!isEnabled()) return;
Felipe Leme305b72c2017-02-27 12:46:04 -0800579
Felipe Lemef480e8c2017-08-10 18:38:44 -0700580 mDelegate.autofill(value);
Felipe Leme305b72c2017-02-27 12:46:04 -0800581 }
582
583 @Override
Felipe Leme8931e302017-03-06 13:44:35 -0800584 public @AutofillType int getAutofillType() {
585 return isEnabled() ? AUTOFILL_TYPE_DATE : AUTOFILL_TYPE_NONE;
Felipe Leme305b72c2017-02-27 12:46:04 -0800586 }
587
588 @Override
Felipe Leme640f30a2017-03-06 15:44:06 -0800589 public AutofillValue getAutofillValue() {
Felipe Lemef480e8c2017-08-10 18:38:44 -0700590 return isEnabled() ? mDelegate.getAutofillValue() : null;
Felipe Leme305b72c2017-02-27 12:46:04 -0800591 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800592}