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 5 package org.chromium.content.browser.input; 6 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; 14 15 import org.chromium.content.R; 16 17 import 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 */ 28 public 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 { onTimeSet(int hourOfDay, int minute, int second, int milli)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 MultiFieldTimePickerDialog( Context context, int theme, int hour, int minute, int second, int milli, int min, int max, int step, boolean is24hourFormat, OnMultiFieldTimeSetListener listener)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 onClick(DialogInterface dialog, int which)244 public void onClick(DialogInterface dialog, int which) { 245 notifyDateSet(); 246 } 247 notifyDateSet()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 NumberFormatter(String format)266 NumberFormatter(String format) { 267 mFormat = format; 268 } 269 270 @Override format(int value)271 public String format(int value) { 272 return String.format(mFormat, value); 273 } 274 } 275 } 276