Android Time Picker Preference

When developing applications for Google Android, there's a great way to implement your preferences using an xml, much like you would for a regular layout. The added benifit of doing it this way is that you don't have to implement how the preference values are saved. However, one of the problems is that there are only a couple of preference types implemented.

For my latest application, I needed to let the user save a specific time. Where there is a TimePicker widget, there is not TimePickerPreference that extends the Preference class. So I wrote my own.

// Please note this must be the package if you want to use XML-based preferences
package android.preference;
 
import android.content.Context;
import android.util.AttributeSet;
import android.view.View;
import android.widget.TimePicker;
 
/**
 * A preference type that allows a user to choose a time
 */
public class TimePickerPreference extends DialogPreference implements
		TimePicker.OnTimeChangedListener {
 
	/**
	 * The validation expression for this preference
	 */
	private static final String VALIDATION_EXPRESSION = "[0-2]*[0-9]:[0-5]*[0-9]";
 
	/**
	 * The default value for this preference
	 */
	private String defaultValue;
 
	/**
	 * @param context
	 * @param attrs
	 */
	public TimePickerPreference(Context context, AttributeSet attrs) {
		super(context, attrs);
		initialize();
	}
 
	/**
	 * @param context
	 * @param attrs
	 * @param defStyle
	 */
	public TimePickerPreference(Context context, AttributeSet attrs,
			int defStyle) {
		super(context, attrs, defStyle);
		initialize();
	}
 
	/**
	 * Initialize this preference
	 */
	private void initialize() {
		setPersistent(true);
	}
 
	/*
	 * (non-Javadoc)
	 * 
	 * @see android.preference.DialogPreference#onCreateDialogView()
	 */
	@Override
	protected View onCreateDialogView() {
 
		TimePicker tp = new TimePicker(getContext());
		tp.setOnTimeChangedListener(this);
 
		int h = getHour();
		int m = getMinute();
		if (h >= 0 && m >= 0) {
			tp.setCurrentHour(h);
			tp.setCurrentMinute(m);
		}
 
		return tp;
	}
 
	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * android.widget.TimePicker.OnTimeChangedListener#onTimeChanged(android
	 * .widget.TimePicker, int, int)
	 */
	@Override
	public void onTimeChanged(TimePicker view, int hour, int minute) {
 
		persistString(hour + ":" + minute);
	}
 
	/*
	 * (non-Javadoc)
	 * 
	 * @see android.preference.Preference#setDefaultValue(java.lang.Object)
	 */
	@Override
	public void setDefaultValue(Object defaultValue) {
		// BUG this method is never called if you use the 'android:defaultValue' attribute in your XML preference file, not sure why it isn't		
 
		super.setDefaultValue(defaultValue);
 
		if (!(defaultValue instanceof String)) {
			return;
		}
 
		if (!((String) defaultValue).matches(VALIDATION_EXPRESSION)) {
			return;
		}
 
		this.defaultValue = (String) defaultValue;
	}
 
	/**
	 * Get the hour value (in 24 hour time)
	 * 
	 * @return The hour value, will be 0 to 23 (inclusive)
	 */
	private int getHour() {
		String time = getPersistedString(this.defaultValue);
		if (time == null || !time.matches(VALIDATION_EXPRESSION)) {
			return -1;
		}
 
		return Integer.valueOf(time.split(":")[0]);
	}
 
	/**
	 * Get the minute value
	 * 
	 * @return the minute value, will be 0 to 59 (inclusive)
	 */
	private int getMinute() {
		String time = getPersistedString(this.defaultValue);
		if (time == null || !time.matches(VALIDATION_EXPRESSION)) {
			return -1;
		}
 
		return Integer.valueOf(time.split(":")[1]);
	}
}

Error

It doesn't save the user preference, if the user types the numbers in the box themselves ( instead of using the + / - )

Feel free to suggest updates

If you find out what event handler is called when a user manually enters a value, let me know so I can update the code here.

Thanks,
Eric

Thanks mate excellent

Thanks mate excellent implementation of a time picker

package doesn't need to be android.preference

hi, great work!
saved me a lot of time.

I just wanna say that you can change the package these preferences lie in,
if you specify entire package in XML-file:

Kris

Problem again again

Ok, I found that if I call tp.setIs24HourView(true); before tp.setOnTimeChangedListener(this); it works fine. Thanks for this great class!

Problem again

Never mind my previous post! The problem was that I added tp.setIs24HourView(true); to the onCreateDialogView method, since I'm from Europe and we use a 24 hour clock. ;)

However, that seems to mess it up. :( How can I set it to use a 24 hour clock by default?

Problem

I've been eager to use this, but I haven't been able to get it working.

When I set a preference using this class, it gets set properly in the shared prefs xml file. However, whenever I open the preference again, the time picker is set to the current time. It seems that the onTimeChanged method is called immediately after the time picker is created, with its parameters set to the current time.

Did anyone else experience this? I don't know how to solve it ... :(

Cancel behavior

I might be missing something but this seems to always persist the value even if you cancel the TimePicker. I changed onTimeChanged to store to a local variable and then did a @Override on onDialogClosed to get the state of the dialog.

If the user said 'ok' then persist the string (In my implementation I didn't make it persist by default, hence the 'isPersistent()'). If the user canceled then I change nothing

	private int mHour = 0, mMinute = 0;
	@Override
	public void onTimeChanged(TimePicker view, int hour, int minute) {
		mHour = hour;
		mMinute = minute;
	}
	@Override
	public void onDialogClosed(boolean positiveResult) {
		if( positiveResult ) {
			if( isPersistent() )
				persistString(mHour + ":" + mMinute);
			mValueText.setText( getNiceTimestring() );
		}
	}

Update for latest Android release

From an email:

I had to modify your TimePickerPreference code a little as it did not respond to an OnPreferenceChangeListener. The change made is just this:

String result = hour + ":" + minute;
persistString(result);
callChangeListener(result);

DatePicker

I've been trying to implement a similar pref for DatePicker ... however I'm having a few problems. Had any luck with that?

RE: DatePicker

I haven't need to use a Date preference value, so I haven't created this kind of class for DatePicker.  If you're having problems, then I suggest posting to the Android Developers group.

Thanks

Thanks for saving me the work. Using this in my Android project now :)

Can we use your code ?

What's the license of this code ?

No license

There is no license, you can use however you want to.

layout

is there another layout or the normal if u create a new file????

This is a class, so it should

This is a class, so it should be a new file.  I'm not exactly sure what you're asking.