It seems to be there is no easy way to get an Alert dialog to return a simple value.
This code does not work (the answer variable cannot be set
I was also struggling to use a blocking confirm dialog and I finally did it using a BlockingQueue :
public static class BlockingConfirmDialog{
private Activity context;
BlockingQueue<Boolean> blockingQueue;
public BlockingConfirmDialog(Activity activity) {
super();
this.context = activity;
blockingQueue = new ArrayBlockingQueue<Boolean>(1);
}
public boolean confirm(final String title, final String message){
context.runOnUiThread(new Runnable() {
@Override
public void run() {
new AlertDialog.Builder(context)
.setTitle(title)
.setMessage(message)
.setPositiveButton("Ok", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
blockingQueue.add(true);
}
})
.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
blockingQueue.add(false);
}
})
.show();
}
});
try {
return blockingQueue.take();
} catch (InterruptedException e) {
e.printStackTrace();
return false;
}
}
}
I've tried all of the solutions and the easier and cleanest is by far the first solution with the Runnable, in my opinion. It supports the dialog on the Cancel button listener, the OnBAckPressed() and the onOptionsItemSelected().
The code as described though calls the ans_false.run(); when clicking the BUTTON_POSITIVE and the ans_true.run(); when clicking the BUTTON_NEGATIVE.
Here is the code I used to fix that problem:
public class MyDialogs {
// private constructor
public Runnable answerTrue = null;
public Runnable answerFalse = null;
// Dialog. --------------------------------------------------------------
public boolean confirm(Activity act, String Title, String ConfirmText,
String noBtn, String yesBtn, Runnable yesProc, Runnable noProc) {
answerTrue = yesProc;
answerFalse= noProc;
AlertDialog.Builder alert = new AlertDialog.Builder(act);
alert.setTitle(Title);
alert.setMessage(ConfirmText);
alert.setCancelable(false);
alert.setPositiveButton(R.string.button_positive, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
answerTrue.run();
}
});
alert.setNegativeButton(R.string.button_negative, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
answerFalse.run();
}
});
alert.show().getButton(DialogInterface.BUTTON_NEGATIVE).requestFocus();
return true;
}
}
I find that using jDeferred helps in cases where you want to wait for input.
It is essentially equivalent to using an interface, but instead you create done and fail handlers. Just an alternative to consider:
new ConfirmationDialog(mContext)
.showConfirmation("Are you sure?", "Yes", "No")
.done(new DoneCallback<Void>() {
@Override
public void onDone(Void aVoid) {
....
}
})
.fail(new FailCallback<Void>() {
@Override
public void onFail(Void aVoid) {
...
}
});
Implementation:
public class ConfirmationDialog {
private final Context mContext;
private final DeferredObject<Void, Void, Void> mDeferred = new DeferredObject<Void, Void, Void>();
public ConfirmationDialog(Context context) {
mContext = context;
}
public Promise<Void, Void, Void> showConfirmation(String message, String positiveButton, String negativeButton) {
AlertDialog dialog = new AlertDialog.Builder(mContext).create();
dialog.setTitle("Alert");
dialog.setMessage(message);
dialog.setCancelable(false);
dialog.setButton(DialogInterface.BUTTON_POSITIVE, positiveButton, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int buttonId) {
mDeferred.resolve(null);
}
});
dialog.setButton(DialogInterface.BUTTON_NEGATIVE, negativeButton, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int buttonId) {
mDeferred.reject(null);
}
});
dialog.setIcon(android.R.drawable.ic_dialog_alert);
dialog.show();
return mDeferred.promise();
}
}
Well, I was going to say that I am very pleased with myself because I found a simple answer, all by myself!
But the truth is that although I find a way to return a value (which I show below). It is of no use.
The real problem is I wanted a synchronous Dialog, a dialog that waits for the user to answer before resuming your code after dialog.show()
.
There is no such beast in Android. All dialogs are asynchronous, so dialog.show()
only posts the dialog in some queue (I think) and continues. Thus you don't get your answer in time.
For all its worth (nothing) below you'll find how to set a value inside the method that builds the dialog. Maybe there are other uses for this technique, not related to the dialog lifecycle.
To give some related info, I'll say that if you replace
boolean answer;
with
final boolean answer;
it is possible to access the variable from within the listener, but it is not possible to assign it a new value, since it was declared as final.
Here comes the trick.
Define the variable as:
final boolean[] answer = new boolean[1];
Some of you already see why this will work. The final variable here is not the single element of the boolean array, is the array itself.
So now you can assign the array element [0] as you wish.
dialog.setButton(DialogInterface.BUTTON_POSITIVE, "Yes", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int buttonId) {
answer[0] = true;
}
});
. . .
return answer[0];
And finally you can return it from your method.
With Andriod, it is not a good idea to block the running thread by waiting for the user to say 'yes' or 'no'.
In order to ask for a confirmation, you can define a method that receives an AsynTask. The method executes that task if the user press the confirm button.
For example:
//this method displays a confirm dialog. If 'yes' answer, runs 'yesTask',
//if 'no' answer, runs 'noTask'
//notice than 'yesTask' and 'noTask' are AysncTask
//'noTask' can be null, example: if you want to cancel when 'no answer'
public static void confirm(Activity act, String title, String confirmText,
String noButtonText, String yesButtonText,
final AsyncTask<String, Void, Boolean> yesTask,
final AsyncTask<String, Void, Boolean> noTask) {
AlertDialog dialog = new AlertDialog.Builder(act).create();
dialog.setTitle(title);
dialog.setMessage(confirmText);
dialog.setCancelable(false);
dialog.setButton(DialogInterface.BUTTON_POSITIVE, yesButtonText,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int buttonId) {
yesTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}
});
dialog.setButton(DialogInterface.BUTTON_NEGATIVE, noButtonText,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int buttonId) {
if(noTask!=null) {
noTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}
}
});
dialog.setIcon(android.R.drawable.ic_dialog_alert);
dialog.show();
}
You can call it from your activity with:
YourTask yourTask = new YourTask( ... );
confirm( YourActivity.this,
"Confirm",
"Are you sure?",
"Cancel",
"Continue",
yourTask,
null);
YourTask
class must extend AsyncTask
Declare a field 'answer' in your activity and set a value to it. Fields of a class are visible to inner classes, so you can do that.