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 com.android.internal.R; 20 21import android.annotation.IntDef; 22import android.annotation.IntRange; 23import android.annotation.NonNull; 24import android.annotation.TestApi; 25import android.annotation.Widget; 26import android.content.Context; 27import android.content.res.TypedArray; 28import android.os.Parcel; 29import android.os.Parcelable; 30import android.util.AttributeSet; 31import android.util.MathUtils; 32import android.view.View; 33import android.view.accessibility.AccessibilityEvent; 34 35import java.lang.annotation.Retention; 36import java.lang.annotation.RetentionPolicy; 37import java.util.Locale; 38 39import libcore.icu.LocaleData; 40 41/** 42 * A widget for selecting the time of day, in either 24-hour or AM/PM mode. 43 * <p> 44 * For a dialog using this view, see {@link android.app.TimePickerDialog}. See 45 * the <a href="{@docRoot}guide/topics/ui/controls/pickers.html">Pickers</a> 46 * guide for more information. 47 * 48 * @attr ref android.R.styleable#TimePicker_timePickerMode 49 */ 50@Widget 51public class TimePicker extends FrameLayout { 52 /** 53 * Presentation mode for the Holo-style time picker that uses a set of 54 * {@link android.widget.NumberPicker}s. 55 * 56 * @see #getMode() 57 * @hide Visible for testing only. 58 */ 59 @TestApi 60 public static final int MODE_SPINNER = 1; 61 62 /** 63 * Presentation mode for the Material-style time picker that uses a clock 64 * face. 65 * 66 * @see #getMode() 67 * @hide Visible for testing only. 68 */ 69 @TestApi 70 public static final int MODE_CLOCK = 2; 71 72 /** @hide */ 73 @IntDef({MODE_SPINNER, MODE_CLOCK}) 74 @Retention(RetentionPolicy.SOURCE) 75 public @interface TimePickerMode {} 76 77 private final TimePickerDelegate mDelegate; 78 79 @TimePickerMode 80 private final int mMode; 81 82 /** 83 * The callback interface used to indicate the time has been adjusted. 84 */ 85 public interface OnTimeChangedListener { 86 87 /** 88 * @param view The view associated with this listener. 89 * @param hourOfDay The current hour. 90 * @param minute The current minute. 91 */ 92 void onTimeChanged(TimePicker view, int hourOfDay, int minute); 93 } 94 95 public TimePicker(Context context) { 96 this(context, null); 97 } 98 99 public TimePicker(Context context, AttributeSet attrs) { 100 this(context, attrs, R.attr.timePickerStyle); 101 } 102 103 public TimePicker(Context context, AttributeSet attrs, int defStyleAttr) { 104 this(context, attrs, defStyleAttr, 0); 105 } 106 107 public TimePicker(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { 108 super(context, attrs, defStyleAttr, defStyleRes); 109 110 final TypedArray a = context.obtainStyledAttributes( 111 attrs, R.styleable.TimePicker, defStyleAttr, defStyleRes); 112 final boolean isDialogMode = a.getBoolean(R.styleable.TimePicker_dialogMode, false); 113 final int requestedMode = a.getInt(R.styleable.TimePicker_timePickerMode, MODE_SPINNER); 114 a.recycle(); 115 116 if (requestedMode == MODE_CLOCK && isDialogMode) { 117 // You want MODE_CLOCK? YOU CAN'T HANDLE MODE_CLOCK! Well, maybe 118 // you can depending on your screen size. Let's check... 119 mMode = context.getResources().getInteger(R.integer.time_picker_mode); 120 } else { 121 mMode = requestedMode; 122 } 123 124 switch (mMode) { 125 case MODE_CLOCK: 126 mDelegate = new TimePickerClockDelegate( 127 this, context, attrs, defStyleAttr, defStyleRes); 128 break; 129 case MODE_SPINNER: 130 default: 131 mDelegate = new TimePickerSpinnerDelegate( 132 this, context, attrs, defStyleAttr, defStyleRes); 133 break; 134 } 135 } 136 137 /** 138 * @return the picker's presentation mode, one of {@link #MODE_CLOCK} or 139 * {@link #MODE_SPINNER} 140 * @attr ref android.R.styleable#TimePicker_timePickerMode 141 * @hide Visible for testing only. 142 */ 143 @TimePickerMode 144 @TestApi 145 public int getMode() { 146 return mMode; 147 } 148 149 /** 150 * Sets the currently selected hour using 24-hour time. 151 * 152 * @param hour the hour to set, in the range (0-23) 153 * @see #getHour() 154 */ 155 public void setHour(@IntRange(from = 0, to = 23) int hour) { 156 mDelegate.setHour(MathUtils.constrain(hour, 0, 23)); 157 } 158 159 /** 160 * Returns the currently selected hour using 24-hour time. 161 * 162 * @return the currently selected hour, in the range (0-23) 163 * @see #setHour(int) 164 */ 165 public int getHour() { 166 return mDelegate.getHour(); 167 } 168 169 /** 170 * Sets the currently selected minute. 171 * 172 * @param minute the minute to set, in the range (0-59) 173 * @see #getMinute() 174 */ 175 public void setMinute(@IntRange(from = 0, to = 59) int minute) { 176 mDelegate.setMinute(MathUtils.constrain(minute, 0, 59)); 177 } 178 179 /** 180 * Returns the currently selected minute. 181 * 182 * @return the currently selected minute, in the range (0-59) 183 * @see #setMinute(int) 184 */ 185 public int getMinute() { 186 return mDelegate.getMinute(); 187 } 188 189 /** 190 * Sets the currently selected hour using 24-hour time. 191 * 192 * @param currentHour the hour to set, in the range (0-23) 193 * @deprecated Use {@link #setHour(int)} 194 */ 195 @Deprecated 196 public void setCurrentHour(@NonNull Integer currentHour) { 197 setHour(currentHour); 198 } 199 200 /** 201 * @return the currently selected hour, in the range (0-23) 202 * @deprecated Use {@link #getHour()} 203 */ 204 @NonNull 205 @Deprecated 206 public Integer getCurrentHour() { 207 return getHour(); 208 } 209 210 /** 211 * Sets the currently selected minute. 212 * 213 * @param currentMinute the minute to set, in the range (0-59) 214 * @deprecated Use {@link #setMinute(int)} 215 */ 216 @Deprecated 217 public void setCurrentMinute(@NonNull Integer currentMinute) { 218 setMinute(currentMinute); 219 } 220 221 /** 222 * @return the currently selected minute, in the range (0-59) 223 * @deprecated Use {@link #getMinute()} 224 */ 225 @NonNull 226 @Deprecated 227 public Integer getCurrentMinute() { 228 return getMinute(); 229 } 230 231 /** 232 * Sets whether this widget displays time in 24-hour mode or 12-hour mode 233 * with an AM/PM picker. 234 * 235 * @param is24HourView {@code true} to display in 24-hour mode, 236 * {@code false} for 12-hour mode with AM/PM 237 * @see #is24HourView() 238 */ 239 public void setIs24HourView(@NonNull Boolean is24HourView) { 240 if (is24HourView == null) { 241 return; 242 } 243 244 mDelegate.setIs24Hour(is24HourView); 245 } 246 247 /** 248 * @return {@code true} if this widget displays time in 24-hour mode, 249 * {@code false} otherwise} 250 * @see #setIs24HourView(Boolean) 251 */ 252 public boolean is24HourView() { 253 return mDelegate.is24Hour(); 254 } 255 256 /** 257 * Set the callback that indicates the time has been adjusted by the user. 258 * 259 * @param onTimeChangedListener the callback, should not be null. 260 */ 261 public void setOnTimeChangedListener(OnTimeChangedListener onTimeChangedListener) { 262 mDelegate.setOnTimeChangedListener(onTimeChangedListener); 263 } 264 265 @Override 266 public void setEnabled(boolean enabled) { 267 super.setEnabled(enabled); 268 mDelegate.setEnabled(enabled); 269 } 270 271 @Override 272 public boolean isEnabled() { 273 return mDelegate.isEnabled(); 274 } 275 276 @Override 277 public int getBaseline() { 278 return mDelegate.getBaseline(); 279 } 280 281 @Override 282 protected Parcelable onSaveInstanceState() { 283 Parcelable superState = super.onSaveInstanceState(); 284 return mDelegate.onSaveInstanceState(superState); 285 } 286 287 @Override 288 protected void onRestoreInstanceState(Parcelable state) { 289 BaseSavedState ss = (BaseSavedState) state; 290 super.onRestoreInstanceState(ss.getSuperState()); 291 mDelegate.onRestoreInstanceState(ss); 292 } 293 294 @Override 295 public CharSequence getAccessibilityClassName() { 296 return TimePicker.class.getName(); 297 } 298 299 /** @hide */ 300 @Override 301 public boolean dispatchPopulateAccessibilityEventInternal(AccessibilityEvent event) { 302 return mDelegate.dispatchPopulateAccessibilityEvent(event); 303 } 304 305 /** 306 * A delegate interface that defined the public API of the TimePicker. Allows different 307 * TimePicker implementations. This would need to be implemented by the TimePicker delegates 308 * for the real behavior. 309 */ 310 interface TimePickerDelegate { 311 void setHour(@IntRange(from = 0, to = 23) int hour); 312 int getHour(); 313 314 void setMinute(@IntRange(from = 0, to = 59) int minute); 315 int getMinute(); 316 317 void setIs24Hour(boolean is24Hour); 318 boolean is24Hour(); 319 320 void setOnTimeChangedListener(OnTimeChangedListener onTimeChangedListener); 321 322 void setEnabled(boolean enabled); 323 boolean isEnabled(); 324 325 int getBaseline(); 326 327 Parcelable onSaveInstanceState(Parcelable superState); 328 void onRestoreInstanceState(Parcelable state); 329 330 boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event); 331 void onPopulateAccessibilityEvent(AccessibilityEvent event); 332 } 333 334 static String[] getAmPmStrings(Context context) { 335 final Locale locale = context.getResources().getConfiguration().locale; 336 final LocaleData d = LocaleData.get(locale); 337 338 final String[] result = new String[2]; 339 result[0] = d.amPm[0].length() > 4 ? d.narrowAm : d.amPm[0]; 340 result[1] = d.amPm[1].length() > 4 ? d.narrowPm : d.amPm[1]; 341 return result; 342 } 343 344 /** 345 * An abstract class which can be used as a start for TimePicker implementations 346 */ 347 abstract static class AbstractTimePickerDelegate implements TimePickerDelegate { 348 protected final TimePicker mDelegator; 349 protected final Context mContext; 350 protected final Locale mLocale; 351 352 protected OnTimeChangedListener mOnTimeChangedListener; 353 354 public AbstractTimePickerDelegate(@NonNull TimePicker delegator, @NonNull Context context) { 355 mDelegator = delegator; 356 mContext = context; 357 mLocale = context.getResources().getConfiguration().locale; 358 } 359 360 protected static class SavedState extends View.BaseSavedState { 361 private final int mHour; 362 private final int mMinute; 363 private final boolean mIs24HourMode; 364 private final int mCurrentItemShowing; 365 366 public SavedState(Parcelable superState, int hour, int minute, boolean is24HourMode) { 367 this(superState, hour, minute, is24HourMode, 0); 368 } 369 370 public SavedState(Parcelable superState, int hour, int minute, boolean is24HourMode, 371 int currentItemShowing) { 372 super(superState); 373 mHour = hour; 374 mMinute = minute; 375 mIs24HourMode = is24HourMode; 376 mCurrentItemShowing = currentItemShowing; 377 } 378 379 private SavedState(Parcel in) { 380 super(in); 381 mHour = in.readInt(); 382 mMinute = in.readInt(); 383 mIs24HourMode = (in.readInt() == 1); 384 mCurrentItemShowing = in.readInt(); 385 } 386 387 public int getHour() { 388 return mHour; 389 } 390 391 public int getMinute() { 392 return mMinute; 393 } 394 395 public boolean is24HourMode() { 396 return mIs24HourMode; 397 } 398 399 public int getCurrentItemShowing() { 400 return mCurrentItemShowing; 401 } 402 403 @Override 404 public void writeToParcel(Parcel dest, int flags) { 405 super.writeToParcel(dest, flags); 406 dest.writeInt(mHour); 407 dest.writeInt(mMinute); 408 dest.writeInt(mIs24HourMode ? 1 : 0); 409 dest.writeInt(mCurrentItemShowing); 410 } 411 412 @SuppressWarnings({"unused", "hiding"}) 413 public static final Creator<SavedState> CREATOR = new Creator<SavedState>() { 414 public SavedState createFromParcel(Parcel in) { 415 return new SavedState(in); 416 } 417 418 public SavedState[] newArray(int size) { 419 return new SavedState[size]; 420 } 421 }; 422 } 423 } 424} 425