290

I've been trying to show a "Do you want to exit?" type of dialog when the user attempts to exit an Activity.

However I can't find the appropriate API hooks. Activity.onUserLeaveHint() initially looked promising, but I can't find a way to stop the Activity from finishing.

slhck
  • 36,575
  • 28
  • 148
  • 201
Peter A
  • 2,919
  • 2
  • 16
  • 4
  • 41
    Is it absolutely essential that you have this prompt? When a user wants to finish an Activity, they should be able to do so immediately. You may want to rethink your strategy. – Tom R Feb 13 '10 at 15:30
  • 5
    Displaying an exit confirmation option is compulsory for listing at Samsung's App store. One of my Apps was rejected for not having this. – John Ashmore Dec 05 '12 at 04:50
  • 7
    That's obnoxious, @Samsung. – dokkaebi Dec 14 '12 at 04:21
  • 4
    It depends on what you want to do upon exiting. If the state is preserved and you can just navigate back, showing a dialog might not be necessary. However, in one of my applications I clean up cookies, data, and close connections for good with a final commit of data. User's should be made aware of the finality of their choice, especially since it is uncommon today to have that sense of finality in a mobile application. – Eric Tobias Feb 25 '13 at 08:05
  • I have noticed that sometimes people (me too) confuse the options and back button since I sometimes use 180deg rotation. So I find it quite necessary that if we are exiting it should not be done by mistake. – Rijul Gupta May 29 '14 at 06:42
  • I've created a solution for this. Please read more http://chintanrathod.com/display-alert-on-back-button-pressed-in-android-studio/ – Chintan Rathod Apr 28 '15 at 08:05
  • 18
    @ Tom R: Totally disagree. There are apps that you dont want to finish by accident. They work in business environment and so on. – TomeeNS Sep 27 '15 at 13:34

11 Answers11

402

In Android 2.0+ this would look like:

@Override
public void onBackPressed() {
    new AlertDialog.Builder(this)
        .setIcon(android.R.drawable.ic_dialog_alert)
        .setTitle("Closing Activity")
        .setMessage("Are you sure you want to close this activity?")
        .setPositiveButton("Yes", new DialogInterface.OnClickListener()
    {
        @Override
        public void onClick(DialogInterface dialog, int which) {
            finish();    
        }

    })
    .setNegativeButton("No", null)
    .show();
}

In earlier versions it would look like:

@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
    //Handle the back button
    if(keyCode == KeyEvent.KEYCODE_BACK) {
        //Ask the user if they want to quit
        new AlertDialog.Builder(this)
        .setIcon(android.R.drawable.ic_dialog_alert)
        .setTitle(R.string.quit)
        .setMessage(R.string.really_quit)
        .setPositiveButton(R.string.yes, new DialogInterface.OnClickListener() {

            @Override
            public void onClick(DialogInterface dialog, int which) {

                //Stop the activity
                YourClass.this.finish();    
            }

        })
        .setNegativeButton(R.string.no, null)
        .show();

        return true;
    }
    else {
        return super.onKeyDown(keyCode, event);
    }

}
Richard Le Mesurier
  • 29,432
  • 22
  • 140
  • 255
jax
  • 37,735
  • 57
  • 182
  • 278
  • 15
    Also in 2.0 and above there is a new onBackPressed event that is recommended over onKeyDown http://developer.android.com/intl/zh-TW/reference/android/app/Activity.html#onBackPressed() There is a section here talking about the changes and new recommended approach. http://developer.android.com/intl/zh-TW/sdk/android-2.0.html – Patrick Kafka Feb 13 '10 at 19:28
  • 3
    Blog post on catching the back key here: http://android-developers.blogspot.com/2009/12/back-and-other-hard-keys-three-stories.html Note that this does not allow you to catch other ways the user can leave your app: pressing home, selecting a notification, receiving a phone call, etc. – hackbod Feb 13 '10 at 19:56
  • This works perfectly but how do you force code execution to stop while it's being showed to the user? – fIwJlxSzApHEZIl Mar 05 '13 at 22:50
  • found it, this is a great solution here: http://stackoverflow.com/questions/4381296/android-wait-on-user-input-from-dialog – fIwJlxSzApHEZIl Mar 05 '13 at 22:55
  • 1
    This is the method() what I am looking for but I did not know where to call this method(). – user2841300 Dec 18 '13 at 09:07
  • Consider android.R.string.no and android.R.string.yes for the standard "Cancel" and "Accept" labels. – Agustí Sánchez Jun 17 '15 at 20:58
  • @user2841300 You just need to copy and paste the onBackPressed method into whichever of your activities you need to override the 'back' behaviour for. – ban-geoengineering Sep 03 '15 at 18:53
  • make sure to remove super.onBackPressed(); or else it will force close the app.. – Siddy Hacks Apr 09 '20 at 19:50
191
@Override
public void onBackPressed() {
    new AlertDialog.Builder(this)
           .setMessage("Are you sure you want to exit?")
           .setCancelable(false)
           .setPositiveButton("Yes", new DialogInterface.OnClickListener() {
               public void onClick(DialogInterface dialog, int id) {
                   ExampleActivity.super.onBackPressed();
               }
           })
           .setNegativeButton("No", null)
           .show();
}
Salam El-Banna
  • 3,784
  • 1
  • 22
  • 34
Chanakya Vadla
  • 3,019
  • 2
  • 22
  • 24
  • 5
    A comment was made by elBradford: Calling the super onBackPressed is better than assuming onBackPressed will only call finish(). Even if that's true now, it may not be true in future APIs `CustomTabActivity.super.onBackPressed` – mplungjan Dec 09 '12 at 07:23
  • @mplungjan Can you clarify your comment... Are you saying it's better to replace the `finish()` code with `super.onBackPressed()` ? – ban-geoengineering Sep 03 '15 at 18:56
  • Comment is 3 years old. I have no idea what is good practice in 2015 – mplungjan Sep 03 '15 at 19:11
  • @mplungjan Your comment referred to future APIs, but it would be helpful if you could clarify anyway. – ban-geoengineering Sep 03 '15 at 20:05
  • No probs. Well, I've just tried `MyActivity.super.onBackPressed();` and it works fine for me. It seems to most logical approach, too - to call the method you are overriding if you want the standard behaviour when user taps "No." – ban-geoengineering Sep 03 '15 at 20:12
33

Have modified @user919216 code .. and made it compatible with WebView

@Override
public void onBackPressed() {
    if (webview.canGoBack()) {
        webview.goBack();

    }
    else
    {
     AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setMessage("Are you sure you want to exit?")
       .setCancelable(false)
       .setPositiveButton("Yes", new DialogInterface.OnClickListener() {
           public void onClick(DialogInterface dialog, int id) {
                finish();
           }
       })
       .setNegativeButton("No", new DialogInterface.OnClickListener() {
           public void onClick(DialogInterface dialog, int id) {
                dialog.cancel();
           }
       });
AlertDialog alert = builder.create();
alert.show();
    }

}
suraj jain
  • 1,012
  • 14
  • 26
26

I'd prefer to exit with double tap on the back button than with an exit Dialog.

In this solution, it show a toast when go back for the first time, warning that another back press will close the App. In this example less than 4 seconds.

private Toast toast;
private long lastBackPressTime = 0;

@Override
public void onBackPressed() {
  if (this.lastBackPressTime < System.currentTimeMillis() - 4000) {
    toast = Toast.makeText(this, "Press back again to close this app", 4000);
    toast.show();
    this.lastBackPressTime = System.currentTimeMillis();
  } else {
    if (toast != null) {
    toast.cancel();
  }
  super.onBackPressed();
 }
}

Token from: http://www.androiduipatterns.com/2011/03/back-button-behavior.html

Toni Gamez
  • 6,819
  • 1
  • 23
  • 18
  • 1
    This one is definitely my favorite as the Dialogbox can be cumbersome. I changed it slightly to make it 3 seconds and it seems a bit more natural with that marker. Thanks for the post. – PGMacDesign Jun 01 '15 at 22:09
  • on double back press ideally app should me exit but in my code it's open a login Activity(means back activity with enabled spinner ) – techDigi Oct 10 '16 at 12:14
22

If you are not sure if the call to "back" will exit the app, or will take the user to another activity, you can wrap the above answers in a check, isTaskRoot(). This can happen if your main activity can be added to the back stack multiple times, or if you are manipulating your back stack history.

if(isTaskRoot()) {
    AlertDialog.Builder builder = new AlertDialog.Builder(this);
    builder.setMessage("Are you sure you want to exit?")
       .setCancelable(false)
       .setPositiveButton("Yes", new DialogInterface.OnClickListener() {
           public void onClick(DialogInterface dialog, int id) {
                YourActivity.super.onBackPressed;
           }
       })
       .setNegativeButton("No", new DialogInterface.OnClickListener() {
           public void onClick(DialogInterface dialog, int id) {
                dialog.cancel();
           }
       });
    AlertDialog alert = builder.create();
    alert.show();

} else {
    super.onBackPressed();
}
GLee
  • 5,003
  • 5
  • 35
  • 39
  • Totally searching for this. Thank you.. I think this must be marked as answer. Others are useless. I was searching for the feature which isTaskRoot() method does. – Samir Alakbarov Jul 07 '20 at 14:02
7

in China, most App will confirm the exit by "click twice":

boolean doubleBackToExitPressedOnce = false;

@Override
public void onBackPressed() {
    if (doubleBackToExitPressedOnce) {
        super.onBackPressed();
        return;
    }

    this.doubleBackToExitPressedOnce = true;
    Toast.makeText(this, "Please click BACK again to exit", Toast.LENGTH_SHORT).show();

    new Handler().postDelayed(new Runnable() {

        @Override
        public void run() {
            doubleBackToExitPressedOnce=false;                       
        }
    }, 2000);
} 
Siwei
  • 19,858
  • 7
  • 75
  • 95
6

Using Lambda:

    new AlertDialog.Builder(this).setMessage(getString(R.string.exit_msg))
        .setTitle(getString(R.string.info))
        .setPositiveButton(getString(R.string.yes), (arg0, arg1) -> {
            moveTaskToBack(true);
            finish();
        })
        .setNegativeButton(getString(R.string.no), (arg0, arg1) -> {
        })
        .show();

You also need to set level language to support java 8 in your gradle.build:

compileOptions {
       targetCompatibility 1.8
       sourceCompatibility 1.8
}
vasiljevski
  • 369
  • 5
  • 9
6

First remove super.onBackPressed(); from onbackPressed() method than and below code:

@Override
public void onBackPressed() {
    AlertDialog.Builder builder = new AlertDialog.Builder(this);
    builder.setMessage("Are you sure you want to exit?")
           .setCancelable(false)
           .setPositiveButton("Yes", new DialogInterface.OnClickListener() {
               public void onClick(DialogInterface dialog, int id) {
                    MyActivity.this.finish();
               }
           })
           .setNegativeButton("No", new DialogInterface.OnClickListener() {
               public void onClick(DialogInterface dialog, int id) {
                    dialog.cancel();
               }
           });
    AlertDialog alert = builder.create();
    alert.show();

}
Anjali-Systematix
  • 1,531
  • 14
  • 17
4
Just put this code in your first activity 

@Override
    public void onBackPressed() {
        if (drawerLayout.isDrawerOpen(GravityCompat.END)) {
            drawerLayout.closeDrawer(GravityCompat.END);
        }
        else {
// if your using fragment then you can do this way
            int fragments = getSupportFragmentManager().getBackStackEntryCount();
            if (fragments == 1) {
new AlertDialog.Builder(this)
           .setMessage("Are you sure you want to exit?")
           .setCancelable(false)
           .setPositiveButton("Yes", new DialogInterface.OnClickListener() {
               public void onClick(DialogInterface dialog, int id) {
                    finish();
               }
           })
           .setNegativeButton("No", null)
           .show();


            } else {
                if (getFragmentManager().getBackStackEntryCount() > 1) {
                    getFragmentManager().popBackStack();
                } else {

           super.onBackPressed();
                }
            }
        }
    }
Mohit Hooda
  • 273
  • 3
  • 9
  • y first activity, cant i put it in activity in middle. because now it jsut closes the current activity and it shows the previous one – shareef Feb 11 '19 at 04:37
2

I like a @GLee approach and using it with fragment like below.

@Override
public void onBackPressed() {
    if(isTaskRoot()) {
        new ExitDialogFragment().show(getSupportFragmentManager(), null);
    } else {
        super.onBackPressed();
    }
}

Dialog using Fragment:

public class ExitDialogFragment extends DialogFragment {

    @Override
    public Dialog onCreateDialog(Bundle savedInstanceState) {
        return new AlertDialog.Builder(getActivity())
            .setTitle(R.string.exit_question)
            .setPositiveButton(android.R.string.yes, new DialogInterface.OnClickListener() {
                @Override
                public void onClick(DialogInterface dialog, int which) {
                    getActivity().finish();
                }
            })
            .setNegativeButton(android.R.string.no, new DialogInterface.OnClickListener() {
                @Override
                public void onClick(DialogInterface dialog, int which) {
                    getDialog().cancel();
                }
            })
            .create();
    }
}
marioosh
  • 27,328
  • 49
  • 143
  • 192
1

Another alternative would be to show a Toast/Snackbar on the first back press asking to press back again to Exit, which is a lot less intrusive than showing an AlertDialog to confirm if user wants to exit the app.

You can use the DoubleBackPress Android Library to achieve this with a few lines of code. Example GIF showing similar behaviour.

To begin with, add the dependency to your application :

dependencies {
    implementation 'com.github.kaushikthedeveloper:double-back-press:0.0.1'
}

Next, in your Activity, implement the required behaviour.

// set the Toast to be shown on FirstBackPress (ToastDisplay - builtin template)
// can be replaced by custom action (new FirstBackPressAction{...})
FirstBackPressAction firstBackPressAction = new ToastDisplay().standard(this);

// set the Action on DoubleBackPress
DoubleBackPressAction doubleBackPressAction = new DoubleBackPressAction() {
    @Override
    public void actionCall() {
        // TODO : Exit the application
        finish();
        System.exit(0);
    }
};

// setup DoubleBackPress behaviour : close the current Activity
DoubleBackPress doubleBackPress = new DoubleBackPress()
        .withDoublePressDuration(3000)     // msec - wait for second back press
        .withFirstBackPressAction(firstBackPressAction)
        .withDoubleBackPressAction(doubleBackPressAction);

Finally, set this as the behaviour on back press.

@Override
public void onBackPressed() {
    doubleBackPress.onBackPressed();
}
Kaushik NP
  • 6,733
  • 9
  • 31
  • 60