76
votes

I am trying to prevent an AlertDialog box from closing when pressing the back button in Android. I have followed both of the popular methods in this thread, and with System.out.println I can see that in both cases the code executes. However, the back button still closes the dialog box.

What could I be doing wrong? Ultimately I'm trying to prevent the back button closing a dialog box - it is a disclaimer that is displayed the first time the app is run and I don't want the user to have any option but to press the "Accept" button in order for the app to continue.

7
If you require a user to accept a license or disclaimer (or whatever) then you MUST allow them to refuse. If they click the BACK button then make sure your license/disclaimer dialog appears again next time they start the app and again and again until they either accept or remove the app from their device. If you choose to use the approach suggested by Sam then you MUST provide a 'Refuse' button but then re-create the license/disclaimer next time they start the app. - Squonk
I would remove an app that gives me "accept" as the only option... and don't even think about disabling the home button! - WarrenFaith
The app can be closed through pressing the home button, but essentially the app cannot progress past the disclaimer if the license is not accepted. @Squonk, you mention that I must allow them to refuse; is this a legal issue? i.e. is there a law that software must be usable (if it has been paid for) without accepting the licence agreement? n.b. I already have a 'decline' button, and pressing this pops up a Toast() saying that you must accept the agreement in order to use the app. The dialog box thus persists until accept is pressed. - CaptainProg
@CaptainProg : I'm not a lawyer but I suspect in some countries it might be considered a legal issue. I'm not suggesting that you allow people to use you app if they refuse/decline the disclaimer. All I'm saying is it is bad policy, bad customer relations (even if the app is free) and bad for the UX to lock somebody in. Simply have the 'decline' option close the app and next time the user tries to start it, present the same dialog. Don't just popup a Toast, leave them locked in and rely on them using thw HOME button to get out. - Squonk

7 Answers

180
votes

Simply use the setCancelable() feature:

AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setCancelable(false);

This prevents the back button from closing the dialog, but leaves the "negative" button intact if you chose to use it.


While any user that does not want to accept your terms of service can push the home button, in light of Squonk's comment, here two more ways to prevent them from "backing out" of the user agreement. One is a simple "Refuse" button and the other overrides the back button in the dialog:

builder.setNegativeButton("Refuse", new OnClickListener() {
           @Override
           public void onClick(DialogInterface dialog, int which) {
               finish();
           }
       })
       .setOnKeyListener(new OnKeyListener() {
           @Override
           public boolean onKey(DialogInterface dialog, int keyCode, KeyEvent event) {
               if(keyCode == KeyEvent.KEYCODE_BACK && event.getAction() == KeyEvent.ACTION_UP)
                   finish();
               return false;
           }
       });
22
votes

To prevent the back button closes a Dialog (without depending on Activity), just the following code:

alertDialog.setOnKeyListener(new DialogInterface.OnKeyListener() {
    @Override
    public boolean onKey(DialogInterface dialog, int keyCode, KeyEvent event) {
        // Prevent dialog close on back press button
        return keyCode == KeyEvent.KEYCODE_BACK;
    }
});
17
votes

When using DialogFragment you will need to call setCancelable() on the fragment, not the dialog:

@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
    dialog = new ProgressDialog(getActivity());
    dialog.setIndeterminate(true);
    dialog.setMessage(...);
    setCancelable(false);

    return dialog;
}

Calling dialog.setCancelable() seem to have no effect. It seems that DialogFragment does not takes notice of the dialog's setting for cancelability.

3
votes

Use setCancelable(false)

SampleDialog sampleDialog = SampleDialog.newInstance();
sampleDialog.setCancelable(false);
sampleDialog.show(getSupportFragmentManager(), SampleDialog.class.getSimpleName());
0
votes

Only this worked for me:

AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
builder.setTitle(title);
builder.setMessage(content);

/**
 * Make it when the Back button is pressed, the dialog isn't dismissed.
 */
builder.setOnKeyListener(new DialogInterface.OnKeyListener() {
    @Override
    public boolean onKey(DialogInterface dialog, int keyCode, KeyEvent event) {
        if(keyCode == KeyEvent.KEYCODE_BACK && event.getAction() == KeyEvent.ACTION_UP) {
            Utilities.makeToast(getContext(), "There is no way back!");
            return true; // Consumed
        }
        else {
            return false; // Not consumed
        }
    }
});

Dialog dialog = builder.create();

/**
 * Make it so touching on the background activity doesn't close the dialog
 */
dialog.setCanceledOnTouchOutside(false);

return dialog;

As you can see, I also added a dialog.setCanceledOnTouchOutside(false); line so tapping outside of the dialog doesn't result in it being closed either.

0
votes

In JQuery Mobile a popup adds a hash to the url, the following code allows the back to dismiss the popup when open and return to the app when closed. You could use the same logic for a custom ui framework.

@Override
public void onBackPressed() {

    // check if modal is open #&ui-state=dialog

    if (webView.getVisibility() == View.VISIBLE && webView.getUrl().contains("#&ui-state=dialog")) {
        // don't pass back button action
        if (webView.canGoBack()) {
            webView.goBack();
        }
    } else {
        // pass back button action
        super.onBackPressed();
    }
}
0
votes
AlertDialog.Builder builder = AlertDialog.Builder(this)
Dialog dialog = builder.create()
dialog.setCancelable(false)
dialog.setCanceledOnTouchOutside(false)

This will prevent the user on canceling the dialog when they press the back button or touch outside the dialog window