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. 4 5package org.chromium.content.browser.input; 6 7import android.app.AlertDialog; 8import android.content.Context; 9import android.content.DialogInterface; 10import android.content.DialogInterface.OnClickListener; 11import android.view.LayoutInflater; 12import android.view.View; 13import android.widget.NumberPicker; 14 15import org.chromium.content.R; 16 17import java.util.ArrayList; 18 19/** 20 * A time picker dialog with upto 5 number pickers left to right: 21 * hour, minute, second, milli, AM/PM. 22 * 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. 27 */ 28public class MultiFieldTimePickerDialog 29 extends AlertDialog implements OnClickListener { 30 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; 40 41 public interface OnMultiFieldTimeSetListener { 42 void onTimeSet(int hourOfDay, int minute, int second, int milli); 43 } 44 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; 48 49 public MultiFieldTimePickerDialog( 50 Context context, 51 int theme, 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); 56 mListener = listener; 57 mStep = step; 58 mIs24hourFormat = is24hourFormat; 59 60 if (min >= max) { 61 min = 0; 62 max = 24 * HOUR_IN_MILLIS - 1; 63 } 64 if (step < 0 || step >= 24 * HOUR_IN_MILLIS) { 65 step = MINUTE_IN_MILLIS; 66 } 67 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); 72 setView(view); 73 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); 79 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; 84 85 if (minHour == maxHour) { 86 mHourSpinner.setEnabled(false); 87 hour = minHour; 88 } 89 90 if (is24hourFormat) { 91 mAmPmSpinner.setVisibility(View.GONE); 92 } else { 93 int minAmPm = minHour / 12; 94 int maxAmPm = maxHour / 12; 95 int amPm = hour / 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) 101 }); 102 103 hour %= 12; 104 if (hour == 0) { 105 hour = 12; 106 } 107 if (minAmPm == maxAmPm) { 108 mAmPmSpinner.setEnabled(false); 109 amPm = minAmPm; 110 111 minHour %= 12; 112 maxHour %= 12; 113 if (minHour == 0 && maxHour == 0) { 114 minHour = 12; 115 maxHour = 12; 116 } else if (minHour == 0) { 117 minHour = maxHour; 118 maxHour = 12; 119 } else if (maxHour == 0) { 120 maxHour = 12; 121 } 122 } else { 123 minHour = 1; 124 maxHour = 12; 125 } 126 mAmPmSpinner.setValue(amPm); 127 } 128 129 if (minHour == maxHour) { 130 mHourSpinner.setEnabled(false); 131 } 132 mHourSpinner.setMinValue(minHour); 133 mHourSpinner.setMaxValue(maxHour); 134 mHourSpinner.setValue(hour); 135 136 NumberFormatter twoDigitPaddingFormatter = new NumberFormatter("%02d"); 137 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; 142 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); 151 minute = minMinute; 152 } 153 } else { 154 mMinuteSpinner.setMinValue(0); 155 mMinuteSpinner.setMaxValue(59); 156 } 157 158 if (step >= HOUR_IN_MILLIS) { 159 mMinuteSpinner.setEnabled(false); 160 } 161 162 mMinuteSpinner.setValue(minute); 163 mMinuteSpinner.setFormatter(twoDigitPaddingFormatter); 164 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); 169 } 170 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; 175 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); 184 second = minSecond; 185 } 186 } else { 187 mSecSpinner.setMinValue(0); 188 mSecSpinner.setMaxValue(59); 189 } 190 191 mSecSpinner.setValue(second); 192 mSecSpinner.setFormatter(twoDigitPaddingFormatter); 193 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); 198 } 199 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); 207 208 if (min == max) { 209 mMilliSpinner.setEnabled(false); 210 milli = min; 211 } 212 } else { 213 mMilliSpinner.setMinValue(0); 214 mMilliSpinner.setMaxValue(999 / step); 215 } 216 217 if (step == 1) { 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")); 223 } 224 mMilliSpinner.setValue(milli / step); 225 mBaseMilli = 0; 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)); 231 } 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()])); 237 mBaseMilli = min; 238 } else { 239 mBaseMilli = 0; 240 } 241 } 242 243 @Override 244 public void onClick(DialogInterface dialog, int which) { 245 notifyDateSet(); 246 } 247 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(); 255 if (hour == 12) { 256 hour = 0; 257 } 258 hour += ampm * 12; 259 } 260 mListener.onTimeSet(hour, minute, sec, milli); 261 } 262 263 private static class NumberFormatter implements NumberPicker.Formatter { 264 private final String mFormat; 265 266 NumberFormatter(String format) { 267 mFormat = format; 268 } 269 270 @Override 271 public String format(int value) { 272 return String.format(mFormat, value); 273 } 274 } 275} 276