How to show a dialog to confirm that the user wishes to exit an Android Activity?
Asked Answered
B

11

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.

Bruit answered 13/2, 2010 at 15:1 Comment(7)
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.Pocketbook
Displaying an exit confirmation option is compulsory for listing at Samsung's App store. One of my Apps was rejected for not having this.Capping
That's obnoxious, @Samsung.Matey
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.Isodimorphism
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.Plait
I've created a solution for this. Please read more chintanrathod.com/…Underclassman
@ Tom R: Totally disagree. There are apps that you dont want to finish by accident. They work in business environment and so on.Exemplify
A
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);
    }

}
Augustin answered 13/2, 2010 at 15:56 Comment(8)
Also in 2.0 and above there is a new onBackPressed event that is recommended over onKeyDown developer.android.com/intl/zh-TW/reference/android/app/… There is a section here talking about the changes and new recommended approach. developer.android.com/intl/zh-TW/sdk/android-2.0.htmlFlotage
Blog post on catching the back key here: android-developers.blogspot.com/2009/12/… 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.Volgograd
This works perfectly but how do you force code execution to stop while it's being showed to the user?Chronaxie
found it, this is a great solution here: #4381796Chronaxie
This is the method() what I am looking for but I did not know where to call this method().Bracteole
Consider android.R.string.no and android.R.string.yes for the standard "Cancel" and "Accept" labels.Trying
@Bracteole You just need to copy and paste the onBackPressed method into whichever of your activities you need to override the 'back' behaviour for.Archon
make sure to remove super.onBackPressed(); or else it will force close the app..Reft
F
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();
}
Fives answered 30/8, 2011 at 8:0 Comment(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.onBackPressedNessy
@Nessy Can you clarify your comment... Are you saying it's better to replace the finish() code with super.onBackPressed() ?Archon
Comment is 3 years old. I have no idea what is good practice in 2015Nessy
@Nessy Your comment referred to future APIs, but it would be helpful if you could clarify anyway.Archon
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."Archon
W
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();
    }

}
Woden answered 28/3, 2012 at 5:55 Comment(0)
A
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

Arela answered 16/1, 2014 at 9:5 Comment(2)
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.Supernatant
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 )Babbittry
J
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();
}
Jamey answered 27/11, 2013 at 0:43 Comment(1)
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.Golgotha
H
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);
} 
Hulbard answered 25/8, 2017 at 0:36 Comment(0)
K
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
}
Kenyakenyatta answered 27/10, 2016 at 14:59 Comment(0)
D
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();

}
Diane answered 22/9, 2017 at 9:52 Comment(0)
C
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();
                }
            }
        }
    }
Chadd answered 13/3, 2018 at 12:55 Comment(1)
y first activity, cant i put it in activity in middle. because now it jsut closes the current activity and it shows the previous oneDoti
A
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();
    }
}
Antecedency answered 8/4, 2016 at 12:57 Comment(0)
C
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();
}
Callan answered 11/3, 2018 at 7:27 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.