// Copyright 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.chromium.content.browser.input;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.DialogInterface.OnClickListener;
import android.os.Build;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.NumberPicker;
import org.chromium.content.R;
import java.util.ArrayList;
/**
* A time picker dialog with upto 5 number pickers left to right:
* hour, minute, second, milli, AM/PM.
*
* If is24hourFormat is true then AM/PM picker is not displayed and
* hour range is 0..23. Otherwise hour range is 1..12.
* The milli picker is not displayed if step >= SECOND_IN_MILLIS
* The second picker is not displayed if step >= MINUTE_IN_MILLIS.
*/
public class MultiFieldTimePickerDialog
extends AlertDialog implements OnClickListener {
private final NumberPicker mHourSpinner;
private final NumberPicker mMinuteSpinner;
private final NumberPicker mSecSpinner;
private final NumberPicker mMilliSpinner;
private final NumberPicker mAmPmSpinner;
private final OnMultiFieldTimeSetListener mListener;
private final int mStep;
private final int mBaseMilli;
private final boolean mIs24hourFormat;
public interface OnMultiFieldTimeSetListener {
void onTimeSet(int hourOfDay, int minute, int second, int milli);
}
private static final int SECOND_IN_MILLIS = 1000;
private static final int MINUTE_IN_MILLIS = 60 * SECOND_IN_MILLIS;
private static final int HOUR_IN_MILLIS = 60 * MINUTE_IN_MILLIS;
public MultiFieldTimePickerDialog(
Context context,
int theme,
int hour, int minute, int second, int milli,
int min, int max, int step, boolean is24hourFormat,
OnMultiFieldTimeSetListener listener) {
super(context, theme);
mListener = listener;
mStep = step;
mIs24hourFormat = is24hourFormat;
if (min >= max) {
min = 0;
max = 24 * HOUR_IN_MILLIS - 1;
}
if (step < 0 || step >= 24 * HOUR_IN_MILLIS) {
step = MINUTE_IN_MILLIS;
}
LayoutInflater inflater =
(LayoutInflater) context.getSystemService(
Context.LAYOUT_INFLATER_SERVICE);
View view = inflater.inflate(R.layout.multi_field_time_picker_dialog, null);
setView(view);
mHourSpinner = (NumberPicker) view.findViewById(R.id.hour);
mMinuteSpinner = (NumberPicker) view.findViewById(R.id.minute);
mSecSpinner = (NumberPicker) view.findViewById(R.id.second);
mMilliSpinner = (NumberPicker) view.findViewById(R.id.milli);
mAmPmSpinner = (NumberPicker) view.findViewById(R.id.ampm);
int minHour = min / HOUR_IN_MILLIS;
int maxHour = max / HOUR_IN_MILLIS;
min -= minHour * HOUR_IN_MILLIS;
max -= maxHour * HOUR_IN_MILLIS;
if (minHour == maxHour) {
mHourSpinner.setEnabled(false);
hour = minHour;
}
if (is24hourFormat) {
mAmPmSpinner.setVisibility(View.GONE);
} else {
int minAmPm = minHour / 12;
int maxAmPm = maxHour / 12;
int amPm = hour / 12;
mAmPmSpinner.setMinValue(minAmPm);
mAmPmSpinner.setMaxValue(maxAmPm);
mAmPmSpinner.setDisplayedValues(new String[] {
context.getString(R.string.time_picker_dialog_am),
context.getString(R.string.time_picker_dialog_pm)
});
hour %= 12;
if (hour == 0) {
hour = 12;
}
if (minAmPm == maxAmPm) {
mAmPmSpinner.setEnabled(false);
amPm = minAmPm;
minHour %= 12;
maxHour %= 12;
if (minHour == 0 && maxHour == 0) {
minHour = 12;
maxHour = 12;
} else if (minHour == 0) {
minHour = maxHour;
maxHour = 12;
} else if (maxHour == 0) {
maxHour = 12;
}
} else {
minHour = 1;
maxHour = 12;
}
mAmPmSpinner.setValue(amPm);
}
if (minHour == maxHour) {
mHourSpinner.setEnabled(false);
}
mHourSpinner.setMinValue(minHour);
mHourSpinner.setMaxValue(maxHour);
mHourSpinner.setValue(hour);
NumberFormatter twoDigitPaddingFormatter = new NumberFormatter("%02d");
int minMinute = min / MINUTE_IN_MILLIS;
int maxMinute = max / MINUTE_IN_MILLIS;
min -= minMinute * MINUTE_IN_MILLIS;
max -= maxMinute * MINUTE_IN_MILLIS;
if (minHour == maxHour) {
mMinuteSpinner.setMinValue(minMinute);
mMinuteSpinner.setMaxValue(maxMinute);
if (minMinute == maxMinute) {
// Set this otherwise the box is empty until you stroke it.
mMinuteSpinner.setDisplayedValues(
new String[] { twoDigitPaddingFormatter.format(minMinute) });
mMinuteSpinner.setEnabled(false);
minute = minMinute;
}
} else {
mMinuteSpinner.setMinValue(0);
mMinuteSpinner.setMaxValue(59);
}
if (step >= HOUR_IN_MILLIS) {
mMinuteSpinner.setEnabled(false);
}
mMinuteSpinner.setValue(minute);
mMinuteSpinner.setFormatter(twoDigitPaddingFormatter);
if (step >= MINUTE_IN_MILLIS) {
// Remove the ':' in front of the second spinner as well.
view.findViewById(R.id.second_colon).setVisibility(View.GONE);
mSecSpinner.setVisibility(View.GONE);
}
int minSecond = min / SECOND_IN_MILLIS;
int maxSecond = max / SECOND_IN_MILLIS;
min -= minSecond * SECOND_IN_MILLIS;
max -= maxSecond * SECOND_IN_MILLIS;
if (minHour == maxHour && minMinute == maxMinute) {
mSecSpinner.setMinValue(minSecond);
mSecSpinner.setMaxValue(maxSecond);
if (minSecond == maxSecond) {
// Set this otherwise the box is empty until you stroke it.
mSecSpinner.setDisplayedValues(
new String[] { twoDigitPaddingFormatter.format(minSecond) });
mSecSpinner.setEnabled(false);
second = minSecond;
}
} else {
mSecSpinner.setMinValue(0);
mSecSpinner.setMaxValue(59);
}
mSecSpinner.setValue(second);
mSecSpinner.setFormatter(twoDigitPaddingFormatter);
if (step >= SECOND_IN_MILLIS) {
// Remove the '.' in front of the milli spinner as well.
view.findViewById(R.id.second_dot).setVisibility(View.GONE);
mMilliSpinner.setVisibility(View.GONE);
}
// Round to the nearest step.
milli = ((milli + step / 2) / step) * step;
if (step == 1 || step == 10 || step == 100) {
if (minHour == maxHour && minMinute == maxMinute &&
minSecond == maxSecond) {
mMilliSpinner.setMinValue(min / step);
mMilliSpinner.setMaxValue(max / step);
if (min == max) {
mMilliSpinner.setEnabled(false);
milli = min;
}
} else {
mMilliSpinner.setMinValue(0);
mMilliSpinner.setMaxValue(999 / step);
}
if (step == 1) {
mMilliSpinner.setFormatter(new NumberFormatter("%03d"));
} else if (step == 10) {
mMilliSpinner.setFormatter(new NumberFormatter("%02d"));
} else if (step == 100) {
mMilliSpinner.setFormatter(new NumberFormatter("%d"));
}
mMilliSpinner.setValue(milli / step);
mBaseMilli = 0;
} else if (step < SECOND_IN_MILLIS) {
// Non-decimal step value.
ArrayList<String> strValue = new ArrayList<String>();
for (int i = min; i < max; i += step) {
strValue.add(String.format("%03d", i));
}
mMilliSpinner.setMinValue(0);
mMilliSpinner.setMaxValue(strValue.size() - 1);
mMilliSpinner.setValue((milli - min) / step);
mMilliSpinner.setDisplayedValues(
strValue.toArray(new String[strValue.size()]));
mBaseMilli = min;
} else {
mBaseMilli = 0;
}
}
@Override
public void onClick(DialogInterface dialog, int which) {
notifyDateSet();
}
private void notifyDateSet() {
int hour = mHourSpinner.getValue();
int minute = mMinuteSpinner.getValue();
int sec = mSecSpinner.getValue();
int milli = mMilliSpinner.getValue() * mStep + mBaseMilli;
if (!mIs24hourFormat) {
int ampm = mAmPmSpinner.getValue();
if (hour == 12) {
hour = 0;
}
hour += ampm * 12;
}
mListener.onTimeSet(hour, minute, sec, milli);
}
@Override
protected void onStop() {
if (Build.VERSION.SDK_INT >= 16) {
// The default behavior of dialogs changed in JellyBean and onwards.
// Dismissing a dialog (by pressing back for example)
// applies the chosen date. This code is added here so that the custom
// pickers behave the same as the internal DatePickerDialog.
notifyDateSet();
}
super.onStop();
}
private static class NumberFormatter implements NumberPicker.Formatter {
private final String mFormat;
NumberFormatter(String format) {
mFormat = format;
}
@Override
public String format(int value) {
return String.format(mFormat, value);
}
}
}