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 17 package android.widget; 18 19 import android.annotation.Widget; 20 import android.content.Context; 21 import android.os.Parcel; 22 import android.os.Parcelable; 23 import android.util.AttributeSet; 24 import android.view.LayoutInflater; 25 import android.view.View; 26 import android.widget.NumberPicker; 27 28 import com.android.internal.R; 29 30 import java.text.DateFormatSymbols; 31 import java.util.Calendar; 32 33 /** 34 * A view for selecting the time of day, in either 24 hour or AM/PM mode. 35 * 36 * The hour, each minute digit, and AM/PM (if applicable) can be conrolled by 37 * vertical spinners. 38 * 39 * The hour can be entered by keyboard input. Entering in two digit hours 40 * can be accomplished by hitting two digits within a timeout of about a 41 * second (e.g. '1' then '2' to select 12). 42 * 43 * The minutes can be entered by entering single digits. 44 * 45 * Under AM/PM mode, the user can hit 'a', 'A", 'p' or 'P' to pick. 46 * 47 * For a dialog using this view, see {@link android.app.TimePickerDialog}. 48 * 49 * <p>See the <a href="{@docRoot}resources/tutorials/views/hello-timepicker.html">Time Picker 50 * tutorial</a>.</p> 51 */ 52 @Widget 53 public class TimePicker extends FrameLayout { 54 55 /** 56 * A no-op callback used in the constructor to avoid null checks 57 * later in the code. 58 */ 59 private static final OnTimeChangedListener NO_OP_CHANGE_LISTENER = new OnTimeChangedListener() { 60 public void onTimeChanged(TimePicker view, int hourOfDay, int minute) { 61 } 62 }; 63 64 // state 65 private int mCurrentHour = 0; // 0-23 66 private int mCurrentMinute = 0; // 0-59 67 private Boolean mIs24HourView = false; 68 private boolean mIsAm; 69 70 // ui components 71 private final NumberPicker mHourPicker; 72 private final NumberPicker mMinutePicker; 73 private final Button mAmPmButton; 74 private final String mAmText; 75 private final String mPmText; 76 77 // callbacks 78 private OnTimeChangedListener mOnTimeChangedListener; 79 80 /** 81 * The callback interface used to indicate the time has been adjusted. 82 */ 83 public interface OnTimeChangedListener { 84 85 /** 86 * @param view The view associated with this listener. 87 * @param hourOfDay The current hour. 88 * @param minute The current minute. 89 */ onTimeChanged(TimePicker view, int hourOfDay, int minute)90 void onTimeChanged(TimePicker view, int hourOfDay, int minute); 91 } 92 TimePicker(Context context)93 public TimePicker(Context context) { 94 this(context, null); 95 } 96 TimePicker(Context context, AttributeSet attrs)97 public TimePicker(Context context, AttributeSet attrs) { 98 this(context, attrs, 0); 99 } 100 TimePicker(Context context, AttributeSet attrs, int defStyle)101 public TimePicker(Context context, AttributeSet attrs, int defStyle) { 102 super(context, attrs, defStyle); 103 104 LayoutInflater inflater = 105 (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); 106 inflater.inflate(R.layout.time_picker, 107 this, // we are the parent 108 true); 109 110 // hour 111 mHourPicker = (NumberPicker) findViewById(R.id.hour); 112 mHourPicker.setOnChangeListener(new NumberPicker.OnChangedListener() { 113 public void onChanged(NumberPicker spinner, int oldVal, int newVal) { 114 mCurrentHour = newVal; 115 if (!mIs24HourView) { 116 // adjust from [1-12] to [0-11] internally, with the times 117 // written "12:xx" being the start of the half-day 118 if (mCurrentHour == 12) { 119 mCurrentHour = 0; 120 } 121 if (!mIsAm) { 122 // PM means 12 hours later than nominal 123 mCurrentHour += 12; 124 } 125 } 126 onTimeChanged(); 127 } 128 }); 129 130 // digits of minute 131 mMinutePicker = (NumberPicker) findViewById(R.id.minute); 132 mMinutePicker.setRange(0, 59); 133 mMinutePicker.setSpeed(100); 134 mMinutePicker.setFormatter(NumberPicker.TWO_DIGIT_FORMATTER); 135 mMinutePicker.setOnChangeListener(new NumberPicker.OnChangedListener() { 136 public void onChanged(NumberPicker spinner, int oldVal, int newVal) { 137 mCurrentMinute = newVal; 138 onTimeChanged(); 139 } 140 }); 141 142 // am/pm 143 mAmPmButton = (Button) findViewById(R.id.amPm); 144 145 // now that the hour/minute picker objects have been initialized, set 146 // the hour range properly based on the 12/24 hour display mode. 147 configurePickerRanges(); 148 149 // initialize to current time 150 Calendar cal = Calendar.getInstance(); 151 setOnTimeChangedListener(NO_OP_CHANGE_LISTENER); 152 153 // by default we're not in 24 hour mode 154 setCurrentHour(cal.get(Calendar.HOUR_OF_DAY)); 155 setCurrentMinute(cal.get(Calendar.MINUTE)); 156 157 mIsAm = (mCurrentHour < 12); 158 159 /* Get the localized am/pm strings and use them in the spinner */ 160 DateFormatSymbols dfs = new DateFormatSymbols(); 161 String[] dfsAmPm = dfs.getAmPmStrings(); 162 mAmText = dfsAmPm[Calendar.AM]; 163 mPmText = dfsAmPm[Calendar.PM]; 164 mAmPmButton.setText(mIsAm ? mAmText : mPmText); 165 mAmPmButton.setOnClickListener(new OnClickListener() { 166 public void onClick(View v) { 167 requestFocus(); 168 if (mIsAm) { 169 170 // Currently AM switching to PM 171 if (mCurrentHour < 12) { 172 mCurrentHour += 12; 173 } 174 } else { 175 176 // Currently PM switching to AM 177 if (mCurrentHour >= 12) { 178 mCurrentHour -= 12; 179 } 180 } 181 mIsAm = !mIsAm; 182 mAmPmButton.setText(mIsAm ? mAmText : mPmText); 183 onTimeChanged(); 184 } 185 }); 186 187 if (!isEnabled()) { 188 setEnabled(false); 189 } 190 } 191 192 @Override setEnabled(boolean enabled)193 public void setEnabled(boolean enabled) { 194 super.setEnabled(enabled); 195 mMinutePicker.setEnabled(enabled); 196 mHourPicker.setEnabled(enabled); 197 mAmPmButton.setEnabled(enabled); 198 } 199 200 /** 201 * Used to save / restore state of time picker 202 */ 203 private static class SavedState extends BaseSavedState { 204 205 private final int mHour; 206 private final int mMinute; 207 SavedState(Parcelable superState, int hour, int minute)208 private SavedState(Parcelable superState, int hour, int minute) { 209 super(superState); 210 mHour = hour; 211 mMinute = minute; 212 } 213 SavedState(Parcel in)214 private SavedState(Parcel in) { 215 super(in); 216 mHour = in.readInt(); 217 mMinute = in.readInt(); 218 } 219 getHour()220 public int getHour() { 221 return mHour; 222 } 223 getMinute()224 public int getMinute() { 225 return mMinute; 226 } 227 228 @Override writeToParcel(Parcel dest, int flags)229 public void writeToParcel(Parcel dest, int flags) { 230 super.writeToParcel(dest, flags); 231 dest.writeInt(mHour); 232 dest.writeInt(mMinute); 233 } 234 235 public static final Parcelable.Creator<SavedState> CREATOR 236 = new Creator<SavedState>() { 237 public SavedState createFromParcel(Parcel in) { 238 return new SavedState(in); 239 } 240 241 public SavedState[] newArray(int size) { 242 return new SavedState[size]; 243 } 244 }; 245 } 246 247 @Override onSaveInstanceState()248 protected Parcelable onSaveInstanceState() { 249 Parcelable superState = super.onSaveInstanceState(); 250 return new SavedState(superState, mCurrentHour, mCurrentMinute); 251 } 252 253 @Override onRestoreInstanceState(Parcelable state)254 protected void onRestoreInstanceState(Parcelable state) { 255 SavedState ss = (SavedState) state; 256 super.onRestoreInstanceState(ss.getSuperState()); 257 setCurrentHour(ss.getHour()); 258 setCurrentMinute(ss.getMinute()); 259 } 260 261 /** 262 * Set the callback that indicates the time has been adjusted by the user. 263 * @param onTimeChangedListener the callback, should not be null. 264 */ setOnTimeChangedListener(OnTimeChangedListener onTimeChangedListener)265 public void setOnTimeChangedListener(OnTimeChangedListener onTimeChangedListener) { 266 mOnTimeChangedListener = onTimeChangedListener; 267 } 268 269 /** 270 * @return The current hour (0-23). 271 */ getCurrentHour()272 public Integer getCurrentHour() { 273 return mCurrentHour; 274 } 275 276 /** 277 * Set the current hour. 278 */ setCurrentHour(Integer currentHour)279 public void setCurrentHour(Integer currentHour) { 280 this.mCurrentHour = currentHour; 281 updateHourDisplay(); 282 } 283 284 /** 285 * Set whether in 24 hour or AM/PM mode. 286 * @param is24HourView True = 24 hour mode. False = AM/PM. 287 */ setIs24HourView(Boolean is24HourView)288 public void setIs24HourView(Boolean is24HourView) { 289 if (mIs24HourView != is24HourView) { 290 mIs24HourView = is24HourView; 291 configurePickerRanges(); 292 updateHourDisplay(); 293 } 294 } 295 296 /** 297 * @return true if this is in 24 hour view else false. 298 */ is24HourView()299 public boolean is24HourView() { 300 return mIs24HourView; 301 } 302 303 /** 304 * @return The current minute. 305 */ getCurrentMinute()306 public Integer getCurrentMinute() { 307 return mCurrentMinute; 308 } 309 310 /** 311 * Set the current minute (0-59). 312 */ setCurrentMinute(Integer currentMinute)313 public void setCurrentMinute(Integer currentMinute) { 314 this.mCurrentMinute = currentMinute; 315 updateMinuteDisplay(); 316 } 317 318 @Override getBaseline()319 public int getBaseline() { 320 return mHourPicker.getBaseline(); 321 } 322 323 /** 324 * Set the state of the spinners appropriate to the current hour. 325 */ updateHourDisplay()326 private void updateHourDisplay() { 327 int currentHour = mCurrentHour; 328 if (!mIs24HourView) { 329 // convert [0,23] ordinal to wall clock display 330 if (currentHour > 12) currentHour -= 12; 331 else if (currentHour == 0) currentHour = 12; 332 } 333 mHourPicker.setCurrent(currentHour); 334 mIsAm = mCurrentHour < 12; 335 mAmPmButton.setText(mIsAm ? mAmText : mPmText); 336 onTimeChanged(); 337 } 338 339 private void configurePickerRanges() { 340 if (mIs24HourView) { 341 mHourPicker.setRange(0, 23); 342 mHourPicker.setFormatter(NumberPicker.TWO_DIGIT_FORMATTER); 343 mAmPmButton.setVisibility(View.GONE); 344 } else { 345 mHourPicker.setRange(1, 12); 346 mHourPicker.setFormatter(null); 347 mAmPmButton.setVisibility(View.VISIBLE); 348 } 349 } 350 351 private void onTimeChanged() { 352 mOnTimeChangedListener.onTimeChanged(this, getCurrentHour(), getCurrentMinute()); 353 } 354 355 /** 356 * Set the state of the spinners appropriate to the current minute. 357 */ 358 private void updateMinuteDisplay() { 359 mMinutePicker.setCurrent(mCurrentMinute); 360 mOnTimeChangedListener.onTimeChanged(this, getCurrentHour(), getCurrentMinute()); 361 } 362 } 363