Jelly Bean DatePickerDialog --- is there a way to cancel?

29,234

Solution 1

Note: Fixed as of Lollipop, source here. Automated class for use in clients (compatible with all Android versions) updated as well.

TL;DR: 1-2-3 dead easy steps for a global solution:

  1. Download this class.
  2. Implement OnDateSetListener in your activity (or change the class to suit your needs).
  3. Trigger the dialog with this code (in this sample, I use it inside a Fragment):

    Bundle b = new Bundle();
    b.putInt(DatePickerDialogFragment.YEAR, 2012);
    b.putInt(DatePickerDialogFragment.MONTH, 6);
    b.putInt(DatePickerDialogFragment.DATE, 17);
    DialogFragment picker = new DatePickerDialogFragment();
    picker.setArguments(b);
    picker.show(getActivity().getSupportFragmentManager(), "frag_date_picker");
    

And that's all it takes! The reason I still keep my answer as "accepted" is because I still prefer my solution since it has a very small footprint in client code, it addresses the fundamental issue (the listener being called in the framework class), works fine across config changes and it routes the code logic to the default implementation in previous Android versions not plagued by this bug (see class source).

Original answer (kept for historical and didactic reasons):

Bug source

OK, looks like it's indeed a bug and someone else already filled it. Issue 34833.

I've found that the problem is possibly in DatePickerDialog.java. Where it reads:

private void tryNotifyDateSet() {
    if (mCallBack != null) {
        mDatePicker.clearFocus();
        mCallBack.onDateSet(mDatePicker, mDatePicker.getYear(),
                mDatePicker.getMonth(), mDatePicker.getDayOfMonth());
    }
}

@Override
protected void onStop() {
    tryNotifyDateSet();
    super.onStop();
}

I'd guess it could have been:

@Override
protected void onStop() {
    // instead of the full tryNotifyDateSet() call:
    if (mCallBack != null) mDatePicker.clearFocus();
    super.onStop();
}

Now if someone can tell me how I can propose a patch/bug report to Android, I'd be glad to. Meanwhile, I suggested a possible fix (simple) as an attached version of DatePickerDialog.java in the Issue there.

Concept to avoid the bug

Set the listener to null in the constructor and create your own BUTTON_POSITIVE button later on. That's it, details below.

The problem happens because DatePickerDialog.java, as you can see in the source, calls a global variable (mCallBack) that stores the listener that was passed in the constructor:

    /**
 * @param context The context the dialog is to run in.
 * @param callBack How the parent is notified that the date is set.
 * @param year The initial year of the dialog.
 * @param monthOfYear The initial month of the dialog.
 * @param dayOfMonth The initial day of the dialog.
 */
public DatePickerDialog(Context context,
        OnDateSetListener callBack,
        int year,
        int monthOfYear,
        int dayOfMonth) {
    this(context, 0, callBack, year, monthOfYear, dayOfMonth);
}

    /**
 * @param context The context the dialog is to run in.
 * @param theme the theme to apply to this dialog
 * @param callBack How the parent is notified that the date is set.
 * @param year The initial year of the dialog.
 * @param monthOfYear The initial month of the dialog.
 * @param dayOfMonth The initial day of the dialog.
 */
public DatePickerDialog(Context context,
        int theme,
        OnDateSetListener callBack,
        int year,
        int monthOfYear,
        int dayOfMonth) {
    super(context, theme);

    mCallBack = callBack;
    // ... rest of the constructor.
}

So, the trick is to provide a null listener to be stored as the listener, and then roll your own set of buttons (below is the original code from #1, updated):

    DatePickerDialog picker = new DatePickerDialog(
        this,
        null, // instead of a listener
        2012, 6, 15);
    picker.setCancelable(true);
    picker.setCanceledOnTouchOutside(true);
    picker.setButton(DialogInterface.BUTTON_POSITIVE, "OK",
        new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                Log.d("Picker", "Correct behavior!");
            }
        });
    picker.setButton(DialogInterface.BUTTON_NEGATIVE, "Cancel", 
        new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                Log.d("Picker", "Cancel!");
            }
        });
picker.show();

Now it will work because of the possible correction that I posted above.

And since DatePickerDialog.java checks for a null whenever it reads mCallback (since the days of API 3/1.5 it seems --- can't check Honeycomb of course), it won't trigger the exception. Considering Lollipop fixed the issue, I'm not going to look into it: just use the default implementation (covered in the class I provided).

At first I was afraid of not calling the clearFocus(), but I've tested here and the Log lines were clean. So that line I proposed may not even be necessary after all, but I don't know.

Compatibility with previous API levels (edited)

As I pointed in the comment below, that was a concept, and you can download the class I'm using from my Google Drive account. The way I used, the default system implementation is used on versions not affected by the bug.

I took a few assumptions (button names etc.) that are suitable for my needs because I wanted to reduce boilerplate code in client classes to a minimum. Full usage example:

class YourActivity extends SherlockFragmentActivity implements OnDateSetListener

// ...

Bundle b = new Bundle();
b.putInt(DatePickerDialogFragment.YEAR, 2012);
b.putInt(DatePickerDialogFragment.MONTH, 6);
b.putInt(DatePickerDialogFragment.DATE, 17);
DialogFragment picker = new DatePickerDialogFragment();
picker.setArguments(b);
picker.show(getActivity().getSupportFragmentManager(), "fragment_date_picker");

Solution 2

I'm gonna add my own riff on the solution David Cesarino posted, in case you're not using Fragments, and want an easy way to fix it in all versions (2.1 thru 4.1):

public class FixedDatePickerDialog extends DatePickerDialog {
  //I use a Calendar object to initialize it, but you can revert to Y,M,D easily
  public FixedDatePickerDialog(Calendar dateToShow, Context context, OnDateSetListener callBack) {
    super(context, null, dateToShow.get(YEAR), dateToShow.get(MONTH), dateToShow.get(DAY_OF_MONTH));
    initializePicker(callBack);
  }

  public FixedDatePickerDialog(Calendar dateToShow, Context context, int theme,
    OnDateSetListener callBack) {
    super(context, theme, null, dateToShow.get(YEAR), dateToShow.get(MONTH), dateToShow.get(DAY_OF_MONTH));
    initializePicker(callBack);
  }

  private void initializePicker(final OnDateSetListener callback) {
    try {
      //If you're only using Honeycomb+ then you can just call getDatePicker() instead of using reflection
      Field pickerField = DatePickerDialog.class.getDeclaredField("mDatePicker");
      pickerField.setAccessible(true);
      final DatePicker picker = (DatePicker) pickerField.get(this);
      this.setCancelable(true);
      this.setButton(DialogInterface.BUTTON_NEGATIVE, getContext().getText(android.R.string.cancel), (OnClickListener) null);
      this.setButton(DialogInterface.BUTTON_POSITIVE, getContext().getText(android.R.string.ok),
          new DialogInterface.OnClickListener() {
              @Override
              public void onClick(DialogInterface dialog, int which) {
                picker.clearFocus(); //Focus must be cleared so the value change listener is called
                callback.onDateSet(picker, picker.getYear(), picker.getMonth(), picker.getDayOfMonth());
              }
          });
    } catch (Exception e) { /* Reflection probably failed*/ }
  }
}

Solution 3

Until the bug will be fixed I suggest not to use DatePickerDialog or TimePickerDialog. Use custom made AlertDialog with TimePicker/DatePicker widget;

Change TimePickerDialog with;

    final TimePicker timePicker = new TimePicker(this);
    timePicker.setIs24HourView(true);
    timePicker.setCurrentHour(20);
    timePicker.setCurrentMinute(15);

    new AlertDialog.Builder(this)
            .setTitle("Test")
            .setPositiveButton(android.R.string.ok, new OnClickListener() {

                @Override
                public void onClick(DialogInterface dialog, int which) {
                    Log.d("Picker", timePicker.getCurrentHour() + ":"
                            + timePicker.getCurrentMinute());
                }
            })
            .setNegativeButton(android.R.string.cancel,
                    new OnClickListener() {

                        @Override
                        public void onClick(DialogInterface dialog,
                                int which) {
                            Log.d("Picker", "Cancelled!");
                        }
                    }).setView(timePicker).show();

Change DatePickerDialog with;

    final DatePicker datePicker = new DatePicker(this);
    datePicker.init(2012, 10, 5, null);
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
        datePicker.setCalendarViewShown(false);
    }

    new AlertDialog.Builder(this)
            .setTitle("Test")
            .setPositiveButton(android.R.string.ok, new OnClickListener() {

                @Override
                public void onClick(DialogInterface dialog, int which) {
                    Log.d("Picker", datePicker.getYear() + " "
                            + (datePicker.getMonth() + 1) + " "
                            + datePicker.getDayOfMonth());
                }
            })
            .setNegativeButton(android.R.string.cancel,
                    new OnClickListener() {

                        @Override
                        public void onClick(DialogInterface dialog,
                                int which) {
                            Log.d("Picker", "Cancelled!");
                        }
                    }).setView(datePicker).show();

Solution 4

The one for TimePicker based on the solution by David Cesarino , "TL;DR: 1-2-3 dead easy steps for a global solution"

TimePickerDialog does not provide the functionality like DatePickerDialog.getDatePicker. So, OnTimeSetListener listener has to be provided. Just to keep the similarity with DatePicker workaround solution, I have maintained the old mListener concept. You can change it if you need to.

Calling and Listener is same as original solution. Just include

import android.app.TimePickerDialog;
import android.app.TimePickerDialog.OnTimeSetListener;

extend parent class,

... implements OnDateSetListener, OnTimeSetListener

Implement

 @Override
 public void onTimeSet(TimePicker view, int hourOfDay, int minute) {
 ...
 }

example calling

    Calendar cal = Calendar.getInstance();
    int hour = cal.get(Calendar.HOUR_OF_DAY);
    int minute = cal.get(Calendar.MINUTE);


    Bundle b = new Bundle();
    b.putInt(TimePickerDialogFragment.HOUR, hour);
    b.putInt(TimePickerDialogFragment.MINUTE, minute);

    DialogFragment picker = new TimePickerDialogFragment();
    picker.setArguments(b);
    picker.show(getSupportFragmentManager(), "frag_time_picker");

(Updated to handle cancel)

public class TimePickerDialogFragment extends DialogFragment {

    public static final String HOUR = "Hour";
    public static final String MINUTE = "Minute";

    private boolean isCancelled = false; //Added to handle cancel
    private TimePickerDialog.OnTimeSetListener mListener;

    //Added to handle parent listener
    private TimePickerDialog.OnTimeSetListener mTimeSetListener = new TimePickerDialog.OnTimeSetListener() {
        public void onTimeSet(TimePicker view, int hourOfDay, int minute) {
            if (!isCancelled)
            {
                mListener.onTimeSet(view,hourOfDay,minute);
            }
        }
    };
    //
    @Override
    public void onAttach(Activity activity) {
        super.onAttach(activity);
        this.mListener = (TimePickerDialog.OnTimeSetListener) activity;
    }

    @Override
    public void onDetach() {
        this.mListener = null;
        super.onDetach();
    }

    @TargetApi(11)
    @Override
    public Dialog onCreateDialog(Bundle savedInstanceState) {
        Bundle b = getArguments();
        int h = b.getInt(HOUR);
        int m = b.getInt(MINUTE);

        final TimePickerDialog picker = new TimePickerDialog(getActivity(), getConstructorListener(), h, m,DateFormat.is24HourFormat(getActivity()));

        //final TimePicker timePicker = new TimePicker(getBaseContext());
        if (hasJellyBeanAndAbove()) {
            picker.setButton(DialogInterface.BUTTON_POSITIVE,
                    getActivity().getString(android.R.string.ok),
                    new DialogInterface.OnClickListener() {
                        @Override
                        public void onClick(DialogInterface dialog, int which) {
                            isCancelled = false; //Cancel flag, used in mTimeSetListener
                        }
                    });
            picker.setButton(DialogInterface.BUTTON_NEGATIVE,
                    getActivity().getString(android.R.string.cancel),
                    new DialogInterface.OnClickListener() {
                        @Override
                        public void onClick(DialogInterface dialog, int which) {
                            isCancelled = true; //Cancel flag, used in mTimeSetListener
                        }
                    });
        }
        return picker;
    }
    private boolean hasJellyBeanAndAbove() {
        return Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN;
    }

    private TimePickerDialog.OnTimeSetListener getConstructorListener() {
        return hasJellyBeanAndAbove() ? mTimeSetListener : mListener; //instead of null, mTimeSetListener is returned.
    }
}

Solution 5

According to Ankur Chaudhary's brilliant answer on the similar TimePickerDialog issue, if we checked inside onDateSet if the given view isShown() or not, it will solve the whole issue with the minimal effort, with no need for extending the picker or checking for some hideous flags going around the code or even checking for the OS version, just do the following:

public void onDateSet(DatePicker view, int year, int month, int day) {
    if (view.isShown()) {
        // read the date here :)
    }
}

and of course the same can be done for onTimeSet as per Ankur's answer

Share:
29,234

Related videos on Youtube

davidcesarino
Author by

davidcesarino

Ubuntu and Dart. Quality over quantity, please! Keep your SNR high.

Updated on March 03, 2020

Comments

  • davidcesarino
    davidcesarino about 4 years

    --- Note to moderators: Today (July 15), I've noticed that someone already faced this problem here. But I'm not sure if it's appropriate to close this as a duplicate, since i think I provided a much better explanation of the issue. I'm not sure if I should edit the other question and paste this content there, but I'm not comfortable changing someone else's question too much. ---

    I have something weird here.

    I don't think the problem depends on which SDK you build against. The device OS version is what matters.

    Problem #1: inconsistency by default

    DatePickerDialog was changed (?) in Jelly Bean and now only provides a Done button. Previous versions included a Cancel button, and this may affect user experience (inconsistency, muscle memory from previous Android versions).

    Replicate: Create a basic project. Put this in onCreate:

    DatePickerDialog picker = new DatePickerDialog(
            this,
            new OnDateSetListener() {
                @Override
                public void onDateSet(DatePicker v, int y, int m, int d) {
                    Log.d("Picker", "Set!");
                }
            },
            2012, 6, 15);
    picker.show();
    

    Expected: A Cancel button to appear in the dialog.

    Current: A Cancel button does not appear.

    Screenshots: 4.0.3 (OK) and 4.1.1 (possibly wrong?).

    Problem #2: wrong dismiss behavior

    Dialog calls whichever listener it should call indeed, and then always calls OnDateSetListener listener. Canceling still calls the set method, and setting it calls the method twice.

    Replicate: Use #1 code, but add code below (you'll see this solves #1, but only visually/UI):

    picker.setButton(DialogInterface.BUTTON_NEGATIVE, "Cancel", 
            new DialogInterface.OnClickListener() {
                @Override
                public void onClick(DialogInterface dialog, int which) {
                    Log.d("Picker", "Cancel!");
                }
            });
    

    Expected:

    • Pressing the BACK key or clicking outside the dialog should do nothing.
    • Pressing "Cancel" should print Picker Cancel!.
    • Pressing "Set" should print Picker Set!.

    Current:

    • Pressing the BACK key or clicking outside the dialog prints Picker Set!.
    • Pressing "Cancel" prints Picker Cancel! and then Picker Set!.
    • Pressing "Set" prints Picker Set! and then Picker Set!.

    Log lines showing the behavior:

    07-15 12:00:13.415: D/Picker(21000): Set!
    
    07-15 12:00:24.860: D/Picker(21000): Cancel!
    07-15 12:00:24.876: D/Picker(21000): Set!
    
    07-15 12:00:33.696: D/Picker(21000): Set!
    07-15 12:00:33.719: D/Picker(21000): Set!
    

    Other notes and comments

    • Wrapping it around a DatePickerFragment doesn't matter. I simplified the problem for you, but I've tested it.
    • Michael Hampton
      Michael Hampton almost 12 years
      Congratulations, you seem to have found a bug in Android. You can report it here.
    • Lumii
      Lumii over 11 years
      Very well written bug report. I can understand it completely without having to test run the code.
    • Bradley
      Bradley over 11 years
      I urge everyone to vote this issue up! Issue 34833
    • Karthik Balakrishnan
      Karthik Balakrishnan almost 11 years
      Couldn't you just override the button function to act like it was dismissed due to touch outside the dialog?
    • davidcesarino
      davidcesarino almost 11 years
      @Torcellite not sure what you mean. The behavior is not dependent on how you dismiss the dialog (i.e., if by clicking outside or using a button, or back), since the listener is called whenever the dialog is stopped. See my answer below, when I quote the Android sources. Thus, the point is to tell the framework to not call the listener somehow, and the framework will not call only when it detects a null listener, hence why the class I wrote selectively pass the listener in the constructor or later on, depending on which API you are. Transparently to the client, of course.
    • Karthik Balakrishnan
      Karthik Balakrishnan almost 11 years
      My point is, instead of working so much why couldn't you just make Android think that the click on the Done button was a click outside the dialog, thereby getting only one alarm anyway? It's a rough hack, but very easy.
    • davidcesarino
      davidcesarino almost 11 years
      @Torcellite Oh, I see... If you fix "Back" behavior (either a dialog back or the system back), I guess you could. But, personally, I think that, by the time I filled all my client classes with the same calls everywhere just to "hack around" the bug in a very obscure manner ("why does this 'Done' button do nothing?"), it would be minimal effort to just put everything in a reusable class that can effortlessly replace the framework one, to begin with. Not only I just fixed the fundamental issue (onStop behavior), but also have superior readability in my clientS. I saved myself future time.
    • Xavier Egea
      Xavier Egea almost 11 years
      Just to add another problem using TimePicker I have found, is the difference in buttons position between 4.0.3 version and the old 2.3.3 where the cancel button is on the right and the set button on the left. I haven't tested in version 3.x, but I think this hadn't been fixed till 4.0.x version. As has been posted in the answer, this fix has been broken in the 4.1.x setting one "Set" button and no possibility to cancel or dismiss.
    • davidcesarino
      davidcesarino almost 11 years
      @XavierEgea Not sure I understand what you mean... I thought they purposely changed the button position for usability reasons (well, at least for right handed people I guess). About 2.x and below, it's better to not mess with it and rely on the system defaults where muscle memory is a factor to consider for the user.
    • Xavier Egea
      Xavier Egea almost 11 years
      @DavidCesarino After my comment I have tested a little bit more on 2.x devices and I realised (correct please if I'm wrong) that in old versions the default cancel button was on the right, at least in the System applications that I've tested. So, you are right, it's better to rely on System Defaults to avoid changing completely the application. Thanks
    • davidcesarino
      davidcesarino almost 11 years
      No problem! That's what I meant as well: before, it was Ok-Cancel, like in desktop systems. After ICS, it's Cancel-OK for usability reasons. Additionally, let's remember that this applies to all dialogs if you use the standard POSITIVE and NEGATIVE identifiers. It is not exclusive to DatePicker or TimePicker dialogs.
    • erdomester
      erdomester over 9 years
      Bug is still open after 2 years...unbelievable.
  • CommonsWare
    CommonsWare almost 12 years
    Awesome job on the research! Sad that it was necessary, but awesome nonetheless.
  • span
    span almost 12 years
    Very nice! I've been hammering my head against the wall on this issue that was updating my textfields and calling/not calling the correct callbacks. Thank you! I wonder if the suggested workaround is "future proof" or if you think it will cause problems when the bug is fixed?
  • davidcesarino
    davidcesarino almost 12 years
    Good question. I don't think anyone except Google can guarantee anything. I still think they should (and will) revert back to the "up-to-ICS" behavior at some point in the future. About future-proofing, I can only see the null listener being the possible point of failure. But considering legacy support for the API, I don't think they would (ever?) remove that null check, which in case you'd be fine. Please note, however, that this is a minimal example for a working solution. I'm NOT using it in production code, and I've made my own DatePickerDialogFragment since then.
  • davidcesarino
    davidcesarino almost 12 years
    Btw, you can get what I'm currently using here.
  • Romain Guidoux
    Romain Guidoux over 11 years
    @DavidCesarino Nice workaround, but how do you get the date on an Android 2.x device ? (there is no getDatePicker() method on the DatePickerDialog)
  • davidcesarino
    davidcesarino over 11 years
    @RomainGuidoux see updated answer at the end. The class in the link has the smarts to only call that method in jelly bean. On anything below it will bypass that and use the default system implementation, routing the system call for ondateset to your activity. It's just that in jelly bean it takes additional measures (avoiding the bug) before routing the callback, and that involves calling that honeycomb+ method. But again, only in JB.
  • davidcesarino
    davidcesarino over 11 years
    Just remembering: if you're hardwiring the button names to ok and cancel, it's probably better to use the standard android.R.string.ok and android.R.string.cancel fields, instead of user's own. And thanks for the reply.
  • dmon
    dmon over 11 years
    Ah, nice, I haven't noticed they provided OK and Cancel text. Thanks!
  • Bill Phillips
    Bill Phillips over 11 years
    Awesome work here. I don't have the words for how ridiculous this issue is.
  • Kishore
    Kishore over 11 years
    getting nullpointer exception at super(context, theme, null, dateToShow.get(YEAR), dateToShow.get(MONTH), dateToShow.get(DAY_OF_MONTH));
  • dmon
    dmon over 11 years
    Errr... are you passing a null dateToShow? The other null there is actually the "fix" so that should be there. Which version are you on?
  • Adil Malik
    Adil Malik about 11 years
    I have on confusion now how can I get the selected Month, Day and Year in onClick method of 'Set' ?
  • Adil Malik
    Adil Malik about 11 years
    can you please let me know which import did you have for Field? I am having 6 options and none of them worked.
  • davidcesarino
    davidcesarino about 11 years
    @AdilMalik You mean in the class I'm using? It routes the selected data to your Activity through the onDateSet call. As you can see, the Activity in which you use the dialog implements onDateSetListener. Of course, you're free to change it, but I think that way it works well (without the need to keep track of changes and everything).
  • davidcesarino
    davidcesarino about 11 years
    Plain and simple, it does not work the way you tell. timePickerListener is still called regardless of what you do in your dialog. I don't even need to test to know that, you just need to look at the sources: if you do not set it to null, tryNotifyTimeSet() will call the listener's onTimeSet() both in its onClick() and onStop().
  • davidcesarino
    davidcesarino about 11 years
    In other words, do not let the native class hold a reference to your listener (i.e., passing it in the constructor). How you deal with buttons is irrelevant.
  • Crocodile
    Crocodile about 11 years
    Hi David, You didn't test this and you assumed it wont work. This is the exact piece of code I am currently using in my app and it works like a champ.
  • davidcesarino
    davidcesarino about 11 years
    1) I never said it didn't work. I said it didn't work the way you said it did. Please read again. 2) you violated LSP and SRP, wasting man-hours to needlessly change all your entire client logic that didn't need any changes to begin with. 3) your answer addresses the question, yes, otherwise I'd flag it for removal as "not an answer", but 4) your answer is still (sorry about it) very inefficient and does not address the fundamental design issue (listener called), hence only the downvote. 6) you disabled back and made it a modal window to force the boolean. So, 6 serious issues there!
  • davidcesarino
    davidcesarino almost 11 years
    Once again, just like I told the other guy before you, this does not solve the bug. I don't want to sound rude, but you guys should be testing your own solution before posting here... just to prove my point, use your code and let the user press back to cancel the dialog and see what I mean. The cause of the bug is onStop calling the method when it shouldn't... it firing twice is a consequence of the bug.
  • davidcesarino
    davidcesarino almost 11 years
    If I wasn't clear enough, let me be: pressing back to cancel the dialog calls onDateSet. Thus, broken.
  • chamikaw
    chamikaw almost 11 years
    Thank you for showing the error. I edit the answer so that it will work with back button. Your answer is working but DatePicker dp = picker.getDatePicker(); is not working with TimePickers since getTimePicker() method is not added. So this would be a valid answer
  • lokoko
    lokoko over 10 years
    How about TimePickerDialog ? It seems like TimePickerDialog does not have a getTimePicker()
  • davidcesarino
    davidcesarino over 10 years
    @lokoko I know. I didn't think about it yet because I don't need it.
  • DDD
    DDD over 10 years
    Even if an ActionBar is used then this might be an acceptable workaround if you never hide/show the ActionBar. To make things extra safe you can override onStart to do nothing as well (then the calls for the animation would be safely unhooked). And even if you do hide/show it, it's just the animation that's disabled.
  • dmon
    dmon over 10 years
    It's the reflection Field, so import java.lang.reflect.Field
  • erdomester
    erdomester over 9 years
    This worked for me wonderfully. Easy to understand, easy to implement! Tested on 4.4
  • Daniel Ryan
    Daniel Ryan over 9 years
    @AbdelHady your code edit was incorrect but I have edited it to make it more clear. There is no need to add a "isJellyBeanOrAbove()" method, there is no advantage, just adds unnecessary complexity. Calling resetFired is cheap.
  • AbdelHady
    AbdelHady over 9 years
    the code here is incorrect, because onDateSet will be called once when the dialog is being closed by any means, and if that mean was to set the time then it will be fired again, so we need to catch the second call, not the first one as you did,
  • AbdelHady
    AbdelHady over 9 years
    Concerning isJellyBeanOrAbove(), versions lower than Jellybean don't have the bug that this whole question is all about, & considering we want to catch the second call, the code won't run unless we make this check, believe me, I've tried my code on Emulators & real devices (with different versions) several times & it is working like a charm
  • AbdelHady
    AbdelHady over 9 years
    How we are trying to skip the second run?!!, I've tried it several times, when closing the dialog by any means onDateSet will be called once, but when choosing "done" or "set" then it will be called twice. Therefor we need to skip only the first one, so if it is called twice then & only then we have the correct date
  • Daniel Ryan
    Daniel Ryan over 9 years
    I'm not sure if that is worth a downvote. I posted this 2 years ago, this has been in our commercial application since then working just fine. No bugs reported by our QA teams or our thousands of users. This is meant to be a simple answer that people can extend. Call "resetFired" when you want it to fire again.
  • Daniel Ryan
    Daniel Ryan over 9 years
    In our app "isJellyBeanOrAbove" is not needed. The app will just work fine in all versions if you call "resetFired" in the correct areas.
  • Shadow
    Shadow over 9 years
    hi but this works for first time. how to handle for second time when user not selected by clicking done button in date and clicked outside of date picker dialog?
  • Daniel Ryan
    Daniel Ryan over 9 years
    By calling resetFired when you open the dialog again.
  • AbdelHady
    AbdelHady about 9 years
    It is not working for me when trying it on s3 API 18
  • stuckj
    stuckj about 9 years
    This is pretty similar to The Sea's answer above.
  • hiew1
    hiew1 over 8 years
    Best answer out of all the other!
  • southerton
    southerton over 7 years
    For TimePickerDialog there is another workaround stackoverflow.com/a/39636443/311060