Friday 2 March 2018

Android Preferences Time Picker

When developing the preferences page for an Android app you can add a time picker with this class.

import android.content.*;
import android.content.res.*;
import android.preference.*;
import android.util.*;
import android.view.*;
import android.widget.*;
import java.text.*;
import java.util.*;

/*
* Use in preferences.xml like this:
*   <net.intrepidis.library.prefs.TimePreference
*       android:key="mytime_preference"
*       android:title="@string/mytime_preftitle"
*       android:summary="@string/mytime_prefsummary"
*       android:defaultValue="00:10"
*       positiveButtonText="@string/ok_button"
*       negativeButtonText="Close me" />
*/
public class TimePreference extends DialogPreference {
private final Calendar calendar;
private final TimePicker picker;

public TimePreference(final Context ctx) {
this(ctx, null);
}

public TimePreference(final Context ctx, final AttributeSet attrs) {
this(ctx, attrs, android.R.attr.dialogPreferenceStyle);
}

public TimePreference(final Context ctx, final AttributeSet attrs, final int defStyle) {
super(ctx, attrs, defStyle);

calendar = new GregorianCalendar();
picker = new TimePicker(ctx);

// Set 24-hour display.
final boolean is24 = is24HourSystemDateSetting();
picker.setIs24HourView(is24);

// Positive button text.
{
final int resId = attrs.getAttributeResourceValue(
null, "positiveButtonText", -1);
final String text =
attrs.getAttributeValue(null, "positiveButtonText");
if (resId != -1) {
setPositiveButtonText(resId);
} else if (text != null) {
setPositiveButtonText(text);
}
}

// Negative button text.
{
final int resId = attrs.getAttributeResourceValue(
null, "negativeButtonText", -1);
final String text =
attrs.getAttributeValue(null, "negativeButtonText");
if (resId != -1) {
setNegativeButtonText(resId);
} else if (text != null) {
setNegativeButtonText(text);
}
}
}

@Override
protected View onCreateDialogView() {
return picker;
}

@Override
protected void onBindDialogView(final View v) {
super.onBindDialogView(v);
picker.setCurrentHour(calendar.get(Calendar.HOUR_OF_DAY));
picker.setCurrentMinute(calendar.get(Calendar.MINUTE));
}

@Override
protected void onDialogClosed(final boolean positiveResult) {
super.onDialogClosed(positiveResult);

if (positiveResult) {
calendar.set(Calendar.HOUR_OF_DAY, picker.getCurrentHour());
calendar.set(Calendar.MINUTE, picker.getCurrentMinute());

setSummary(getSummary());

final long millis = calendar.getTimeInMillis();
if (callChangeListener(millis)) {
persistLong(millis);
notifyChanged();
}
}
}

@Override
protected Object onGetDefaultValue(final TypedArray a, final int index) {
try {
final String s = a.getString(index);
// Use 24-hour format for the default setting.
final DateFormat df = new SimpleDateFormat("HH:mm");
final Date date = df.parse(s);
final long l = date.getTime();
return l;
} catch (ParseException e) {
return null;
}
}

@Override
protected void onSetInitialValue(
final boolean restoreValue,
final Object defaultValue
) {
final long def =
defaultValue == null
? System.currentTimeMillis()
: (long)defaultValue;

final long time;
if (restoreValue) {
time = getPersistedLong(def);
} else {
time = def;
if (shouldPersist()) {
persistLong(time);
}
}

calendar.setTimeInMillis(time);

setSummary(getSummary());
}

@Override
public CharSequence getSummary() {
// Format time as per user's locale.
final DateFormat df =
SimpleDateFormat.getTimeInstance(SimpleDateFormat.SHORT);
final Date time = calendar.getTime();
final String s = df.format(time);
return s;
}

public static boolean is24HourSystemDateSetting() {
final SimpleDateFormat f = (SimpleDateFormat)
SimpleDateFormat.getTimeInstance(SimpleDateFormat.SHORT);
final String pattern = f.toPattern();
final boolean is24 = pattern.indexOf('H') >= 0;
return is24;
}
}