McGuile
McGuile

Reputation: 828

Prevent user from selecting past time - TimePickerDialog

IMPORTANT EDIT: I've done a bit of searching and it seems that the OnTimeChangedListener doesn't fire in Android 5.0 (API 21). Gustavo's answer below will work for API 20 or lower, and API 22 which I've tested. (Source)

I would like to prevent a user from entering a time prior to the current time. I check for a valid time using the before method provided by Date. How can I prevent the dialog from accepting an invalid time within this validity check?

I have omitted excess code where I initialize the selected time (as a Date).

final Calendar mcurrentTime = Calendar.getInstance();
final int hour = mcurrentTime.get(Calendar.HOUR_OF_DAY);
final int minute = mcurrentTime.get(Calendar.MINUTE);
final TimePickerDialog mTimePicker;
mTimePicker = new TimePickerDialog(MainActivity.this, new TimePickerDialog.OnTimeSetListener() {
    @Override
    public void onTimeSet(TimePicker timePicker, int selectedHour, int selectedMinute) {

        //PSEUDO: Date selectedDateTime init here

        if (selectedDateTime.before(mcurrentTime.getTime())) {
            // prevent user from selecting time
        }
    }
}, hour, minute, true);//true = 24 hour time
mTimePicker.setTitle("Select Time");
mTimePicker.show(); 

Upvotes: 4

Views: 10623

Answers (2)

Chad Bingham
Chad Bingham

Reputation: 33856

Method onTimeSet() is called once when dialog is dismissed and is called twice when Done button is clicked.

I would just validate in that method on the second call. Ignore the first. If it is invalid, show an error (probably a toast) and show the dialog again

Elaborate

Both of the onTimeSet calls will be identical times. Now, I don't have a reference, but I believe that one call is when the dialog was shown (time set or not) and then was cancelled. Two is exactly that but instead of cancel, it is when the user hits "Done".

The reason I say ignore the first is that the could set the time, and decide to cancel. In that case, don't take that onTimeSet(). If that doesn't matter to you, then no need to worry about this.

Upvotes: 2

Gustavo Morales
Gustavo Morales

Reputation: 2670

Here the original post.

Following answer you should add TimePickerDialog class.

public class RangeTimePickerDialog extends TimePickerDialog {

private int minHour = -1;
private int minMinute = -1;

private int maxHour = 25;
private int maxMinute = 25;

private int currentHour = 0;
private int currentMinute = 0;

private Calendar calendar = Calendar.getInstance();
private DateFormat dateFormat;


public RangeTimePickerDialog(Context context, OnTimeSetListener callBack, int hourOfDay, int minute, boolean is24HourView) {
    super(context, callBack, hourOfDay, minute, is24HourView);
    currentHour = hourOfDay;
    currentMinute = minute;
    dateFormat = DateFormat.getTimeInstance(DateFormat.SHORT);

    try {
        Class<?> superclass = getClass().getSuperclass();
        Field mTimePickerField = superclass.getDeclaredField("mTimePicker");
        mTimePickerField.setAccessible(true);
        TimePicker mTimePicker = (TimePicker) mTimePickerField.get(this);
        mTimePicker.setOnTimeChangedListener(this);
    } catch (NoSuchFieldException e) {
    } catch (IllegalArgumentException e) {
    } catch (IllegalAccessException e) {
    }
}

public void setMin(int hour, int minute) {
    minHour = hour;
    minMinute = minute;
}

public void setMax(int hour, int minute) {
    maxHour = hour;
    maxMinute = minute;
}

@Override
public void onTimeChanged(TimePicker view, int hourOfDay, int minute) {

    Log.d("DADADADA", "onTimeChanged");

    boolean validTime = true;
    if (hourOfDay < minHour || (hourOfDay == minHour && minute < minMinute)) {
        validTime = false;
    }

    if (hourOfDay > maxHour || (hourOfDay == maxHour && minute > maxMinute)) {
        validTime = false;
    }

    if (validTime) {
        currentHour = hourOfDay;
        currentMinute = minute;
    }

    updateTime(currentHour, currentMinute);
    updateDialogTitle(view, currentHour, currentMinute);
}

private void updateDialogTitle(TimePicker timePicker, int hourOfDay, int minute) {
    calendar.set(Calendar.HOUR_OF_DAY, hourOfDay);
    calendar.set(Calendar.MINUTE, minute);
    String title = dateFormat.format(calendar.getTime());
    setTitle(title);
}
}

After that, replace your TimePickerDialog for the RangeTimePickerDialog

public class MainActivity extends Activity {

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    final Calendar mcurrentTime = Calendar.getInstance();
    final int hour = mcurrentTime.get(Calendar.HOUR_OF_DAY);
    final int minute = mcurrentTime.get(Calendar.MINUTE);
    final RangeTimePickerDialog mTimePicker;
    mTimePicker = new RangeTimePickerDialog(MainActivity.this, new TimePickerDialog.OnTimeSetListener() {
        @Override
        public void onTimeSet(TimePicker timePicker, int selectedHour, int selectedMinute) {

            Log.d("TAG", "inside OnTimeSetListener");

        }
    }, hour, minute, true);//true = 24 hour time
    mTimePicker.setTitle("Select Time");
    mTimePicker.setMin(hour, minute);
    mTimePicker.show();
}
}

After that, check you dialog, time doesn't pass limits, I have checked this with both setMax() or setMin() and also setting only one.

Upvotes: 9

Related Questions