2121import android .app .Application ;
2222import android .content .Context ;
2323import android .os .Bundle ;
24+ import androidx .annotation .GuardedBy ;
2425import androidx .annotation .NonNull ;
2526import androidx .annotation .VisibleForTesting ;
2627import com .google .android .gms .common .internal .Preconditions ;
2728import com .google .android .gms .tasks .Task ;
28- import com .google .android .gms .tasks .TaskCompletionSource ;
2929import com .google .android .gms .tasks .Tasks ;
3030import com .google .firebase .FirebaseApp ;
3131import com .google .firebase .appdistribution .internal .AppDistributionReleaseInternal ;
@@ -39,10 +39,15 @@ public class FirebaseAppDistribution implements Application.ActivityLifecycleCal
3939 private final CheckForUpdateClient checkForUpdateClient ;
4040 private final UpdateAppClient updateAppClient ;
4141 private Activity currentActivity ;
42+ private static final int UNKNOWN_RELEASE_FILE_SIZE = -1 ;
4243
43- private Task <Void > cachedUpdateToLatestReleaseTask ;
44+ @ GuardedBy ("updateTaskLock" )
45+ private UpdateTaskImpl cachedUpdateIfNewReleaseTask ;
46+
47+ private final Object updateTaskLock = new Object ();
4448 private Task <AppDistributionRelease > cachedCheckForUpdateTask ;
45- private AppDistributionReleaseInternal cachedLatestRelease ;
49+
50+ private AppDistributionReleaseInternal cachedNewRelease ;
4651 private final SignInStorage signInStorage ;
4752
4853 /** Constructor for FirebaseAppDistribution */
@@ -103,30 +108,54 @@ public static FirebaseAppDistribution getInstance(@NonNull FirebaseApp app) {
103108 }
104109
105110 /**
106- * Updates the app to the latest release, if one is available. Returns the release information or
111+ * Updates the app to the newest release, if one is available. Returns the release information or
107112 * null if no update is found. Performs the following actions: 1. If tester is not signed in,
108113 * presents the tester with a Google sign in UI 2. Checks if a newer release is available. If so,
109114 * presents the tester with a confirmation dialog to begin the download. 3. For APKs, downloads
110115 * the binary and starts an installation intent. 4. For AABs, directs the tester to the Play app
111116 * to complete the download and installation.
112117 */
113118 @ NonNull
114- public synchronized Task <Void > updateToLatestRelease () {
115- if (cachedUpdateToLatestReleaseTask != null && !cachedUpdateToLatestReleaseTask .isComplete ()) {
116- return cachedUpdateToLatestReleaseTask ;
117- }
118-
119- cachedUpdateToLatestReleaseTask =
120- checkForUpdate ()
121- .onSuccessTask (
122- release -> {
123- if (release == null ) {
124- return Tasks .forResult (null );
125- }
126- return showUpdateAlertDialog (release );
127- });
119+ public synchronized UpdateTask updateIfNewReleaseAvailable () {
120+ synchronized (updateTaskLock ) {
121+ if (cachedUpdateIfNewReleaseTask != null && !cachedUpdateIfNewReleaseTask .isComplete ()) {
122+ return cachedUpdateIfNewReleaseTask ;
123+ }
128124
129- return cachedUpdateToLatestReleaseTask ;
125+ cachedUpdateIfNewReleaseTask = new UpdateTaskImpl ();
126+ }
127+ checkForNewRelease ()
128+ .onSuccessTask (
129+ release -> {
130+ if (release == null ) {
131+ postProgressToCachedUpdateIfNewReleaseTask (
132+ UpdateProgress .builder ()
133+ .setApkFileTotalBytes (UNKNOWN_RELEASE_FILE_SIZE )
134+ .setApkBytesDownloaded (UNKNOWN_RELEASE_FILE_SIZE )
135+ .setUpdateStatus (UpdateStatus .NEW_RELEASE_NOT_AVAILABLE )
136+ .build ());
137+ setCachedUpdateIfNewReleaseResult ();
138+ return Tasks .forResult (null );
139+ }
140+ return showUpdateAlertDialog (release );
141+ })
142+ .addOnFailureListener (
143+ e -> {
144+ postProgressToCachedUpdateIfNewReleaseTask (
145+ UpdateProgress .builder ()
146+ .setApkFileTotalBytes (UNKNOWN_RELEASE_FILE_SIZE )
147+ .setApkBytesDownloaded (UNKNOWN_RELEASE_FILE_SIZE )
148+ .setUpdateStatus (UpdateStatus .NEW_RELEASE_CHECK_FAILED )
149+ .build ());
150+ setCachedUpdateIfNewReleaseCompletionError (
151+ e ,
152+ new FirebaseAppDistributionException (
153+ Constants .ErrorMessages .NETWORK_ERROR ,
154+ FirebaseAppDistributionException .Status .NETWORK_FAILURE ));
155+ });
156+ synchronized (updateTaskLock ) {
157+ return cachedUpdateIfNewReleaseTask ;
158+ }
130159 }
131160
132161 /** Signs in the App Distribution tester. Presents the tester with a Google sign in UI */
@@ -141,18 +170,17 @@ public Task<Void> signInTester() {
141170 * sign in UI
142171 */
143172 @ NonNull
144- public synchronized Task <AppDistributionRelease > checkForUpdate () {
173+ public synchronized Task <AppDistributionRelease > checkForNewRelease () {
145174 if (cachedCheckForUpdateTask != null && !cachedCheckForUpdateTask .isComplete ()) {
146175 LogWrapper .getInstance ().v ("Response in progress" );
147176 return cachedCheckForUpdateTask ;
148177 }
149-
150178 cachedCheckForUpdateTask =
151179 signInTester ()
152180 .onSuccessTask (unused -> this .checkForUpdateClient .checkForUpdate ())
153181 .onSuccessTask (
154182 appDistributionReleaseInternal -> {
155- setCachedLatestRelease (appDistributionReleaseInternal );
183+ setCachedNewRelease (appDistributionReleaseInternal );
156184 return Tasks .forResult (
157185 ReleaseUtils .convertToAppDistributionRelease (appDistributionReleaseInternal ));
158186 });
@@ -161,11 +189,11 @@ public synchronized Task<AppDistributionRelease> checkForUpdate() {
161189 }
162190
163191 /**
164- * Updates app to the latest release. If the latest release is an APK, downloads the binary and
165- * starts an installation If the latest release is an AAB, directs the tester to the Play app to
192+ * Updates app to the newest release. If the newest release is an APK, downloads the binary and
193+ * starts an installation If the newest release is an AAB, directs the tester to the Play app to
166194 * complete the download and installation.
167195 *
168- * <p>cancels task with FirebaseAppDistributionException with UPDATE_NOT_AVAIALBLE exception if no
196+ * <p>cancels task with FirebaseAppDistributionException with UPDATE_NOT_AVAILABLE exception if no
169197 * new release is cached from checkForUpdate
170198 */
171199 @ NonNull
@@ -186,7 +214,7 @@ private synchronized UpdateTask updateApp(boolean showDownloadInNotificationMana
186214 return updateTask ;
187215 }
188216
189- return this .updateAppClient .updateApp (cachedLatestRelease , showDownloadInNotificationManager );
217+ return this .updateAppClient .updateApp (cachedNewRelease , showDownloadInNotificationManager );
190218 }
191219
192220 /** Returns true if the App Distribution tester is signed in */
@@ -196,7 +224,7 @@ public boolean isTesterSignedIn() {
196224
197225 /** Signs out the App Distribution tester */
198226 public void signOutTester () {
199- this .cachedLatestRelease = null ;
227+ this .cachedNewRelease = null ;
200228 this .signInStorage .setSignInStatus (false );
201229 }
202230
@@ -266,17 +294,16 @@ public void onActivityDestroyed(@NonNull Activity activity) {
266294 }
267295
268296 @ VisibleForTesting
269- void setCachedLatestRelease (AppDistributionReleaseInternal latestRelease ) {
270- this .cachedLatestRelease = latestRelease ;
297+ void setCachedNewRelease (AppDistributionReleaseInternal newRelease ) {
298+ this .cachedNewRelease = newRelease ;
271299 }
272300
273301 @ VisibleForTesting
274- AppDistributionReleaseInternal getCachedLatestRelease () {
275- return this .cachedLatestRelease ;
302+ AppDistributionReleaseInternal getCachedNewRelease () {
303+ return this .cachedNewRelease ;
276304 }
277305
278- private Task <Void > showUpdateAlertDialog (AppDistributionRelease latestRelease ) {
279- TaskCompletionSource <Void > updateAlertDialogTask = new TaskCompletionSource <>();
306+ private UpdateTaskImpl showUpdateAlertDialog (AppDistributionRelease newRelease ) {
280307 Context context = firebaseApp .getApplicationContext ();
281308 AlertDialog alertDialog = new AlertDialog .Builder (currentActivity ).create ();
282309 alertDialog .setTitle (context .getString (R .string .update_dialog_title ));
@@ -285,38 +312,82 @@ private Task<Void> showUpdateAlertDialog(AppDistributionRelease latestRelease) {
285312 new StringBuilder (
286313 String .format (
287314 "Version %s (%s) is available." ,
288- latestRelease .getDisplayVersion (), latestRelease .getVersionCode ()));
315+ newRelease .getDisplayVersion (), newRelease .getVersionCode ()));
289316
290- if (latestRelease .getReleaseNotes () != null && !latestRelease .getReleaseNotes ().isEmpty ()) {
291- message .append (String .format ("\n \n Release notes: %s" , latestRelease .getReleaseNotes ()));
317+ if (newRelease .getReleaseNotes () != null && !newRelease .getReleaseNotes ().isEmpty ()) {
318+ message .append (String .format ("\n \n Release notes: %s" , newRelease .getReleaseNotes ()));
292319 }
293320
294321 alertDialog .setMessage (message );
295322 alertDialog .setButton (
296323 AlertDialog .BUTTON_POSITIVE ,
297324 context .getString (R .string .update_yes_button ),
298- (dialogInterface , i ) ->
325+ (dialogInterface , i ) -> {
326+ synchronized (updateTaskLock ) {
299327 // show download progress in notification manager
300328 updateApp (true )
301- .addOnSuccessListener (unused -> updateAlertDialogTask .setResult (null ))
302- .addOnFailureListener (updateAlertDialogTask ::setException ));
329+ .addOnProgressListener (
330+ progress -> postProgressToCachedUpdateIfNewReleaseTask (progress ))
331+ .addOnSuccessListener (unused -> setCachedUpdateIfNewReleaseResult ())
332+ .addOnFailureListener (cachedUpdateIfNewReleaseTask ::setException );
333+ }
334+ });
303335
304336 alertDialog .setButton (
305337 AlertDialog .BUTTON_NEGATIVE ,
306338 context .getString (R .string .update_no_button ),
307339 (dialogInterface , i ) -> {
308340 dialogInterface .dismiss ();
309- updateAlertDialogTask .setException (
310- new FirebaseAppDistributionException (
311- Constants .ErrorMessages .UPDATE_CANCELED ,
312- FirebaseAppDistributionException .Status .INSTALLATION_CANCELED ));
341+ synchronized (updateTaskLock ) {
342+ postProgressToCachedUpdateIfNewReleaseTask (
343+ UpdateProgress .builder ()
344+ .setApkFileTotalBytes (UNKNOWN_RELEASE_FILE_SIZE )
345+ .setApkBytesDownloaded (UNKNOWN_RELEASE_FILE_SIZE )
346+ .setUpdateStatus (UpdateStatus .UPDATE_CANCELED )
347+ .build ());
348+ setCachedUpdateIfNewReleaseCompletionError (
349+ new FirebaseAppDistributionException (
350+ Constants .ErrorMessages .UPDATE_CANCELED ,
351+ FirebaseAppDistributionException .Status .INSTALLATION_CANCELED ));
352+ }
313353 });
314354
315355 alertDialog .show ();
316- return updateAlertDialogTask .getTask ();
356+ synchronized (updateTaskLock ) {
357+ return cachedUpdateIfNewReleaseTask ;
358+ }
317359 }
318360
319361 void setInstallationResult (int resultCode ) {
320362 this .updateAppClient .setInstallationResult (resultCode );
321363 }
364+
365+ private void setCachedUpdateIfNewReleaseCompletionError (FirebaseAppDistributionException e ) {
366+ synchronized (updateTaskLock ) {
367+ if (cachedUpdateIfNewReleaseTask != null && !cachedUpdateIfNewReleaseTask .isComplete ()) {
368+ cachedUpdateIfNewReleaseTask .setException (e );
369+ }
370+ }
371+ }
372+
373+ private void setCachedUpdateIfNewReleaseCompletionError (
374+ Exception e , FirebaseAppDistributionException defaultFirebaseException ) {
375+ if (e instanceof FirebaseAppDistributionException ) {
376+ setCachedUpdateIfNewReleaseCompletionError ((FirebaseAppDistributionException ) e );
377+ } else {
378+ setCachedUpdateIfNewReleaseCompletionError (defaultFirebaseException );
379+ }
380+ }
381+
382+ private void postProgressToCachedUpdateIfNewReleaseTask (UpdateProgress progress ) {
383+ synchronized (updateTaskLock ) {
384+ cachedUpdateIfNewReleaseTask .updateProgress (progress );
385+ }
386+ }
387+
388+ private void setCachedUpdateIfNewReleaseResult () {
389+ synchronized (updateTaskLock ) {
390+ cachedUpdateIfNewReleaseTask .setResult ();
391+ }
392+ }
322393}
0 commit comments