blob: 863d409138362a78971f6d67b059efb02d21dde3 [file] [log] [blame]
Fabrice Di Meglioeeff63a2013-08-05 12:07:24 -07001/*
2 * Copyright (C) 2013 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
Fabrice Di Meglioeeff63a2013-08-05 12:07:24 -070019import android.content.Context;
Fabrice Di Meglioeeff63a2013-08-05 12:07:24 -070020import android.content.res.TypedArray;
21import android.os.Parcel;
22import android.os.Parcelable;
Fabrice Di Meglioeeff63a2013-08-05 12:07:24 -070023import android.text.format.DateFormat;
Fabrice Di Meglio014b8cf2013-12-18 12:51:22 -080024import android.text.format.DateUtils;
Fabrice Di Meglioeeff63a2013-08-05 12:07:24 -070025import android.util.AttributeSet;
Fabrice Di Meglioeeff63a2013-08-05 12:07:24 -070026import android.view.LayoutInflater;
27import android.view.View;
28import android.view.ViewGroup;
29import android.view.accessibility.AccessibilityEvent;
Alan Viverettedaf33ed2014-10-23 13:34:17 -070030import android.view.inputmethod.EditorInfo;
31import android.view.inputmethod.InputMethodManager;
Fabrice Di Meglioeeff63a2013-08-05 12:07:24 -070032import com.android.internal.R;
33
Fabrice Di Meglioeeff63a2013-08-05 12:07:24 -070034import java.util.Calendar;
Alan Viverette68016a62015-11-19 17:10:54 -050035import java.util.Locale;
Fabrice Di Meglioeeff63a2013-08-05 12:07:24 -070036
Alan Viverettedaf33ed2014-10-23 13:34:17 -070037import libcore.icu.LocaleData;
38
39import static android.view.View.IMPORTANT_FOR_ACCESSIBILITY_AUTO;
40import static android.view.View.IMPORTANT_FOR_ACCESSIBILITY_YES;
41
Fabrice Di Meglioeeff63a2013-08-05 12:07:24 -070042/**
Alan Viverettedaf33ed2014-10-23 13:34:17 -070043 * A delegate implementing the basic spinner-based TimePicker.
Fabrice Di Meglioeeff63a2013-08-05 12:07:24 -070044 */
Alan Viverettedaf33ed2014-10-23 13:34:17 -070045class TimePickerSpinnerDelegate extends TimePicker.AbstractTimePickerDelegate {
Fabrice Di Meglioeeff63a2013-08-05 12:07:24 -070046 private static final boolean DEFAULT_ENABLED_STATE = true;
Fabrice Di Meglioeeff63a2013-08-05 12:07:24 -070047 private static final int HOURS_IN_HALF_DAY = 12;
48
Alan Viverettedaf33ed2014-10-23 13:34:17 -070049 private final NumberPicker mHourSpinner;
50 private final NumberPicker mMinuteSpinner;
51 private final NumberPicker mAmPmSpinner;
52 private final EditText mHourSpinnerInput;
53 private final EditText mMinuteSpinnerInput;
54 private final EditText mAmPmSpinnerInput;
55 private final TextView mDivider;
Fabrice Di Meglioeeff63a2013-08-05 12:07:24 -070056
Alan Viverettedaf33ed2014-10-23 13:34:17 -070057 // Note that the legacy implementation of the TimePicker is
58 // using a button for toggling between AM/PM while the new
59 // version uses a NumberPicker spinner. Therefore the code
60 // accommodates these two cases to be backwards compatible.
61 private final Button mAmPmButton;
Fabrice Di Meglioeeff63a2013-08-05 12:07:24 -070062
Alan Viverettedaf33ed2014-10-23 13:34:17 -070063 private final String[] mAmPmStrings;
Alan Viverette26c563b2014-10-17 12:49:16 -070064
Alan Viverette68016a62015-11-19 17:10:54 -050065 private final Calendar mTempCalendar;
66
Alan Viverettedaf33ed2014-10-23 13:34:17 -070067 private boolean mIsEnabled = DEFAULT_ENABLED_STATE;
Alan Viverettedaf33ed2014-10-23 13:34:17 -070068 private boolean mHourWithTwoDigit;
69 private char mHourFormat;
Fabrice Di Meglio014b8cf2013-12-18 12:51:22 -080070
Alan Viverette68016a62015-11-19 17:10:54 -050071 private boolean mIs24HourView;
72 private boolean mIsAm;
73
Chet Haase3053b2f2014-08-06 07:51:50 -070074 public TimePickerSpinnerDelegate(TimePicker delegator, Context context, AttributeSet attrs,
75 int defStyleAttr, int defStyleRes) {
Fabrice Di Meglioeeff63a2013-08-05 12:07:24 -070076 super(delegator, context);
77
78 // process style attributes
Alan Viverettedaf33ed2014-10-23 13:34:17 -070079 final TypedArray a = mContext.obtainStyledAttributes(
80 attrs, R.styleable.TimePicker, defStyleAttr, defStyleRes);
81 final int layoutResourceId = a.getResourceId(
82 R.styleable.TimePicker_legacyLayout, R.layout.time_picker_legacy);
Alan Viverette60727e02014-07-28 16:56:32 -070083 a.recycle();
Alan Viverette51344782014-07-16 17:39:27 -070084
Alan Viverettedaf33ed2014-10-23 13:34:17 -070085 final LayoutInflater inflater = LayoutInflater.from(mContext);
86 inflater.inflate(layoutResourceId, mDelegator, true);
Alan Viverette67945c12014-10-15 13:20:47 -070087
Alan Viverettedaf33ed2014-10-23 13:34:17 -070088 // hour
89 mHourSpinner = (NumberPicker) delegator.findViewById(R.id.hour);
90 mHourSpinner.setOnValueChangedListener(new NumberPicker.OnValueChangeListener() {
91 public void onValueChange(NumberPicker spinner, int oldVal, int newVal) {
92 updateInputState();
Alan Viverette4420ae82015-11-16 16:10:56 -050093 if (!is24Hour()) {
Alan Viverettedaf33ed2014-10-23 13:34:17 -070094 if ((oldVal == HOURS_IN_HALF_DAY - 1 && newVal == HOURS_IN_HALF_DAY) ||
95 (oldVal == HOURS_IN_HALF_DAY && newVal == HOURS_IN_HALF_DAY - 1)) {
96 mIsAm = !mIsAm;
97 updateAmPmControl();
98 }
99 }
100 onTimeChanged();
101 }
102 });
103 mHourSpinnerInput = (EditText) mHourSpinner.findViewById(R.id.numberpicker_input);
104 mHourSpinnerInput.setImeOptions(EditorInfo.IME_ACTION_NEXT);
Fabrice Di Meglioeeff63a2013-08-05 12:07:24 -0700105
Alan Viverettedaf33ed2014-10-23 13:34:17 -0700106 // divider (only for the new widget style)
107 mDivider = (TextView) mDelegator.findViewById(R.id.divider);
108 if (mDivider != null) {
109 setDividerText();
Fabrice Di Meglioeeff63a2013-08-05 12:07:24 -0700110 }
Fabrice Di Meglioeeff63a2013-08-05 12:07:24 -0700111
Alan Viverettedaf33ed2014-10-23 13:34:17 -0700112 // minute
113 mMinuteSpinner = (NumberPicker) mDelegator.findViewById(R.id.minute);
114 mMinuteSpinner.setMinValue(0);
115 mMinuteSpinner.setMaxValue(59);
116 mMinuteSpinner.setOnLongPressUpdateInterval(100);
117 mMinuteSpinner.setFormatter(NumberPicker.getTwoDigitFormatter());
118 mMinuteSpinner.setOnValueChangedListener(new NumberPicker.OnValueChangeListener() {
119 public void onValueChange(NumberPicker spinner, int oldVal, int newVal) {
120 updateInputState();
121 int minValue = mMinuteSpinner.getMinValue();
122 int maxValue = mMinuteSpinner.getMaxValue();
123 if (oldVal == maxValue && newVal == minValue) {
124 int newHour = mHourSpinner.getValue() + 1;
Alan Viverette4420ae82015-11-16 16:10:56 -0500125 if (!is24Hour() && newHour == HOURS_IN_HALF_DAY) {
Alan Viverettedaf33ed2014-10-23 13:34:17 -0700126 mIsAm = !mIsAm;
127 updateAmPmControl();
128 }
129 mHourSpinner.setValue(newHour);
130 } else if (oldVal == minValue && newVal == maxValue) {
131 int newHour = mHourSpinner.getValue() - 1;
Alan Viverette4420ae82015-11-16 16:10:56 -0500132 if (!is24Hour() && newHour == HOURS_IN_HALF_DAY - 1) {
Alan Viverettedaf33ed2014-10-23 13:34:17 -0700133 mIsAm = !mIsAm;
134 updateAmPmControl();
135 }
136 mHourSpinner.setValue(newHour);
137 }
138 onTimeChanged();
139 }
140 });
141 mMinuteSpinnerInput = (EditText) mMinuteSpinner.findViewById(R.id.numberpicker_input);
142 mMinuteSpinnerInput.setImeOptions(EditorInfo.IME_ACTION_NEXT);
143
144 // Get the localized am/pm strings and use them in the spinner.
145 mAmPmStrings = getAmPmStrings(context);
146
147 // am/pm
148 final View amPmView = mDelegator.findViewById(R.id.amPm);
149 if (amPmView instanceof Button) {
150 mAmPmSpinner = null;
151 mAmPmSpinnerInput = null;
152 mAmPmButton = (Button) amPmView;
153 mAmPmButton.setOnClickListener(new View.OnClickListener() {
154 public void onClick(View button) {
155 button.requestFocus();
156 mIsAm = !mIsAm;
157 updateAmPmControl();
158 onTimeChanged();
159 }
160 });
Fabrice Di Meglioeeff63a2013-08-05 12:07:24 -0700161 } else {
Alan Viverettedaf33ed2014-10-23 13:34:17 -0700162 mAmPmButton = null;
163 mAmPmSpinner = (NumberPicker) amPmView;
164 mAmPmSpinner.setMinValue(0);
165 mAmPmSpinner.setMaxValue(1);
166 mAmPmSpinner.setDisplayedValues(mAmPmStrings);
167 mAmPmSpinner.setOnValueChangedListener(new NumberPicker.OnValueChangeListener() {
168 public void onValueChange(NumberPicker picker, int oldVal, int newVal) {
169 updateInputState();
170 picker.requestFocus();
171 mIsAm = !mIsAm;
172 updateAmPmControl();
173 onTimeChanged();
174 }
175 });
176 mAmPmSpinnerInput = (EditText) mAmPmSpinner.findViewById(R.id.numberpicker_input);
177 mAmPmSpinnerInput.setImeOptions(EditorInfo.IME_ACTION_DONE);
Fabrice Di Meglioeeff63a2013-08-05 12:07:24 -0700178 }
Alan Viverettedaf33ed2014-10-23 13:34:17 -0700179
180 if (isAmPmAtStart()) {
181 // Move the am/pm view to the beginning
182 ViewGroup amPmParent = (ViewGroup) delegator.findViewById(R.id.timePickerLayout);
183 amPmParent.removeView(amPmView);
184 amPmParent.addView(amPmView, 0);
185 // Swap layout margins if needed. They may be not symmetrical (Old Standard Theme
186 // for example and not for Holo Theme)
187 ViewGroup.MarginLayoutParams lp =
188 (ViewGroup.MarginLayoutParams) amPmView.getLayoutParams();
189 final int startMargin = lp.getMarginStart();
190 final int endMargin = lp.getMarginEnd();
191 if (startMargin != endMargin) {
192 lp.setMarginStart(endMargin);
193 lp.setMarginEnd(startMargin);
194 }
195 }
196
197 getHourFormatData();
198
199 // update controls to initial state
200 updateHourControl();
201 updateMinuteControl();
202 updateAmPmControl();
203
204 // set to current time
Alan Viverette68016a62015-11-19 17:10:54 -0500205 mTempCalendar = Calendar.getInstance(mLocale);
Alan Viverette4420ae82015-11-16 16:10:56 -0500206 setHour(mTempCalendar.get(Calendar.HOUR_OF_DAY));
207 setMinute(mTempCalendar.get(Calendar.MINUTE));
Alan Viverettedaf33ed2014-10-23 13:34:17 -0700208
209 if (!isEnabled()) {
210 setEnabled(false);
211 }
212
213 // set the content descriptions
214 setContentDescriptions();
215
216 // If not explicitly specified this view is important for accessibility.
217 if (mDelegator.getImportantForAccessibility() == IMPORTANT_FOR_ACCESSIBILITY_AUTO) {
218 mDelegator.setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_YES);
219 }
220 }
221
222 private void getHourFormatData() {
Alan Viverette4420ae82015-11-16 16:10:56 -0500223 final String bestDateTimePattern = DateFormat.getBestDateTimePattern(mLocale,
Alan Viverettedaf33ed2014-10-23 13:34:17 -0700224 (mIs24HourView) ? "Hm" : "hm");
225 final int lengthPattern = bestDateTimePattern.length();
226 mHourWithTwoDigit = false;
227 char hourFormat = '\0';
228 // Check if the returned pattern is single or double 'H', 'h', 'K', 'k'. We also save
229 // the hour format that we found.
230 for (int i = 0; i < lengthPattern; i++) {
231 final char c = bestDateTimePattern.charAt(i);
232 if (c == 'H' || c == 'h' || c == 'K' || c == 'k') {
233 mHourFormat = c;
234 if (i + 1 < lengthPattern && c == bestDateTimePattern.charAt(i + 1)) {
235 mHourWithTwoDigit = true;
236 }
237 break;
238 }
239 }
240 }
241
242 private boolean isAmPmAtStart() {
Alan Viverette4420ae82015-11-16 16:10:56 -0500243 final String bestDateTimePattern = DateFormat.getBestDateTimePattern(mLocale,
Alan Viverettedaf33ed2014-10-23 13:34:17 -0700244 "hm" /* skeleton */);
245
246 return bestDateTimePattern.startsWith("a");
Fabrice Di Meglioeeff63a2013-08-05 12:07:24 -0700247 }
248
Fabrice Di Meglioeeff63a2013-08-05 12:07:24 -0700249 /**
Alan Viverettedaf33ed2014-10-23 13:34:17 -0700250 * The time separator is defined in the Unicode CLDR and cannot be supposed to be ":".
251 *
252 * See http://unicode.org/cldr/trac/browser/trunk/common/main
253 *
254 * We pass the correct "skeleton" depending on 12 or 24 hours view and then extract the
255 * separator as the character which is just after the hour marker in the returned pattern.
Fabrice Di Meglioeeff63a2013-08-05 12:07:24 -0700256 */
Alan Viverettedaf33ed2014-10-23 13:34:17 -0700257 private void setDividerText() {
258 final String skeleton = (mIs24HourView) ? "Hm" : "hm";
Alan Viverette4420ae82015-11-16 16:10:56 -0500259 final String bestDateTimePattern = DateFormat.getBestDateTimePattern(mLocale,
Alan Viverettedaf33ed2014-10-23 13:34:17 -0700260 skeleton);
261 final String separatorText;
262 int hourIndex = bestDateTimePattern.lastIndexOf('H');
263 if (hourIndex == -1) {
264 hourIndex = bestDateTimePattern.lastIndexOf('h');
265 }
266 if (hourIndex == -1) {
267 // Default case
268 separatorText = ":";
269 } else {
270 int minuteIndex = bestDateTimePattern.indexOf('m', hourIndex + 1);
271 if (minuteIndex == -1) {
272 separatorText = Character.toString(bestDateTimePattern.charAt(hourIndex + 1));
273 } else {
274 separatorText = bestDateTimePattern.substring(hourIndex + 1, minuteIndex);
275 }
276 }
277 mDivider.setText(separatorText);
278 }
279
Fabrice Di Meglioeeff63a2013-08-05 12:07:24 -0700280 @Override
Alan Viverette4420ae82015-11-16 16:10:56 -0500281 public void setHour(int hour) {
282 setCurrentHour(hour, true);
Fabrice Di Meglioeeff63a2013-08-05 12:07:24 -0700283 }
284
Alan Viverette646a0f82015-03-18 13:24:07 -0700285 private void setCurrentHour(int currentHour, boolean notifyTimeChanged) {
Alan Viverettedaf33ed2014-10-23 13:34:17 -0700286 // why was Integer used in the first place?
Alan Viverette4420ae82015-11-16 16:10:56 -0500287 if (currentHour == getHour()) {
Alan Viverettedaf33ed2014-10-23 13:34:17 -0700288 return;
289 }
Alan Viverette4420ae82015-11-16 16:10:56 -0500290 if (!is24Hour()) {
Alan Viverettedaf33ed2014-10-23 13:34:17 -0700291 // convert [0,23] ordinal to wall clock display
292 if (currentHour >= HOURS_IN_HALF_DAY) {
293 mIsAm = false;
294 if (currentHour > HOURS_IN_HALF_DAY) {
295 currentHour = currentHour - HOURS_IN_HALF_DAY;
296 }
297 } else {
298 mIsAm = true;
299 if (currentHour == 0) {
300 currentHour = HOURS_IN_HALF_DAY;
301 }
302 }
303 updateAmPmControl();
304 }
305 mHourSpinner.setValue(currentHour);
306 if (notifyTimeChanged) {
307 onTimeChanged();
308 }
309 }
310
Fabrice Di Meglioeeff63a2013-08-05 12:07:24 -0700311 @Override
Alan Viverette4420ae82015-11-16 16:10:56 -0500312 public int getHour() {
Alan Viverettedaf33ed2014-10-23 13:34:17 -0700313 int currentHour = mHourSpinner.getValue();
Alan Viverette4420ae82015-11-16 16:10:56 -0500314 if (is24Hour()) {
Fabrice Di Meglioeeff63a2013-08-05 12:07:24 -0700315 return currentHour;
Alan Viverettedaf33ed2014-10-23 13:34:17 -0700316 } else if (mIsAm) {
317 return currentHour % HOURS_IN_HALF_DAY;
Fabrice Di Meglioeeff63a2013-08-05 12:07:24 -0700318 } else {
Alan Viverettedaf33ed2014-10-23 13:34:17 -0700319 return (currentHour % HOURS_IN_HALF_DAY) + HOURS_IN_HALF_DAY;
Fabrice Di Meglioeeff63a2013-08-05 12:07:24 -0700320 }
321 }
322
Fabrice Di Meglioeeff63a2013-08-05 12:07:24 -0700323 @Override
Alan Viverette4420ae82015-11-16 16:10:56 -0500324 public void setMinute(int minute) {
325 if (minute == getMinute()) {
Fabrice Di Meglioeeff63a2013-08-05 12:07:24 -0700326 return;
327 }
Alan Viverette4420ae82015-11-16 16:10:56 -0500328 mMinuteSpinner.setValue(minute);
Fabrice Di Meglioeeff63a2013-08-05 12:07:24 -0700329 onTimeChanged();
330 }
331
Fabrice Di Meglioeeff63a2013-08-05 12:07:24 -0700332 @Override
Alan Viverette4420ae82015-11-16 16:10:56 -0500333 public int getMinute() {
Alan Viverettedaf33ed2014-10-23 13:34:17 -0700334 return mMinuteSpinner.getValue();
Fabrice Di Meglioeeff63a2013-08-05 12:07:24 -0700335 }
336
Alan Viverette4420ae82015-11-16 16:10:56 -0500337 public void setIs24Hour(boolean is24Hour) {
338 if (mIs24HourView == is24Hour) {
Fabrice Di Meglioeeff63a2013-08-05 12:07:24 -0700339 return;
340 }
Alan Viverettedaf33ed2014-10-23 13:34:17 -0700341 // cache the current hour since spinner range changes and BEFORE changing mIs24HourView!!
Alan Viverette4420ae82015-11-16 16:10:56 -0500342 int currentHour = getHour();
Alan Viverettedaf33ed2014-10-23 13:34:17 -0700343 // Order is important here.
Alan Viverette4420ae82015-11-16 16:10:56 -0500344 mIs24HourView = is24Hour;
Alan Viverettedaf33ed2014-10-23 13:34:17 -0700345 getHourFormatData();
346 updateHourControl();
347 // set value after spinner range is updated
348 setCurrentHour(currentHour, false);
349 updateMinuteControl();
350 updateAmPmControl();
Fabrice Di Meglioeeff63a2013-08-05 12:07:24 -0700351 }
352
Fabrice Di Meglioeeff63a2013-08-05 12:07:24 -0700353 @Override
Alan Viverette4420ae82015-11-16 16:10:56 -0500354 public boolean is24Hour() {
Fabrice Di Meglioeeff63a2013-08-05 12:07:24 -0700355 return mIs24HourView;
356 }
357
358 @Override
Alan Viverettedaf33ed2014-10-23 13:34:17 -0700359 public void setOnTimeChangedListener(TimePicker.OnTimeChangedListener onTimeChangedListener) {
360 mOnTimeChangedListener = onTimeChangedListener;
Fabrice Di Meglioeeff63a2013-08-05 12:07:24 -0700361 }
362
363 @Override
364 public void setEnabled(boolean enabled) {
Alan Viverettedaf33ed2014-10-23 13:34:17 -0700365 mMinuteSpinner.setEnabled(enabled);
366 if (mDivider != null) {
367 mDivider.setEnabled(enabled);
368 }
369 mHourSpinner.setEnabled(enabled);
370 if (mAmPmSpinner != null) {
371 mAmPmSpinner.setEnabled(enabled);
372 } else {
373 mAmPmButton.setEnabled(enabled);
374 }
Fabrice Di Meglioeeff63a2013-08-05 12:07:24 -0700375 mIsEnabled = enabled;
376 }
377
378 @Override
379 public boolean isEnabled() {
380 return mIsEnabled;
381 }
382
383 @Override
Fabrice Di Meglioeeff63a2013-08-05 12:07:24 -0700384 public int getBaseline() {
Alan Viverettedaf33ed2014-10-23 13:34:17 -0700385 return mHourSpinner.getBaseline();
Fabrice Di Meglioeeff63a2013-08-05 12:07:24 -0700386 }
387
388 @Override
Fabrice Di Meglioeeff63a2013-08-05 12:07:24 -0700389 public Parcelable onSaveInstanceState(Parcelable superState) {
Alan Viverette4420ae82015-11-16 16:10:56 -0500390 return new SavedState(superState, getHour(), getMinute());
Fabrice Di Meglioeeff63a2013-08-05 12:07:24 -0700391 }
392
393 @Override
394 public void onRestoreInstanceState(Parcelable state) {
395 SavedState ss = (SavedState) state;
Alan Viverette4420ae82015-11-16 16:10:56 -0500396 setHour(ss.getHour());
397 setMinute(ss.getMinute());
Fabrice Di Meglio014b8cf2013-12-18 12:51:22 -0800398 }
399
400 @Override
Fabrice Di Meglioeeff63a2013-08-05 12:07:24 -0700401 public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {
Fabrice Di Meglio014b8cf2013-12-18 12:51:22 -0800402 onPopulateAccessibilityEvent(event);
403 return true;
Fabrice Di Meglioeeff63a2013-08-05 12:07:24 -0700404 }
405
406 @Override
407 public void onPopulateAccessibilityEvent(AccessibilityEvent event) {
Fabrice Di Meglio014b8cf2013-12-18 12:51:22 -0800408 int flags = DateUtils.FORMAT_SHOW_TIME;
409 if (mIs24HourView) {
410 flags |= DateUtils.FORMAT_24HOUR;
411 } else {
412 flags |= DateUtils.FORMAT_12HOUR;
413 }
Alan Viverette4420ae82015-11-16 16:10:56 -0500414 mTempCalendar.set(Calendar.HOUR_OF_DAY, getHour());
415 mTempCalendar.set(Calendar.MINUTE, getMinute());
Alan Viverettedaf33ed2014-10-23 13:34:17 -0700416 String selectedDateUtterance = DateUtils.formatDateTime(mContext,
Fabrice Di Meglio014b8cf2013-12-18 12:51:22 -0800417 mTempCalendar.getTimeInMillis(), flags);
Alan Viverettedaf33ed2014-10-23 13:34:17 -0700418 event.getText().add(selectedDateUtterance);
Fabrice Di Meglioeeff63a2013-08-05 12:07:24 -0700419 }
420
Alan Viverettedaf33ed2014-10-23 13:34:17 -0700421 private void updateInputState() {
422 // Make sure that if the user changes the value and the IME is active
423 // for one of the inputs if this widget, the IME is closed. If the user
424 // changed the value via the IME and there is a next input the IME will
425 // be shown, otherwise the user chose another means of changing the
426 // value and having the IME up makes no sense.
427 InputMethodManager inputMethodManager = InputMethodManager.peekInstance();
428 if (inputMethodManager != null) {
429 if (inputMethodManager.isActive(mHourSpinnerInput)) {
430 mHourSpinnerInput.clearFocus();
431 inputMethodManager.hideSoftInputFromWindow(mDelegator.getWindowToken(), 0);
432 } else if (inputMethodManager.isActive(mMinuteSpinnerInput)) {
433 mMinuteSpinnerInput.clearFocus();
434 inputMethodManager.hideSoftInputFromWindow(mDelegator.getWindowToken(), 0);
435 } else if (inputMethodManager.isActive(mAmPmSpinnerInput)) {
436 mAmPmSpinnerInput.clearFocus();
437 inputMethodManager.hideSoftInputFromWindow(mDelegator.getWindowToken(), 0);
438 }
439 }
440 }
441
442 private void updateAmPmControl() {
Alan Viverette4420ae82015-11-16 16:10:56 -0500443 if (is24Hour()) {
Alan Viverettedaf33ed2014-10-23 13:34:17 -0700444 if (mAmPmSpinner != null) {
445 mAmPmSpinner.setVisibility(View.GONE);
446 } else {
447 mAmPmButton.setVisibility(View.GONE);
448 }
449 } else {
450 int index = mIsAm ? Calendar.AM : Calendar.PM;
451 if (mAmPmSpinner != null) {
452 mAmPmSpinner.setValue(index);
453 mAmPmSpinner.setVisibility(View.VISIBLE);
454 } else {
455 mAmPmButton.setText(mAmPmStrings[index]);
456 mAmPmButton.setVisibility(View.VISIBLE);
457 }
458 }
459 mDelegator.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_SELECTED);
460 }
461
Fabrice Di Meglioeeff63a2013-08-05 12:07:24 -0700462 private void onTimeChanged() {
463 mDelegator.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_SELECTED);
464 if (mOnTimeChangedListener != null) {
Alan Viverette4420ae82015-11-16 16:10:56 -0500465 mOnTimeChangedListener.onTimeChanged(mDelegator, getHour(),
466 getMinute());
Alan Viverettedaf33ed2014-10-23 13:34:17 -0700467 }
468 }
469
470 private void updateHourControl() {
Alan Viverette4420ae82015-11-16 16:10:56 -0500471 if (is24Hour()) {
Alan Viverettedaf33ed2014-10-23 13:34:17 -0700472 // 'k' means 1-24 hour
473 if (mHourFormat == 'k') {
474 mHourSpinner.setMinValue(1);
475 mHourSpinner.setMaxValue(24);
476 } else {
477 mHourSpinner.setMinValue(0);
478 mHourSpinner.setMaxValue(23);
479 }
480 } else {
481 // 'K' means 0-11 hour
482 if (mHourFormat == 'K') {
483 mHourSpinner.setMinValue(0);
484 mHourSpinner.setMaxValue(11);
485 } else {
486 mHourSpinner.setMinValue(1);
487 mHourSpinner.setMaxValue(12);
488 }
489 }
490 mHourSpinner.setFormatter(mHourWithTwoDigit ? NumberPicker.getTwoDigitFormatter() : null);
491 }
492
493 private void updateMinuteControl() {
Alan Viverette4420ae82015-11-16 16:10:56 -0500494 if (is24Hour()) {
Alan Viverettedaf33ed2014-10-23 13:34:17 -0700495 mMinuteSpinnerInput.setImeOptions(EditorInfo.IME_ACTION_DONE);
496 } else {
497 mMinuteSpinnerInput.setImeOptions(EditorInfo.IME_ACTION_NEXT);
498 }
499 }
500
501 private void setContentDescriptions() {
502 // Minute
503 trySetContentDescription(mMinuteSpinner, R.id.increment,
504 R.string.time_picker_increment_minute_button);
505 trySetContentDescription(mMinuteSpinner, R.id.decrement,
506 R.string.time_picker_decrement_minute_button);
507 // Hour
508 trySetContentDescription(mHourSpinner, R.id.increment,
509 R.string.time_picker_increment_hour_button);
510 trySetContentDescription(mHourSpinner, R.id.decrement,
511 R.string.time_picker_decrement_hour_button);
512 // AM/PM
513 if (mAmPmSpinner != null) {
514 trySetContentDescription(mAmPmSpinner, R.id.increment,
515 R.string.time_picker_increment_set_pm_button);
516 trySetContentDescription(mAmPmSpinner, R.id.decrement,
517 R.string.time_picker_decrement_set_am_button);
518 }
519 }
520
521 private void trySetContentDescription(View root, int viewId, int contDescResId) {
522 View target = root.findViewById(viewId);
523 if (target != null) {
524 target.setContentDescription(mContext.getString(contDescResId));
Fabrice Di Meglioeeff63a2013-08-05 12:07:24 -0700525 }
526 }
527
528 /**
529 * Used to save / restore state of time picker
530 */
531 private static class SavedState extends View.BaseSavedState {
Fabrice Di Meglioeeff63a2013-08-05 12:07:24 -0700532 private final int mHour;
533 private final int mMinute;
Fabrice Di Meglioeeff63a2013-08-05 12:07:24 -0700534
Alan Viverettedaf33ed2014-10-23 13:34:17 -0700535 private SavedState(Parcelable superState, int hour, int minute) {
Fabrice Di Meglioeeff63a2013-08-05 12:07:24 -0700536 super(superState);
537 mHour = hour;
538 mMinute = minute;
Fabrice Di Meglioeeff63a2013-08-05 12:07:24 -0700539 }
540
541 private SavedState(Parcel in) {
542 super(in);
543 mHour = in.readInt();
544 mMinute = in.readInt();
Fabrice Di Meglioeeff63a2013-08-05 12:07:24 -0700545 }
546
547 public int getHour() {
548 return mHour;
549 }
550
551 public int getMinute() {
552 return mMinute;
553 }
554
Fabrice Di Meglioeeff63a2013-08-05 12:07:24 -0700555 @Override
556 public void writeToParcel(Parcel dest, int flags) {
557 super.writeToParcel(dest, flags);
558 dest.writeInt(mHour);
559 dest.writeInt(mMinute);
Fabrice Di Meglioeeff63a2013-08-05 12:07:24 -0700560 }
561
562 @SuppressWarnings({"unused", "hiding"})
563 public static final Parcelable.Creator<SavedState> CREATOR = new Creator<SavedState>() {
564 public SavedState createFromParcel(Parcel in) {
565 return new SavedState(in);
566 }
567
568 public SavedState[] newArray(int size) {
569 return new SavedState[size];
570 }
571 };
572 }
573
Alan Viverettedaf33ed2014-10-23 13:34:17 -0700574 public static String[] getAmPmStrings(Context context) {
575 String[] result = new String[2];
576 LocaleData d = LocaleData.get(context.getResources().getConfiguration().locale);
Narayan Kamath14b50392015-07-08 18:45:39 +0100577 result[0] = d.amPm[0].length() > 4 ? d.narrowAm : d.amPm[0];
578 result[1] = d.amPm[1].length() > 4 ? d.narrowPm : d.amPm[1];
Alan Viverettedaf33ed2014-10-23 13:34:17 -0700579 return result;
Fabrice Di Meglioeeff63a2013-08-05 12:07:24 -0700580 }
Fabrice Di Meglioeeff63a2013-08-05 12:07:24 -0700581}