Stop execution until User selects a button from the Alert dialog
In my Android app, I use inonordHandler to detect conflicts between mobile device database records and server database records. When a conflict is detected, I want to display an alert dialog so that the user can choose which version of the entry "wins" (server or mobile). I have placed the Alert dialog code in the ConflictHandler class, so when a conflict is detected, the Alert dialog appears. The problem is that the code doesn't stop executing when the Alert Dialog appears, so the user can choose what action to take. It always returns serverItem.
private class ConflictResolvingSyncHandler implements MobileServiceSyncHandler {
@Override
public JsonObject executeTableOperation(
final RemoteTableOperationProcessor processor, final TableOperation operation)
throws MobileServiceSyncHandlerException {
final JsonObject clientItem = processor.getItem().getAsJsonObject();
MobileServicePreconditionFailedExceptionBase ex = null;
final JsonObject[] result = {null};
try {
result[0] = operation.accept(processor);
} catch (MobileServicePreconditionFailedExceptionBase e) {
ex = e;
} catch (Throwable e) {
ex = (MobileServicePreconditionFailedExceptionBase) e.getCause();
}
if (ex != null) {
// A conflict was detected; let force the server to "win"
// by discarding the client version of the item
final AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(
context);
// set title
alertDialogBuilder.setTitle("Conflict Detected");
// set dialog message
final MobileServicePreconditionFailedExceptionBase finalEx = ex;
alertDialogBuilder
.setMessage("Choose winner")
.setCancelable(false)
.setPositiveButton("Server",new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog,int id) {
// if this button is clicked, Server wins
JsonObject serverItem = finalEx.getValue();
if (serverItem == null) {
// Item not returned in the exception, retrieving it from the server
try {
serverItem = mClient.getTable(operation.getTableName()).lookUp(operation.getItemId()).get();
} catch (Exception e) {
try {
throw new MobileServiceSyncHandlerException(e);
} catch (MobileServiceSyncHandlerException e1) {
e1.printStackTrace();
}
}
}
result[0] = serverItem;
}
})
.setNegativeButton("Client",new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog,int id) {
// if this button is clicked, Client wins
result[0]=clientItem;
}
});
runOnUiThread(new Runnable() {
@Override
public void run() {
// create alert dialog
AlertDialog alertDialog = alertDialogBuilder.create();
// show it
alertDialog.show();
}
});
}
return result[0];
}
@Override
public void onPushComplete(MobileServicePushCompletionResult result)
throws MobileServiceSyncHandlerException {
}
}
source to share
You need to block the thread that is executing executeTableOperation
until the user clicks something in the dialog. For example, using CountDownLatch
as in the code below:
private class ConflictResolvingSyncHandler implements MobileServiceSyncHandler {
@Override
public JsonObject executeTableOperation(
final RemoteTableOperationProcessor processor, final TableOperation operation)
throws MobileServiceSyncHandlerException {
final JsonObject clientItem = processor.getItem().getAsJsonObject();
MobileServicePreconditionFailedExceptionBase ex = null;
final JsonObject[] result = {null};
try {
result[0] = operation.accept(processor);
} catch (MobileServicePreconditionFailedExceptionBase e) {
ex = e;
} catch (Throwable e) {
ex = (MobileServicePreconditionFailedExceptionBase) e.getCause();
}
if (ex != null) {
// A conflict was detected; let make the client choose who "wins"
final AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(
context);
final CountDownLatch latch = new CountDownLatch(1);
// set title
alertDialogBuilder.setTitle("Conflict Detected");
// set dialog message
final MobileServicePreconditionFailedExceptionBase finalEx = ex;
alertDialogBuilder
.setMessage("Choose winner")
.setCancelable(false)
.setPositiveButton("Server",new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog,int id) {
// if this button is clicked, Server wins
JsonObject serverItem = finalEx.getValue();
if (serverItem == null) {
// Item not returned in the exception, retrieving it from the server
try {
serverItem = mClient.getTable(operation.getTableName()).lookUp(operation.getItemId()).get();
} catch (Exception e) {
try {
throw new MobileServiceSyncHandlerException(e);
} catch (MobileServiceSyncHandlerException e1) {
e1.printStackTrace();
}
}
}
result[0] = serverItem;
latch.countDown();
}
})
.setNegativeButton("Client",new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog,int id) {
// if this button is clicked, Client wins
result[0] = clientItem;
latch.countDown();
}
});
runOnUiThread(new Runnable() {
@Override
public void run() {
// create alert dialog
AlertDialog alertDialog = alertDialogBuilder.create();
// show it
alertDialog.show();
}
});
try {
latch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
return result[0];
}
@Override
public void onPushComplete(MobileServicePushCompletionResult result)
throws MobileServiceSyncHandlerException {
}
}
source to share
To stop execution, I added a boolean variable called "loop" with the value true in the while loop. When the user selects a button from the Alert dialog, the "loop" variable is set to false. The code I added is in bold. Although I believe this practice should be avoided.
private class ConflictResolvingSyncHandler implements MobileServiceSyncHandler {
**private boolean loop = true;**
@Override
public JsonObject executeTableOperation(
final RemoteTableOperationProcessor processor, final TableOperation operation)
throws MobileServiceSyncHandlerException {
final JsonObject clientItem = processor.getItem().getAsJsonObject();
MobileServicePreconditionFailedExceptionBase ex = null;
final JsonObject[] result = {null};
try {
result[0] = operation.accept(processor);
} catch (MobileServicePreconditionFailedExceptionBase e) {
ex = e;
} catch (Throwable e) {
ex = (MobileServicePreconditionFailedExceptionBase) e.getCause();
}
if (ex != null) {
// A conflict was detected
final AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(
context);
// set title
alertDialogBuilder.setTitle("Conflict Detected");
// set dialog message
final MobileServicePreconditionFailedExceptionBase finalEx = ex;
alertDialogBuilder
.setMessage("Choose winner")
.setCancelable(true)
.setPositiveButton("Server",new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog,int id) {
// if this button is clicked, Server wins
JsonObject serverItem = finalEx.getValue();
if (serverItem == null) {
// Item not returned in the exception, retrieving it from the server
try {
serverItem = mClient.getTable(operation.getTableName()).lookUp(operation.getItemId()).get();
} catch (Exception e) {
try {
throw new MobileServiceSyncHandlerException(e);
} catch (MobileServiceSyncHandlerException e1) {
e1.printStackTrace();
}
}
}
result[0] = serverItem;
**loop = false;**
}
})
.setNegativeButton("Client",new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog,int id) {
ToDoItem item = new ToDoItem();
// if this button is clicked, Client wins
result[0] = clientItem;
// Convert Json object to an item of my class
item.setId(clientItem.get("id").getAsString());
item.setText(clientItem.get("text").getAsString());
item.setComplete(clientItem.get("complete").getAsBoolean());
// update server table
updateItem(item);
new AsyncTask<Void, Void, Void>() {
@Override
protected Void doInBackground(Void... params) {
try {
// send changes to the server DB
mClient.getSyncContext().push().get();
// get query answer from the server DB
mToDoTable.pull(mPullQuery).get();
refreshItemsFromTable();
} catch (final Exception exception) {
runOnUiThread(new Runnable() {
@Override
public void run() {
createAndShowDialog(exception, "Error");
}
});
}
return null;
}
}.execute();
**loop = false;**
}
});
runOnUiThread(new Runnable() {
@Override
public void run() {
// create alert dialog
AlertDialog alertDialog = alertDialogBuilder.create();
// show it
alertDialog.show();
}
});
}
**while(loop){}**
return result[0];
}
@Override
public void onPushComplete(MobileServicePushCompletionResult result)
throws MobileServiceSyncHandlerException {
}
}
source to share