Skip to content

Commit bf750da

Browse files
committed
Show dialogs on UI thread
1 parent 494453a commit bf750da

File tree

3 files changed

+110
-60
lines changed

3 files changed

+110
-60
lines changed

firebase-appdistribution/src/main/java/com/google/firebase/appdistribution/FirebaseAppDistribution.java

Lines changed: 71 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -215,34 +215,40 @@ private Task<Void> showSignInConfirmationDialog(Activity hostActivity) {
215215
showSignInDialogTask = new TaskCompletionSource<>();
216216
}
217217

218-
signInConfirmationDialog = new AlertDialog.Builder(hostActivity).create();
219218
dialogHostActivity = hostActivity;
220219

221-
Context context = firebaseApp.getApplicationContext();
222-
signInConfirmationDialog.setTitle(context.getString(R.string.signin_dialog_title));
223-
signInConfirmationDialog.setMessage(context.getString(R.string.singin_dialog_message));
224-
225-
signInConfirmationDialog.setButton(
226-
AlertDialog.BUTTON_POSITIVE,
227-
context.getString(R.string.singin_yes_button),
228-
(dialogInterface, i) -> showSignInDialogTask.setResult(null));
229-
230-
signInConfirmationDialog.setButton(
231-
AlertDialog.BUTTON_NEGATIVE,
232-
context.getString(R.string.singin_no_button),
233-
(dialogInterface, i) ->
234-
showSignInDialogTask.setException(
235-
new FirebaseAppDistributionException(
236-
ErrorMessages.AUTHENTICATION_CANCELED, AUTHENTICATION_CANCELED)));
237-
238-
signInConfirmationDialog.setOnCancelListener(
239-
dialogInterface ->
240-
showSignInDialogTask.setException(
241-
new FirebaseAppDistributionException(
242-
ErrorMessages.AUTHENTICATION_CANCELED, AUTHENTICATION_CANCELED)));
243-
244-
signInConfirmationDialog.show();
245-
220+
// We may not be on the main (UI) thread in some cases, specifically if the developer calls
221+
// the basic config from the background. If we are already on the main thread, this will
222+
// execute immediately.
223+
hostActivity.runOnUiThread(
224+
() -> {
225+
signInConfirmationDialog = new AlertDialog.Builder(hostActivity).create();
226+
227+
Context context = firebaseApp.getApplicationContext();
228+
signInConfirmationDialog.setTitle(context.getString(R.string.signin_dialog_title));
229+
signInConfirmationDialog.setMessage(context.getString(R.string.singin_dialog_message));
230+
231+
signInConfirmationDialog.setButton(
232+
AlertDialog.BUTTON_POSITIVE,
233+
context.getString(R.string.singin_yes_button),
234+
(dialogInterface, i) -> showSignInDialogTask.setResult(null));
235+
236+
signInConfirmationDialog.setButton(
237+
AlertDialog.BUTTON_NEGATIVE,
238+
context.getString(R.string.singin_no_button),
239+
(dialogInterface, i) ->
240+
showSignInDialogTask.setException(
241+
new FirebaseAppDistributionException(
242+
ErrorMessages.AUTHENTICATION_CANCELED, AUTHENTICATION_CANCELED)));
243+
244+
signInConfirmationDialog.setOnCancelListener(
245+
dialogInterface ->
246+
showSignInDialogTask.setException(
247+
new FirebaseAppDistributionException(
248+
ErrorMessages.AUTHENTICATION_CANCELED, AUTHENTICATION_CANCELED)));
249+
250+
signInConfirmationDialog.show();
251+
});
246252
return showSignInDialogTask.getTask();
247253
}
248254

@@ -420,42 +426,47 @@ private Task<Void> showUpdateConfirmationDialog(
420426
}
421427

422428
Context context = firebaseApp.getApplicationContext();
423-
424-
updateConfirmationDialog = new AlertDialog.Builder(hostActivity).create();
425429
dialogHostActivity = hostActivity;
426-
updateConfirmationDialog.setTitle(context.getString(R.string.update_dialog_title));
427430

428-
StringBuilder message =
429-
new StringBuilder(
430-
String.format(
431-
"Version %s (%s) is available.",
432-
newRelease.getDisplayVersion(), newRelease.getVersionCode()));
433-
434-
if (newRelease.getReleaseNotes() != null && !newRelease.getReleaseNotes().isEmpty()) {
435-
message.append(String.format("\n\nRelease notes: %s", newRelease.getReleaseNotes()));
436-
}
437-
updateConfirmationDialog.setMessage(message);
438-
439-
updateConfirmationDialog.setButton(
440-
AlertDialog.BUTTON_POSITIVE,
441-
context.getString(R.string.update_yes_button),
442-
(dialogInterface, i) -> showUpdateDialogTask.setResult(null));
443-
444-
updateConfirmationDialog.setButton(
445-
AlertDialog.BUTTON_NEGATIVE,
446-
context.getString(R.string.update_no_button),
447-
(dialogInterface, i) ->
448-
showUpdateDialogTask.setException(
449-
new FirebaseAppDistributionException(
450-
ErrorMessages.UPDATE_CANCELED, Status.INSTALLATION_CANCELED)));
451-
452-
updateConfirmationDialog.setOnCancelListener(
453-
dialogInterface ->
454-
showUpdateDialogTask.setException(
455-
new FirebaseAppDistributionException(
456-
ErrorMessages.UPDATE_CANCELED, Status.INSTALLATION_CANCELED)));
457-
458-
updateConfirmationDialog.show();
431+
// We should already be on the main (UI) thread here, but be explicit just to be safe. If we are
432+
// already on the main thread, this will execute immediately.
433+
hostActivity.runOnUiThread(
434+
() -> {
435+
updateConfirmationDialog = new AlertDialog.Builder(hostActivity).create();
436+
updateConfirmationDialog.setTitle(context.getString(R.string.update_dialog_title));
437+
438+
StringBuilder message =
439+
new StringBuilder(
440+
String.format(
441+
"Version %s (%s) is available.",
442+
newRelease.getDisplayVersion(), newRelease.getVersionCode()));
443+
444+
if (newRelease.getReleaseNotes() != null && !newRelease.getReleaseNotes().isEmpty()) {
445+
message.append(String.format("\n\nRelease notes: %s", newRelease.getReleaseNotes()));
446+
}
447+
updateConfirmationDialog.setMessage(message);
448+
449+
updateConfirmationDialog.setButton(
450+
AlertDialog.BUTTON_POSITIVE,
451+
context.getString(R.string.update_yes_button),
452+
(dialogInterface, i) -> showUpdateDialogTask.setResult(null));
453+
454+
updateConfirmationDialog.setButton(
455+
AlertDialog.BUTTON_NEGATIVE,
456+
context.getString(R.string.update_no_button),
457+
(dialogInterface, i) ->
458+
showUpdateDialogTask.setException(
459+
new FirebaseAppDistributionException(
460+
ErrorMessages.UPDATE_CANCELED, Status.INSTALLATION_CANCELED)));
461+
462+
updateConfirmationDialog.setOnCancelListener(
463+
dialogInterface ->
464+
showUpdateDialogTask.setException(
465+
new FirebaseAppDistributionException(
466+
ErrorMessages.UPDATE_CANCELED, Status.INSTALLATION_CANCELED)));
467+
468+
updateConfirmationDialog.show();
469+
});
459470

460471
return showUpdateDialogTask.getTask();
461472
}

firebase-appdistribution/src/main/java/com/google/firebase/appdistribution/FirebaseAppDistributionLifecycleNotifier.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,10 @@ interface OnActivityDestroyedListener {
9898
/**
9999
* Apply a function to a foreground activity, when one is available, returning a {@link Task} that
100100
* will complete immediately after the function is applied.
101+
*
102+
* <p>The consumer function will be called immediately once the activity is available. This may be
103+
* on the main thread or the calling thread, depending on whether or not there is already a
104+
* foreground activity available when this method is called.
101105
*/
102106
Task<Void> applyToForegroundActivity(ActivityConsumer consumer) {
103107
return getForegroundActivity()
@@ -117,6 +121,10 @@ Task<Void> applyToForegroundActivity(ActivityConsumer consumer) {
117121
/**
118122
* Apply a function to a foreground activity, when one is available, returning a {@link Task} that
119123
* will complete with the result of the Task returned by that function.
124+
*
125+
* <p>The continuation function will be called immediately once the activity is available. This
126+
* may be on the main thread or the calling thread, depending on whether or not there is already
127+
* a foreground activity available when this method is called.
120128
*/
121129
<T> Task<T> applyToForegroundActivityTask(SuccessContinuation<Activity, T> continuation) {
122130
return getForegroundActivity()

firebase-appdistribution/src/test/java/com/google/firebase/appdistribution/FirebaseAppDistributionTest.java

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,10 @@
6161
import com.google.firebase.installations.InstallationTokenResult;
6262
import java.util.ArrayList;
6363
import java.util.List;
64+
import java.util.concurrent.ExecutionException;
65+
import java.util.concurrent.ExecutorService;
66+
import java.util.concurrent.Executors;
67+
import java.util.concurrent.Future;
6468
import org.junit.Before;
6569
import org.junit.Test;
6670
import org.junit.runner.RunWith;
@@ -247,6 +251,19 @@ public void updateToNewRelease_whenNewAabReleaseAvailable_showsUpdateDialog() {
247251
shadowOf(dialog).getMessage().toString());
248252
}
249253

254+
@Test
255+
public void updateToNewRelease_fromABackgroundThread_showsUpdateDialog()
256+
throws InterruptedException {
257+
when(mockNewReleaseFetcher.checkForNewRelease())
258+
.thenReturn(Tasks.forResult((TEST_RELEASE_NEWER_AAB_INTERNAL.build())));
259+
260+
ExecutorService executorService = Executors.newSingleThreadExecutor();
261+
executorService.submit(() -> firebaseAppDistribution.updateIfNewReleaseAvailable());
262+
TestUtils.awaitAsyncOperations(executorService);
263+
264+
assertAlertDialogShown();
265+
}
266+
250267
@Test
251268
public void updateToNewRelease_whenReleaseNotesEmpty_doesNotShowReleaseNotes() {
252269
when(mockNewReleaseFetcher.checkForNewRelease())
@@ -455,6 +472,20 @@ public void updateToNewRelease_receiveProgressUpdateFromUpdateApp() {
455472
assertEquals(UpdateStatus.DOWNLOADING, progressEvents.get(0).getUpdateStatus());
456473
}
457474

475+
@Test
476+
public void updateIfNewReleaseAvailable_fromABackgroundThread_showsSignInDialog()
477+
throws InterruptedException, ExecutionException {
478+
when(mockSignInStorage.getSignInStatus()).thenReturn(false);
479+
480+
ExecutorService executorService = Executors.newSingleThreadExecutor();
481+
Future<UpdateTask> future =
482+
executorService.submit(() -> firebaseAppDistribution.updateIfNewReleaseAvailable());
483+
TestUtils.awaitAsyncOperations(executorService);
484+
485+
assertAlertDialogShown();
486+
assertFalse(((UpdateTask) future.get()).isComplete());
487+
}
488+
458489
@Test
459490
public void updateIfNewReleaseAvailable_whenScreenRotates_signInConfirmationDialogReappears() {
460491
when(mockSignInStorage.getSignInStatus()).thenReturn(false);

0 commit comments

Comments
 (0)