1/*
2 * Copyright (C) 2007 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package android.widget;
18
19import android.annotation.NonNull;
20import android.annotation.Nullable;
21import android.annotation.Widget;
22import android.content.Context;
23import android.content.res.Configuration;
24import android.content.res.TypedArray;
25import android.os.Parcel;
26import android.os.Parcelable;
27import android.os.Parcelable.Creator;
28import android.util.AttributeSet;
29import android.view.View;
30import android.view.accessibility.AccessibilityEvent;
31import com.android.internal.R;
32
33import java.util.Locale;
34
35import libcore.icu.LocaleData;
36
37/**
38 * A widget for selecting the time of day, in either 24-hour or AM/PM mode.
39 * <p>
40 * For a dialog using this view, see {@link android.app.TimePickerDialog}. See
41 * the <a href="{@docRoot}guide/topics/ui/controls/pickers.html">Pickers</a>
42 * guide for more information.
43 *
44 * @attr ref android.R.styleable#TimePicker_timePickerMode
45 */
46@Widget
47public class TimePicker extends FrameLayout {
48    private static final int MODE_SPINNER = 1;
49    private static final int MODE_CLOCK = 2;
50
51    private final TimePickerDelegate mDelegate;
52
53    /**
54     * The callback interface used to indicate the time has been adjusted.
55     */
56    public interface OnTimeChangedListener {
57
58        /**
59         * @param view The view associated with this listener.
60         * @param hourOfDay The current hour.
61         * @param minute The current minute.
62         */
63        void onTimeChanged(TimePicker view, int hourOfDay, int minute);
64    }
65
66    public TimePicker(Context context) {
67        this(context, null);
68    }
69
70    public TimePicker(Context context, AttributeSet attrs) {
71        this(context, attrs, R.attr.timePickerStyle);
72    }
73
74    public TimePicker(Context context, AttributeSet attrs, int defStyleAttr) {
75        this(context, attrs, defStyleAttr, 0);
76    }
77
78    public TimePicker(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
79        super(context, attrs, defStyleAttr, defStyleRes);
80
81        final TypedArray a = context.obtainStyledAttributes(
82                attrs, R.styleable.TimePicker, defStyleAttr, defStyleRes);
83        final int mode = a.getInt(R.styleable.TimePicker_timePickerMode, MODE_SPINNER);
84        a.recycle();
85
86        switch (mode) {
87            case MODE_CLOCK:
88                mDelegate = new TimePickerClockDelegate(
89                        this, context, attrs, defStyleAttr, defStyleRes);
90                break;
91            case MODE_SPINNER:
92            default:
93                mDelegate = new TimePickerSpinnerDelegate(
94                        this, context, attrs, defStyleAttr, defStyleRes);
95                break;
96        }
97    }
98
99    /**
100     * Sets the currently selected hour using 24-hour time.
101     *
102     * @param hour the hour to set, in the range (0-23)
103     * @see #getHour()
104     */
105    public void setHour(int hour) {
106        mDelegate.setHour(hour);
107    }
108
109    /**
110     * Returns the currently selected hour using 24-hour time.
111     *
112     * @return the currently selected hour, in the range (0-23)
113     * @see #setHour(int)
114     */
115    public int getHour() {
116        return mDelegate.getHour();
117    }
118
119    /**
120     * Sets the currently selected minute..
121     *
122     * @param minute the minute to set, in the range (0-59)
123     * @see #getMinute()
124     */
125    public void setMinute(int minute) {
126        mDelegate.setMinute(minute);
127    }
128
129    /**
130     * Returns the currently selected minute.
131     *
132     * @return the currently selected minute, in the range (0-59)
133     * @see #setMinute(int)
134     */
135    public int getMinute() {
136        return mDelegate.getMinute();
137    }
138
139    /**
140     * Sets the current hour.
141     *
142     * @deprecated Use {@link #setHour(int)}
143     */
144    @Deprecated
145    public void setCurrentHour(@NonNull Integer currentHour) {
146        setHour(currentHour);
147    }
148
149    /**
150     * @return the current hour in the range (0-23)
151     * @deprecated Use {@link #getHour()}
152     */
153    @NonNull
154    @Deprecated
155    public Integer getCurrentHour() {
156        return mDelegate.getHour();
157    }
158
159    /**
160     * Set the current minute (0-59).
161     *
162     * @deprecated Use {@link #setMinute(int)}
163     */
164    @Deprecated
165    public void setCurrentMinute(@NonNull Integer currentMinute) {
166        mDelegate.setMinute(currentMinute);
167    }
168
169    /**
170     * @return the current minute
171     * @deprecated Use {@link #getMinute()}
172     */
173    @NonNull
174    @Deprecated
175    public Integer getCurrentMinute() {
176        return mDelegate.getMinute();
177    }
178
179    /**
180     * Sets whether this widget displays time in 24-hour mode or 12-hour mode
181     * with an AM/PM picker.
182     *
183     * @param is24HourView {@code true} to display in 24-hour mode,
184     *                     {@code false} for 12-hour mode with AM/PM
185     * @see #is24HourView()
186     */
187    public void setIs24HourView(@NonNull Boolean is24HourView) {
188        if (is24HourView == null) {
189            return;
190        }
191
192        mDelegate.setIs24Hour(is24HourView);
193    }
194
195    /**
196     * @return {@code true} if this widget displays time in 24-hour mode,
197     *         {@code false} otherwise}
198     * @see #setIs24HourView(Boolean)
199     */
200    public boolean is24HourView() {
201        return mDelegate.is24Hour();
202    }
203
204    /**
205     * Set the callback that indicates the time has been adjusted by the user.
206     *
207     * @param onTimeChangedListener the callback, should not be null.
208     */
209    public void setOnTimeChangedListener(OnTimeChangedListener onTimeChangedListener) {
210        mDelegate.setOnTimeChangedListener(onTimeChangedListener);
211    }
212
213    @Override
214    public void setEnabled(boolean enabled) {
215        super.setEnabled(enabled);
216        mDelegate.setEnabled(enabled);
217    }
218
219    @Override
220    public boolean isEnabled() {
221        return mDelegate.isEnabled();
222    }
223
224    @Override
225    public int getBaseline() {
226        return mDelegate.getBaseline();
227    }
228
229    @Override
230    protected Parcelable onSaveInstanceState() {
231        Parcelable superState = super.onSaveInstanceState();
232        return mDelegate.onSaveInstanceState(superState);
233    }
234
235    @Override
236    protected void onRestoreInstanceState(Parcelable state) {
237        BaseSavedState ss = (BaseSavedState) state;
238        super.onRestoreInstanceState(ss.getSuperState());
239        mDelegate.onRestoreInstanceState(ss);
240    }
241
242    @Override
243    public CharSequence getAccessibilityClassName() {
244        return TimePicker.class.getName();
245    }
246
247    /** @hide */
248    @Override
249    public boolean dispatchPopulateAccessibilityEventInternal(AccessibilityEvent event) {
250        return mDelegate.dispatchPopulateAccessibilityEvent(event);
251    }
252
253    /**
254     * A delegate interface that defined the public API of the TimePicker. Allows different
255     * TimePicker implementations. This would need to be implemented by the TimePicker delegates
256     * for the real behavior.
257     */
258    interface TimePickerDelegate {
259        void setHour(int hour);
260        int getHour();
261
262        void setMinute(int minute);
263        int getMinute();
264
265        void setIs24Hour(boolean is24Hour);
266        boolean is24Hour();
267
268        void setOnTimeChangedListener(OnTimeChangedListener onTimeChangedListener);
269
270        void setEnabled(boolean enabled);
271        boolean isEnabled();
272
273        int getBaseline();
274
275        Parcelable onSaveInstanceState(Parcelable superState);
276        void onRestoreInstanceState(Parcelable state);
277
278        boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event);
279        void onPopulateAccessibilityEvent(AccessibilityEvent event);
280    }
281
282    static String[] getAmPmStrings(Context context) {
283        final Locale locale = context.getResources().getConfiguration().locale;
284        final LocaleData d = LocaleData.get(locale);
285
286        final String[] result = new String[2];
287        result[0] = d.amPm[0].length() > 4 ? d.narrowAm : d.amPm[0];
288        result[1] = d.amPm[1].length() > 4 ? d.narrowPm : d.amPm[1];
289        return result;
290    }
291
292    /**
293     * An abstract class which can be used as a start for TimePicker implementations
294     */
295    abstract static class AbstractTimePickerDelegate implements TimePickerDelegate {
296        protected final TimePicker mDelegator;
297        protected final Context mContext;
298        protected final Locale mLocale;
299
300        protected OnTimeChangedListener mOnTimeChangedListener;
301
302        public AbstractTimePickerDelegate(@NonNull TimePicker delegator, @NonNull Context context) {
303            mDelegator = delegator;
304            mContext = context;
305            mLocale = context.getResources().getConfiguration().locale;
306        }
307
308        protected static class SavedState extends View.BaseSavedState {
309            private final int mHour;
310            private final int mMinute;
311            private final boolean mIs24HourMode;
312            private final int mCurrentItemShowing;
313
314            public SavedState(Parcelable superState, int hour, int minute, boolean is24HourMode) {
315                this(superState, hour, minute, is24HourMode, 0);
316            }
317
318            public SavedState(Parcelable superState, int hour, int minute, boolean is24HourMode,
319                    int currentItemShowing) {
320                super(superState);
321                mHour = hour;
322                mMinute = minute;
323                mIs24HourMode = is24HourMode;
324                mCurrentItemShowing = currentItemShowing;
325            }
326
327            private SavedState(Parcel in) {
328                super(in);
329                mHour = in.readInt();
330                mMinute = in.readInt();
331                mIs24HourMode = (in.readInt() == 1);
332                mCurrentItemShowing = in.readInt();
333            }
334
335            public int getHour() {
336                return mHour;
337            }
338
339            public int getMinute() {
340                return mMinute;
341            }
342
343            public boolean is24HourMode() {
344                return mIs24HourMode;
345            }
346
347            public int getCurrentItemShowing() {
348                return mCurrentItemShowing;
349            }
350
351            @Override
352            public void writeToParcel(Parcel dest, int flags) {
353                super.writeToParcel(dest, flags);
354                dest.writeInt(mHour);
355                dest.writeInt(mMinute);
356                dest.writeInt(mIs24HourMode ? 1 : 0);
357                dest.writeInt(mCurrentItemShowing);
358            }
359
360            @SuppressWarnings({"unused", "hiding"})
361            public static final Creator<SavedState> CREATOR = new Creator<SavedState>() {
362                public SavedState createFromParcel(Parcel in) {
363                    return new SavedState(in);
364                }
365
366                public SavedState[] newArray(int size) {
367                    return new SavedState[size];
368                }
369            };
370        }
371    }
372}
373