diff --git a/firebase-app-distribution/api.txt b/firebase-app-distribution/api.txt index 53e5cb5b492..3f7070a08f8 100644 --- a/firebase-app-distribution/api.txt +++ b/firebase-app-distribution/api.txt @@ -34,6 +34,7 @@ package com.google.firebase.app.distribution { enum_constant public static final com.google.firebase.app.distribution.FirebaseAppDistributionException.Status AUTHENTICATION_CANCELED; enum_constant public static final com.google.firebase.app.distribution.FirebaseAppDistributionException.Status AUTHENTICATION_FAILURE; enum_constant public static final com.google.firebase.app.distribution.FirebaseAppDistributionException.Status DOWNLOAD_FAILURE; + enum_constant public static final com.google.firebase.app.distribution.FirebaseAppDistributionException.Status FOREGROUND_ACTIVITY_NOT_AVAILABLE; enum_constant public static final com.google.firebase.app.distribution.FirebaseAppDistributionException.Status INSTALLATION_CANCELED; enum_constant public static final com.google.firebase.app.distribution.FirebaseAppDistributionException.Status INSTALLATION_FAILURE; enum_constant public static final com.google.firebase.app.distribution.FirebaseAppDistributionException.Status INSTALLATION_FAILURE_SIGNATURE_MISMATCH; diff --git a/firebase-app-distribution/src/main/java/com/google/firebase/app/distribution/AabUpdater.java b/firebase-app-distribution/src/main/java/com/google/firebase/app/distribution/AabUpdater.java index 05a368b7b17..1e2637eb980 100644 --- a/firebase-app-distribution/src/main/java/com/google/firebase/app/distribution/AabUpdater.java +++ b/firebase-app-distribution/src/main/java/com/google/firebase/app/distribution/AabUpdater.java @@ -135,6 +135,7 @@ private static boolean isRedirectResponse(int responseCode) { private void redirectToPlayForAabUpdate(String downloadUrl) { synchronized (updateAabLock) { + // TODO(rachelprince): change this to getCurrentNonNullActivity if (lifecycleNotifier.getCurrentActivity() == null) { safeSetTaskException( cachedUpdateTask, diff --git a/firebase-app-distribution/src/main/java/com/google/firebase/app/distribution/FirebaseAppDistribution.java b/firebase-app-distribution/src/main/java/com/google/firebase/app/distribution/FirebaseAppDistribution.java index d3e3576295e..9cf3fb6fad7 100644 --- a/firebase-app-distribution/src/main/java/com/google/firebase/app/distribution/FirebaseAppDistribution.java +++ b/firebase-app-distribution/src/main/java/com/google/firebase/app/distribution/FirebaseAppDistribution.java @@ -14,6 +14,7 @@ package com.google.firebase.app.distribution; +import static com.google.firebase.app.distribution.FirebaseAppDistributionException.Status.AUTHENTICATION_CANCELED; import static com.google.firebase.app.distribution.FirebaseAppDistributionException.Status.AUTHENTICATION_FAILURE; import static com.google.firebase.app.distribution.FirebaseAppDistributionException.Status.UPDATE_NOT_AVAILABLE; import static com.google.firebase.app.distribution.TaskUtils.safeSetTaskException; @@ -27,6 +28,7 @@ import androidx.annotation.Nullable; import androidx.annotation.VisibleForTesting; import com.google.android.gms.tasks.Task; +import com.google.android.gms.tasks.TaskCompletionSource; import com.google.android.gms.tasks.Tasks; import com.google.firebase.FirebaseApp; import com.google.firebase.app.distribution.Constants.ErrorMessages; @@ -38,6 +40,7 @@ import com.google.firebase.installations.FirebaseInstallationsApi; public class FirebaseAppDistribution { + private static final int UNKNOWN_RELEASE_FILE_SIZE = -1; private final FirebaseApp firebaseApp; @@ -60,7 +63,7 @@ public class FirebaseAppDistribution { private Task cachedCheckForNewReleaseTask; private AlertDialog updateDialog; - private boolean updateDialogShown; + private AlertDialog signInDialog; /** Constructor for FirebaseAppDistribution */ @VisibleForTesting @@ -132,12 +135,30 @@ public UpdateTask updateIfNewReleaseAvailable() { if (cachedUpdateIfNewReleaseTask != null && !cachedUpdateIfNewReleaseTask.isComplete()) { return cachedUpdateIfNewReleaseTask; } - cachedUpdateIfNewReleaseTask = new UpdateTaskImpl(); } - checkForNewRelease() - .onSuccessTask( - release -> { + + showSignInDialog() + // TODO(rachelprince): Revisit this comment once changes to checkForNewRelease are reviewed + // Even though checkForNewRelease() calls signInTester(), we explicitly call signInTester + // here both for code clarifty, and because we plan to remove the signInTester() call + // from checkForNewRelease() in the near future + .onSuccessTask(unused -> signInTester()) + .onSuccessTask(unused -> checkForNewRelease()) + .continueWithTask( + task -> { + if (!task.isSuccessful()) { + postProgressToCachedUpdateIfNewReleaseTask( + UpdateProgress.builder() + .setApkBytesDownloaded(UNKNOWN_RELEASE_FILE_SIZE) + .setApkFileTotalBytes(UNKNOWN_RELEASE_FILE_SIZE) + .setUpdateStatus(UpdateStatus.NEW_RELEASE_CHECK_FAILED) + .build()); + } + // if the task failed, this get() will cause the error to propogate to the handler + // below + AppDistributionRelease release = task.getResult(); + if (release == null) { postProgressToCachedUpdateIfNewReleaseTask( UpdateProgress.builder() @@ -150,26 +171,59 @@ public UpdateTask updateIfNewReleaseAvailable() { } return showUpdateAlertDialog(release); }) - .addOnFailureListener( - e -> { - postProgressToCachedUpdateIfNewReleaseTask( - UpdateProgress.builder() - .setApkFileTotalBytes(UNKNOWN_RELEASE_FILE_SIZE) - .setApkBytesDownloaded(UNKNOWN_RELEASE_FILE_SIZE) - .setUpdateStatus(UpdateStatus.NEW_RELEASE_CHECK_FAILED) - .build()); - setCachedUpdateIfNewReleaseCompletionError( - e, - new FirebaseAppDistributionException( - Constants.ErrorMessages.NETWORK_ERROR, - FirebaseAppDistributionException.Status.NETWORK_FAILURE)); - }); + .onSuccessTask( + unused -> + updateApp(true) + .addOnProgressListener(this::postProgressToCachedUpdateIfNewReleaseTask)) + .addOnFailureListener(this::setCachedUpdateIfNewReleaseCompletionError); synchronized (updateIfNewReleaseTaskLock) { return cachedUpdateIfNewReleaseTask; } } + private Task showSignInDialog() { + if (isTesterSignedIn()) { + return Tasks.forResult(null); + } + + TaskCompletionSource showDialogTask = new TaskCompletionSource<>(); + + Activity currentActivity; + try { + currentActivity = lifecycleNotifier.getNonNullCurrentActivity(); + } catch (FirebaseAppDistributionException e) { + return Tasks.forException(e); + } + + signInDialog = new AlertDialog.Builder(currentActivity).create(); + Context context = firebaseApp.getApplicationContext(); + signInDialog.setTitle(context.getString(R.string.signin_dialog_title)); + signInDialog.setMessage(context.getString(R.string.singin_dialog_message)); + + signInDialog.setButton( + AlertDialog.BUTTON_POSITIVE, + context.getString(R.string.singin_yes_button), + (dialogInterface, i) -> showDialogTask.setResult(null)); + + signInDialog.setButton( + AlertDialog.BUTTON_NEGATIVE, + context.getString(R.string.singin_no_button), + (dialogInterface, i) -> + showDialogTask.setException( + new FirebaseAppDistributionException( + ErrorMessages.AUTHENTICATION_CANCELED, AUTHENTICATION_CANCELED))); + + signInDialog.setOnCancelListener( + dialogInterface -> + showDialogTask.setException( + new FirebaseAppDistributionException( + ErrorMessages.AUTHENTICATION_CANCELED, AUTHENTICATION_CANCELED))); + + signInDialog.show(); + return showDialogTask.getTask(); + } + /** Signs in the App Distribution tester. Presents the tester with a Google sign in UI */ @NonNull public Task signInTester() { @@ -276,7 +330,7 @@ void onActivityDestroyed(@NonNull Activity activity) { // SignInResult is internal to the SDK and is destroyed after creation return; } - if (updateDialogShown) { + if (updateDialog != null && updateDialog.isShowing()) { setCachedUpdateIfNewReleaseCompletionError( new FirebaseAppDistributionException( ErrorMessages.UPDATE_CANCELED, Status.INSTALLATION_CANCELED)); @@ -297,18 +351,18 @@ AppDistributionReleaseInternal getCachedNewRelease() { } } - private UpdateTaskImpl showUpdateAlertDialog(AppDistributionRelease newRelease) { - Context context = firebaseApp.getApplicationContext(); - Activity currentActivity = lifecycleNotifier.getCurrentActivity(); - if (currentActivity == null) { - LogWrapper.getInstance().e("No foreground activity found."); - UpdateTaskImpl updateTask = new UpdateTaskImpl(); - updateTask.setException( - new FirebaseAppDistributionException( - ErrorMessages.APP_BACKGROUNDED, - FirebaseAppDistributionException.Status.UPDATE_NOT_AVAILABLE)); - return updateTask; + private Task showUpdateAlertDialog(AppDistributionRelease newRelease) { + TaskCompletionSource showUpdateDialogTask = new TaskCompletionSource<>(); + + Activity currentActivity; + try { + currentActivity = lifecycleNotifier.getNonNullCurrentActivity(); + } catch (FirebaseAppDistributionException e) { + return Tasks.forException(e); } + + Context context = firebaseApp.getApplicationContext(); + updateDialog = new AlertDialog.Builder(currentActivity).create(); updateDialog.setTitle(context.getString(R.string.update_dialog_title)); @@ -321,71 +375,44 @@ private UpdateTaskImpl showUpdateAlertDialog(AppDistributionRelease newRelease) if (newRelease.getReleaseNotes() != null && !newRelease.getReleaseNotes().isEmpty()) { message.append(String.format("\n\nRelease notes: %s", newRelease.getReleaseNotes())); } - updateDialog.setMessage(message); + updateDialog.setButton( AlertDialog.BUTTON_POSITIVE, context.getString(R.string.update_yes_button), - (dialogInterface, i) -> { - synchronized (updateIfNewReleaseTaskLock) { - // show download progress in notification manager - updateApp(true) - .addOnProgressListener(this::postProgressToCachedUpdateIfNewReleaseTask) - .addOnSuccessListener(unused -> setCachedUpdateIfNewReleaseResult()) - .addOnFailureListener(cachedUpdateIfNewReleaseTask::setException); - } - }); + (dialogInterface, i) -> showUpdateDialogTask.setResult(null)); updateDialog.setButton( AlertDialog.BUTTON_NEGATIVE, context.getString(R.string.update_no_button), - (dialogInterface, i) -> dismissUpdateDialogCallback()); + (dialogInterface, i) -> + showUpdateDialogTask.setException( + new FirebaseAppDistributionException( + ErrorMessages.UPDATE_CANCELED, Status.INSTALLATION_CANCELED))); updateDialog.setOnCancelListener( - dialogInterface -> { - dismissUpdateDialogCallback(); - }); + dialogInterface -> + showUpdateDialogTask.setException( + new FirebaseAppDistributionException( + ErrorMessages.UPDATE_CANCELED, Status.INSTALLATION_CANCELED))); updateDialog.show(); - updateDialogShown = true; - synchronized (updateIfNewReleaseTaskLock) { - return cachedUpdateIfNewReleaseTask; - } - } - private void dismissUpdateDialogCallback() { - synchronized (updateIfNewReleaseTaskLock) { - postProgressToCachedUpdateIfNewReleaseTask( - UpdateProgress.builder() - .setApkFileTotalBytes(UNKNOWN_RELEASE_FILE_SIZE) - .setApkBytesDownloaded(UNKNOWN_RELEASE_FILE_SIZE) - .setUpdateStatus(UpdateStatus.UPDATE_CANCELED) - .build()); - setCachedUpdateIfNewReleaseCompletionError( - new FirebaseAppDistributionException( - ErrorMessages.UPDATE_CANCELED, Status.INSTALLATION_CANCELED)); - } + return showUpdateDialogTask.getTask(); } - private void setCachedUpdateIfNewReleaseCompletionError(FirebaseAppDistributionException e) { + private void setCachedUpdateIfNewReleaseCompletionError(Exception e) { synchronized (updateIfNewReleaseTaskLock) { safeSetTaskException(cachedUpdateIfNewReleaseTask, e); } - dismissUpdateDialog(); - } - - private void setCachedUpdateIfNewReleaseCompletionError( - Exception e, FirebaseAppDistributionException defaultFirebaseException) { - if (e instanceof FirebaseAppDistributionException) { - setCachedUpdateIfNewReleaseCompletionError((FirebaseAppDistributionException) e); - } else { - setCachedUpdateIfNewReleaseCompletionError(defaultFirebaseException); - } + dismissDialogs(); } private void postProgressToCachedUpdateIfNewReleaseTask(UpdateProgress progress) { synchronized (updateIfNewReleaseTaskLock) { - cachedUpdateIfNewReleaseTask.updateProgress(progress); + if (cachedUpdateIfNewReleaseTask != null && !cachedUpdateIfNewReleaseTask.isComplete()) { + cachedUpdateIfNewReleaseTask.updateProgress(progress); + } } } @@ -393,17 +420,19 @@ private void setCachedUpdateIfNewReleaseResult() { synchronized (updateIfNewReleaseTaskLock) { safeSetTaskResult(cachedUpdateIfNewReleaseTask); } - dismissUpdateDialog(); + dismissDialogs(); } - private void dismissUpdateDialog() { - if (updateDialog != null) { + private void dismissDialogs() { + if (signInDialog != null && signInDialog.isShowing()) { + signInDialog.dismiss(); + } + if (updateDialog != null && updateDialog.isShowing()) { updateDialog.dismiss(); - updateDialogShown = false; } } - private UpdateTask getErrorUpdateTask(Exception e) { + private UpdateTaskImpl getErrorUpdateTask(Exception e) { UpdateTaskImpl updateTask = new UpdateTaskImpl(); updateTask.setException(e); return updateTask; diff --git a/firebase-app-distribution/src/main/java/com/google/firebase/app/distribution/FirebaseAppDistributionException.java b/firebase-app-distribution/src/main/java/com/google/firebase/app/distribution/FirebaseAppDistributionException.java index c34d335e17d..0bb9f14fbac 100644 --- a/firebase-app-distribution/src/main/java/com/google/firebase/app/distribution/FirebaseAppDistributionException.java +++ b/firebase-app-distribution/src/main/java/com/google/firebase/app/distribution/FirebaseAppDistributionException.java @@ -42,6 +42,10 @@ public enum Status { /** Installation canceled */ INSTALLATION_CANCELED, + /** No foreground activity available for a given intent */ + // TODO(rachelprince): add this to API council review + FOREGROUND_ACTIVITY_NOT_AVAILABLE, + /** Update not available for the current tester and app */ UPDATE_NOT_AVAILABLE, diff --git a/firebase-app-distribution/src/main/java/com/google/firebase/app/distribution/FirebaseAppDistributionLifecycleNotifier.java b/firebase-app-distribution/src/main/java/com/google/firebase/app/distribution/FirebaseAppDistributionLifecycleNotifier.java index 36c8d20def1..b725e143c70 100644 --- a/firebase-app-distribution/src/main/java/com/google/firebase/app/distribution/FirebaseAppDistributionLifecycleNotifier.java +++ b/firebase-app-distribution/src/main/java/com/google/firebase/app/distribution/FirebaseAppDistributionLifecycleNotifier.java @@ -20,6 +20,8 @@ import androidx.annotation.GuardedBy; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import com.google.firebase.app.distribution.Constants.ErrorMessages; +import com.google.firebase.app.distribution.FirebaseAppDistributionException.Status; import java.util.ArrayDeque; import java.util.Queue; @@ -78,6 +80,16 @@ Activity getCurrentActivity() { } } + Activity getNonNullCurrentActivity() throws FirebaseAppDistributionException { + synchronized (lock) { + if (currentActivity == null) { + throw new FirebaseAppDistributionException( + ErrorMessages.APP_BACKGROUNDED, Status.FOREGROUND_ACTIVITY_NOT_AVAILABLE); + } + return currentActivity; + } + } + void addOnActivityCreatedListener(@NonNull OnActivityCreatedListener listener) { synchronized (lock) { this.onActivityCreatedListeners.add(listener); diff --git a/firebase-app-distribution/src/main/java/com/google/firebase/app/distribution/TesterSignInManager.java b/firebase-app-distribution/src/main/java/com/google/firebase/app/distribution/TesterSignInManager.java index 8f41d63b904..cd2fc2367e7 100644 --- a/firebase-app-distribution/src/main/java/com/google/firebase/app/distribution/TesterSignInManager.java +++ b/firebase-app-distribution/src/main/java/com/google/firebase/app/distribution/TesterSignInManager.java @@ -20,7 +20,6 @@ import static com.google.firebase.app.distribution.TaskUtils.safeSetTaskResult; import android.app.Activity; -import android.app.AlertDialog; import android.content.Context; import android.content.Intent; import android.content.pm.ResolveInfo; @@ -59,8 +58,6 @@ class TesterSignInManager { @GuardedBy("signInTaskLock") private TaskCompletionSource signInTaskCompletionSource = null; - private AlertDialog alertDialog; - TesterSignInManager( @NonNull FirebaseApp firebaseApp, @NonNull Provider firebaseInstallationsApiProvider, @@ -85,7 +82,6 @@ class TesterSignInManager { lifecycleNotifier.addOnActivityCreatedListener(this::onActivityCreated); lifecycleNotifier.addOnActivityStartedListener(this::onActivityStarted); - lifecycleNotifier.addOnActivityDestroyedListener(this::onActivityDestroyed); } @VisibleForTesting @@ -115,10 +111,6 @@ void onActivityStarted(Activity activity) { } } - private void onActivityDestroyed(Activity activity) { - this.dismissAlertDialog(); - } - @NonNull public Task signInTester() { @@ -133,6 +125,8 @@ public Task signInTester() { .v(TAG + "Detected In-Progress sign in task. Returning the same task."); return signInTaskCompletionSource.getTask(); } + + // TODO(rachelprince): Change this to getCurrentNonNullActivity Activity currentActivity = lifecycleNotifier.getCurrentActivity(); if (currentActivity == null) { LogWrapper.getInstance().e(TAG + "No foreground activity found."); @@ -144,8 +138,17 @@ public Task signInTester() { signInTaskCompletionSource = new TaskCompletionSource<>(); - alertDialog = getSignInAlertDialog(currentActivity); - alertDialog.show(); + firebaseInstallationsApiProvider + .get() + .getId() + .addOnSuccessListener(getFidGenerationOnSuccessListener(currentActivity)) + .addOnFailureListener( + e -> { + LogWrapper.getInstance().e(TAG + "Fid retrieval failed.", e); + setSignInTaskCompletionError( + new FirebaseAppDistributionException( + Constants.ErrorMessages.AUTHENTICATION_ERROR, AUTHENTICATION_FAILURE, e)); + }); return signInTaskCompletionSource.getTask(); } @@ -158,52 +161,10 @@ private boolean isCurrentlySigningIn() { } } - private AlertDialog getSignInAlertDialog(Activity currentActivity) { - alertDialog = new AlertDialog.Builder(currentActivity).create(); - Context context = firebaseApp.getApplicationContext(); - alertDialog.setTitle(context.getString(R.string.signin_dialog_title)); - alertDialog.setMessage(context.getString(R.string.singin_dialog_message)); - alertDialog.setButton( - AlertDialog.BUTTON_POSITIVE, - context.getString(R.string.singin_yes_button), - (dialogInterface, i) -> { - firebaseInstallationsApiProvider - .get() - .getId() - .addOnSuccessListener(getFidGenerationOnSuccessListener(currentActivity)) - .addOnFailureListener( - e -> { - LogWrapper.getInstance().e(TAG + "Fid retrieval failed.", e); - setSignInTaskCompletionError( - new FirebaseAppDistributionException( - Constants.ErrorMessages.AUTHENTICATION_ERROR, - AUTHENTICATION_FAILURE, - e)); - }); - }); - - alertDialog.setButton( - AlertDialog.BUTTON_NEGATIVE, - context.getString(R.string.singin_no_button), - (dialogInterface, i) -> dismissSignInDialogCallback()); - - alertDialog.setOnCancelListener(dialogInterface -> dismissSignInDialogCallback()); - - return alertDialog; - } - - private void dismissSignInDialogCallback() { - LogWrapper.getInstance().v("Sign in has been canceled."); - setSignInTaskCompletionError( - new FirebaseAppDistributionException( - ErrorMessages.AUTHENTICATION_CANCELED, AUTHENTICATION_CANCELED)); - } - private void setSignInTaskCompletionError(FirebaseAppDistributionException e) { synchronized (signInTaskLock) { safeSetTaskException(signInTaskCompletionSource, e); } - dismissAlertDialog(); } private void setCanceledAuthenticationError() { @@ -216,13 +177,6 @@ private void setSuccessfulSignInResult() { synchronized (signInTaskLock) { safeSetTaskResult(signInTaskCompletionSource, null); } - dismissAlertDialog(); - } - - private void dismissAlertDialog() { - if (alertDialog != null && alertDialog.isShowing()) { - alertDialog.dismiss(); - } } private OnSuccessListener getFidGenerationOnSuccessListener(Activity currentActivity) { diff --git a/firebase-app-distribution/src/test/java/com/google/firebase/app/distribution/FirebaseAppDistributionTest.java b/firebase-app-distribution/src/test/java/com/google/firebase/app/distribution/FirebaseAppDistributionTest.java index 40e20f82c6c..5745219f26c 100644 --- a/firebase-app-distribution/src/test/java/com/google/firebase/app/distribution/FirebaseAppDistributionTest.java +++ b/firebase-app-distribution/src/test/java/com/google/firebase/app/distribution/FirebaseAppDistributionTest.java @@ -33,6 +33,7 @@ import android.app.Activity; import android.app.AlertDialog; import android.app.Dialog; +import android.content.DialogInterface; import android.content.pm.ApplicationInfo; import android.content.pm.PackageInfo; import android.os.Bundle; @@ -108,7 +109,7 @@ public class FirebaseAppDistributionTest { static class TestActivity extends Activity {} @Before - public void setup() { + public void setup() throws FirebaseAppDistributionException { MockitoAnnotations.initMocks(this); @@ -156,7 +157,7 @@ public void setup() { shadowPackageManager.installPackage(packageInfo); activity = Robolectric.buildActivity(TestActivity.class).create().get(); - when(mockLifecycleNotifier.getCurrentActivity()).thenReturn(activity); + when(mockLifecycleNotifier.getNonNullCurrentActivity()).thenReturn(activity); when(mockSignInStorage.getSignInStatus()).thenReturn(true); } @@ -258,7 +259,6 @@ public void updateToNewRelease_whenReleaseNotesEmpty_doesNotShowReleaseNotes() { @Test public void updateToNewRelease_whenNoReleaseAvailable_updateDialogNotShown() { - when(mockSignInStorage.getSignInStatus()).thenReturn(false); when(mockNewReleaseFetcher.checkForNewRelease()).thenReturn(Tasks.forResult(null)); UpdateTask task = firebaseAppDistribution.updateIfNewReleaseAvailable(); @@ -273,7 +273,6 @@ public void updateToNewRelease_whenNoReleaseAvailable_updateDialogNotShown() { @Test public void updateToNewRelease_whenActivityBackgrounded_updateDialogNotShown() { - when(mockSignInStorage.getSignInStatus()).thenReturn(false); when(mockNewReleaseFetcher.checkForNewRelease()).thenReturn(Tasks.forResult(null)); when(mockLifecycleNotifier.getCurrentActivity()).thenReturn(null); @@ -298,6 +297,9 @@ public void updateToNewRelease_whenSignInCancelled_checkForUpdateNotCalled() { UpdateTask task = firebaseAppDistribution.updateIfNewReleaseAvailable(); + AlertDialog signInDialog = verifySignInAlertDialog(); + signInDialog.getButton(DialogInterface.BUTTON_POSITIVE).performClick(); + verify(mockTesterSignInManager, times(1)).signInTester(); verify(mockNewReleaseFetcher, never()).checkForNewRelease(); assertTrue(task.getException() instanceof FirebaseAppDistributionException); @@ -316,11 +318,10 @@ public void updateToNewRelease_whenSignInFailed_checkForUpdateNotCalled() { ErrorMessages.AUTHENTICATION_ERROR, AUTHENTICATION_FAILURE))); UpdateTask task = firebaseAppDistribution.updateIfNewReleaseAvailable(); - List progressEvents = new ArrayList<>(); - task.addOnProgressListener(progressEvents::add); - assertEquals(1, progressEvents.size()); - assertEquals(UpdateStatus.NEW_RELEASE_CHECK_FAILED, progressEvents.get(0).getUpdateStatus()); + AlertDialog signInDialog = verifySignInAlertDialog(); + signInDialog.getButton(DialogInterface.BUTTON_POSITIVE).performClick(); + verify(mockNewReleaseFetcher, never()).checkForNewRelease(); assertTrue(task.getException() instanceof FirebaseAppDistributionException); FirebaseAppDistributionException e = (FirebaseAppDistributionException) task.getException(); @@ -385,6 +386,53 @@ public void updateToNewRelease_whenCheckForUpdateFails_updateAppNotCalled() { assertEquals(FirebaseAppDistributionException.Status.NETWORK_FAILURE, e.getErrorCode()); } + @Test + public void updateToNewRelease_whenTesterIsSignedIn_doesNotOpenDialog() { + when(mockSignInStorage.getSignInStatus()).thenReturn(true); + + firebaseAppDistribution.updateIfNewReleaseAvailable(); + + assertNull(ShadowAlertDialog.getLatestAlertDialog()); + } + + @Test + public void signInTester_whenDialogDismissed_taskFails() { + when(mockSignInStorage.getSignInStatus()).thenReturn(false); + Task updateTask = firebaseAppDistribution.updateIfNewReleaseAvailable(); + + AlertDialog dialog = verifySignInAlertDialog(); + dialog.getButton(AlertDialog.BUTTON_NEGATIVE).performClick(); // dismiss dialog + + assertFalse(updateTask.isSuccessful()); + Exception e = updateTask.getException(); + assertTrue(e instanceof FirebaseAppDistributionException); + assertEquals(AUTHENTICATION_CANCELED, ((FirebaseAppDistributionException) e).getErrorCode()); + assertEquals(ErrorMessages.AUTHENTICATION_CANCELED, e.getMessage()); + } + + @Test + public void updateToNewRelease_whenSignInDialogCanceled_taskFails() { + when(mockSignInStorage.getSignInStatus()).thenReturn(false); + Task signInTask = firebaseAppDistribution.updateIfNewReleaseAvailable(); + + AlertDialog dialog = verifySignInAlertDialog(); + dialog.onBackPressed(); // cancel dialog + + assertFalse(signInTask.isSuccessful()); + Exception e = signInTask.getException(); + assertTrue(e instanceof FirebaseAppDistributionException); + assertEquals(AUTHENTICATION_CANCELED, ((FirebaseAppDistributionException) e).getErrorCode()); + assertEquals(ErrorMessages.AUTHENTICATION_CANCELED, e.getMessage()); + } + + private AlertDialog verifySignInAlertDialog() { + assertTrue(ShadowAlertDialog.getLatestDialog() instanceof AlertDialog); + AlertDialog dialog = (AlertDialog) ShadowAlertDialog.getLatestDialog(); + assertTrue(dialog.isShowing()); + + return dialog; + } + @Test public void signOutTester_setsSignInStatusFalse() { firebaseAppDistribution.signOutTester(); diff --git a/firebase-app-distribution/src/test/java/com/google/firebase/app/distribution/TesterSignInManagerTest.java b/firebase-app-distribution/src/test/java/com/google/firebase/app/distribution/TesterSignInManagerTest.java index 4337cafa1f2..9b1c95f8f2b 100644 --- a/firebase-app-distribution/src/test/java/com/google/firebase/app/distribution/TesterSignInManagerTest.java +++ b/firebase-app-distribution/src/test/java/com/google/firebase/app/distribution/TesterSignInManagerTest.java @@ -18,14 +18,12 @@ import static com.google.firebase.app.distribution.FirebaseAppDistributionException.Status.AUTHENTICATION_CANCELED; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import static org.robolectric.Shadows.shadowOf; -import android.app.AlertDialog; import android.content.Intent; import android.content.pm.ApplicationInfo; import android.content.pm.PackageInfo; @@ -54,7 +52,6 @@ import org.robolectric.Robolectric; import org.robolectric.RobolectricTestRunner; import org.robolectric.shadows.ShadowActivity; -import org.robolectric.shadows.ShadowAlertDialog; import org.robolectric.shadows.ShadowPackageManager; @RunWith(RobolectricTestRunner.class) @@ -139,7 +136,7 @@ public void setUp() { } @Test - public void signInTester_whenDialogConfirmedAndChromeAvailable_opensCustomTab() { + public void signInTester_whenChromeAvailable_opensCustomTab() { when(mockSignInStorage.getSignInStatus()).thenReturn(false); ResolveInfo resolveInfo = new ResolveInfo(); resolveInfo.resolvePackageName = "garbage"; @@ -149,15 +146,12 @@ public void signInTester_whenDialogConfirmedAndChromeAvailable_opensCustomTab() testerSignInManager.signInTester(); - AlertDialog dialog = verifySignInAlertDialog(); - dialog.getButton(AlertDialog.BUTTON_POSITIVE).performClick(); - verify(mockFirebaseInstallations, times(1)).getId(); assertThat(shadowActivity.getNextStartedActivity().getData()).isEqualTo(Uri.parse(TEST_URL)); } @Test - public void signInTester_whenDialogConfirmedAndChromeNotAvailable_opensBrowserIntent() { + public void signInTester_whenChromeNotAvailable_opensBrowserIntent() { when(mockSignInStorage.getSignInStatus()).thenReturn(false); ResolveInfo resolveInfo = new ResolveInfo(); resolveInfo.resolvePackageName = "garbage"; @@ -166,9 +160,6 @@ public void signInTester_whenDialogConfirmedAndChromeNotAvailable_opensBrowserIn testerSignInManager.signInTester(); - AlertDialog dialog = verifySignInAlertDialog(); - dialog.getButton(AlertDialog.BUTTON_POSITIVE).performClick(); - verify(mockFirebaseInstallations, times(1)).getId(); assertThat(shadowActivity.getNextStartedActivity().getData()).isEqualTo(Uri.parse(TEST_URL)); } @@ -181,20 +172,9 @@ public void signInTester_whenSignInCalledMultipleTimes_returnsSameTask() { assertEquals(signInTask1, signInTask2); } - @Test - public void signInTester_whenTesterIsSignedIn_doesNotOpenDialog() { - when(mockSignInStorage.getSignInStatus()).thenReturn(true); - - testerSignInManager.signInTester(); - - assertNull(ShadowAlertDialog.getLatestAlertDialog()); - } - @Test public void signInTester_whenReturnFromSignIn_taskSucceeds() { Task signInTask = testerSignInManager.signInTester(); - AlertDialog dialog = verifySignInAlertDialog(); - dialog.getButton(AlertDialog.BUTTON_POSITIVE).performClick(); // Simulate re-entering app testerSignInManager.onActivityCreated(mockSignInResultActivity); @@ -216,40 +196,4 @@ public void signInTester_whenAppReenteredDuringSignIn_taskFails() { assertEquals(AUTHENTICATION_CANCELED, ((FirebaseAppDistributionException) e).getErrorCode()); assertEquals(ErrorMessages.AUTHENTICATION_CANCELED, e.getMessage()); } - - @Test - public void signInTester_whenDialogDismissed_taskFails() { - Task signInTask = testerSignInManager.signInTester(); - - AlertDialog dialog = verifySignInAlertDialog(); - dialog.getButton(AlertDialog.BUTTON_NEGATIVE).performClick(); // dismiss dialog - - assertFalse(signInTask.isSuccessful()); - Exception e = signInTask.getException(); - assertTrue(e instanceof FirebaseAppDistributionException); - assertEquals(AUTHENTICATION_CANCELED, ((FirebaseAppDistributionException) e).getErrorCode()); - assertEquals(ErrorMessages.AUTHENTICATION_CANCELED, e.getMessage()); - } - - @Test - public void signInTester_whenDialogCanceled_taskFails() { - Task signInTask = testerSignInManager.signInTester(); - - AlertDialog dialog = verifySignInAlertDialog(); - dialog.onBackPressed(); // cancel dialog - - assertFalse(signInTask.isSuccessful()); - Exception e = signInTask.getException(); - assertTrue(e instanceof FirebaseAppDistributionException); - assertEquals(AUTHENTICATION_CANCELED, ((FirebaseAppDistributionException) e).getErrorCode()); - assertEquals(ErrorMessages.AUTHENTICATION_CANCELED, e.getMessage()); - } - - private AlertDialog verifySignInAlertDialog() { - assertTrue(ShadowAlertDialog.getLatestDialog() instanceof AlertDialog); - AlertDialog dialog = (AlertDialog) ShadowAlertDialog.getLatestDialog(); - assertTrue(dialog.isShowing()); - - return dialog; - } }