1 // Copyright 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 package org.chromium.content.browser.input;
7 import android.app.AlertDialog;
8 import android.content.Context;
9 import android.content.DialogInterface;
10 import android.content.DialogInterface.OnClickListener;
11 import android.view.LayoutInflater;
12 import android.view.View;
13 import android.widget.NumberPicker;
15 import org.chromium.content.R;
17 import java.util.ArrayList;
20 * A time picker dialog with upto 5 number pickers left to right:
21 * hour, minute, second, milli, AM/PM.
23 * If is24hourFormat is true then AM/PM picker is not displayed and
24 * hour range is 0..23. Otherwise hour range is 1..12.
25 * The milli picker is not displayed if step >= SECOND_IN_MILLIS
26 * The second picker is not displayed if step >= MINUTE_IN_MILLIS.
28 public class MultiFieldTimePickerDialog
29 extends AlertDialog implements OnClickListener {
31 private final NumberPicker mHourSpinner;
32 private final NumberPicker mMinuteSpinner;
33 private final NumberPicker mSecSpinner;
34 private final NumberPicker mMilliSpinner;
35 private final NumberPicker mAmPmSpinner;
36 private final OnMultiFieldTimeSetListener mListener;
37 private final int mStep;
38 private final int mBaseMilli;
39 private final boolean mIs24hourFormat;
41 public interface OnMultiFieldTimeSetListener {
42 void onTimeSet(int hourOfDay, int minute, int second, int milli);
45 private static final int SECOND_IN_MILLIS = 1000;
46 private static final int MINUTE_IN_MILLIS = 60 * SECOND_IN_MILLIS;
47 private static final int HOUR_IN_MILLIS = 60 * MINUTE_IN_MILLIS;
49 public MultiFieldTimePickerDialog(
52 int hour, int minute, int second, int milli,
53 int min, int max, int step, boolean is24hourFormat,
54 OnMultiFieldTimeSetListener listener) {
55 super(context, theme);
58 mIs24hourFormat = is24hourFormat;
62 max = 24 * HOUR_IN_MILLIS - 1;
64 if (step < 0 || step >= 24 * HOUR_IN_MILLIS) {
65 step = MINUTE_IN_MILLIS;
68 LayoutInflater inflater =
69 (LayoutInflater) context.getSystemService(
70 Context.LAYOUT_INFLATER_SERVICE);
71 View view = inflater.inflate(R.layout.multi_field_time_picker_dialog, null);
74 mHourSpinner = (NumberPicker) view.findViewById(R.id.hour);
75 mMinuteSpinner = (NumberPicker) view.findViewById(R.id.minute);
76 mSecSpinner = (NumberPicker) view.findViewById(R.id.second);
77 mMilliSpinner = (NumberPicker) view.findViewById(R.id.milli);
78 mAmPmSpinner = (NumberPicker) view.findViewById(R.id.ampm);
80 int minHour = min / HOUR_IN_MILLIS;
81 int maxHour = max / HOUR_IN_MILLIS;
82 min -= minHour * HOUR_IN_MILLIS;
83 max -= maxHour * HOUR_IN_MILLIS;
85 if (minHour == maxHour) {
86 mHourSpinner.setEnabled(false);
91 mAmPmSpinner.setVisibility(View.GONE);
93 int minAmPm = minHour / 12;
94 int maxAmPm = maxHour / 12;
96 mAmPmSpinner.setMinValue(minAmPm);
97 mAmPmSpinner.setMaxValue(maxAmPm);
98 mAmPmSpinner.setDisplayedValues(new String[] {
99 context.getString(R.string.time_picker_dialog_am),
100 context.getString(R.string.time_picker_dialog_pm)
107 if (minAmPm == maxAmPm) {
108 mAmPmSpinner.setEnabled(false);
113 if (minHour == 0 && maxHour == 0) {
116 } else if (minHour == 0) {
119 } else if (maxHour == 0) {
126 mAmPmSpinner.setValue(amPm);
129 if (minHour == maxHour) {
130 mHourSpinner.setEnabled(false);
132 mHourSpinner.setMinValue(minHour);
133 mHourSpinner.setMaxValue(maxHour);
134 mHourSpinner.setValue(hour);
136 NumberFormatter twoDigitPaddingFormatter = new NumberFormatter("%02d");
138 int minMinute = min / MINUTE_IN_MILLIS;
139 int maxMinute = max / MINUTE_IN_MILLIS;
140 min -= minMinute * MINUTE_IN_MILLIS;
141 max -= maxMinute * MINUTE_IN_MILLIS;
143 if (minHour == maxHour) {
144 mMinuteSpinner.setMinValue(minMinute);
145 mMinuteSpinner.setMaxValue(maxMinute);
146 if (minMinute == maxMinute) {
147 // Set this otherwise the box is empty until you stroke it.
148 mMinuteSpinner.setDisplayedValues(
149 new String[] { twoDigitPaddingFormatter.format(minMinute) });
150 mMinuteSpinner.setEnabled(false);
154 mMinuteSpinner.setMinValue(0);
155 mMinuteSpinner.setMaxValue(59);
158 if (step >= HOUR_IN_MILLIS) {
159 mMinuteSpinner.setEnabled(false);
162 mMinuteSpinner.setValue(minute);
163 mMinuteSpinner.setFormatter(twoDigitPaddingFormatter);
165 if (step >= MINUTE_IN_MILLIS) {
166 // Remove the ':' in front of the second spinner as well.
167 view.findViewById(R.id.second_colon).setVisibility(View.GONE);
168 mSecSpinner.setVisibility(View.GONE);
171 int minSecond = min / SECOND_IN_MILLIS;
172 int maxSecond = max / SECOND_IN_MILLIS;
173 min -= minSecond * SECOND_IN_MILLIS;
174 max -= maxSecond * SECOND_IN_MILLIS;
176 if (minHour == maxHour && minMinute == maxMinute) {
177 mSecSpinner.setMinValue(minSecond);
178 mSecSpinner.setMaxValue(maxSecond);
179 if (minSecond == maxSecond) {
180 // Set this otherwise the box is empty until you stroke it.
181 mSecSpinner.setDisplayedValues(
182 new String[] { twoDigitPaddingFormatter.format(minSecond) });
183 mSecSpinner.setEnabled(false);
187 mSecSpinner.setMinValue(0);
188 mSecSpinner.setMaxValue(59);
191 mSecSpinner.setValue(second);
192 mSecSpinner.setFormatter(twoDigitPaddingFormatter);
194 if (step >= SECOND_IN_MILLIS) {
195 // Remove the '.' in front of the milli spinner as well.
196 view.findViewById(R.id.second_dot).setVisibility(View.GONE);
197 mMilliSpinner.setVisibility(View.GONE);
200 // Round to the nearest step.
201 milli = ((milli + step / 2) / step) * step;
202 if (step == 1 || step == 10 || step == 100) {
203 if (minHour == maxHour && minMinute == maxMinute &&
204 minSecond == maxSecond) {
205 mMilliSpinner.setMinValue(min / step);
206 mMilliSpinner.setMaxValue(max / step);
209 mMilliSpinner.setEnabled(false);
213 mMilliSpinner.setMinValue(0);
214 mMilliSpinner.setMaxValue(999 / step);
218 mMilliSpinner.setFormatter(new NumberFormatter("%03d"));
219 } else if (step == 10) {
220 mMilliSpinner.setFormatter(new NumberFormatter("%02d"));
221 } else if (step == 100) {
222 mMilliSpinner.setFormatter(new NumberFormatter("%d"));
224 mMilliSpinner.setValue(milli / step);
226 } else if (step < SECOND_IN_MILLIS) {
227 // Non-decimal step value.
228 ArrayList<String> strValue = new ArrayList<String>();
229 for (int i = min; i < max; i += step) {
230 strValue.add(String.format("%03d", i));
232 mMilliSpinner.setMinValue(0);
233 mMilliSpinner.setMaxValue(strValue.size() - 1);
234 mMilliSpinner.setValue((milli - min) / step);
235 mMilliSpinner.setDisplayedValues(
236 strValue.toArray(new String[strValue.size()]));
244 public void onClick(DialogInterface dialog, int which) {
248 private void notifyDateSet() {
249 int hour = mHourSpinner.getValue();
250 int minute = mMinuteSpinner.getValue();
251 int sec = mSecSpinner.getValue();
252 int milli = mMilliSpinner.getValue() * mStep + mBaseMilli;
253 if (!mIs24hourFormat) {
254 int ampm = mAmPmSpinner.getValue();
260 mListener.onTimeSet(hour, minute, sec, milli);
263 private static class NumberFormatter implements NumberPicker.Formatter {
264 private final String mFormat;
266 NumberFormatter(String format) {
271 public String format(int value) {
272 return String.format(mFormat, value);