16
16
17
17
import static com .google .firebase .appdistribution .FirebaseAppDistributionException .Status .AUTHENTICATION_CANCELED ;
18
18
import static com .google .firebase .appdistribution .FirebaseAppDistributionException .Status .AUTHENTICATION_FAILURE ;
19
+ import static com .google .firebase .appdistribution .FirebaseAppDistributionException .Status .HOST_ACTIVITY_INTERRUPTED ;
19
20
import static com .google .firebase .appdistribution .FirebaseAppDistributionException .Status .UPDATE_NOT_AVAILABLE ;
20
21
import static com .google .firebase .appdistribution .TaskUtils .safeSetTaskException ;
21
22
import static com .google .firebase .appdistribution .TaskUtils .safeSetTaskResult ;
34
35
import com .google .firebase .appdistribution .Constants .ErrorMessages ;
35
36
import com .google .firebase .appdistribution .FirebaseAppDistributionException .Status ;
36
37
import com .google .firebase .appdistribution .internal .LogWrapper ;
37
- import com .google .firebase .appdistribution .internal .SignInResultActivity ;
38
38
import com .google .firebase .appdistribution .internal .SignInStorage ;
39
39
import com .google .firebase .inject .Provider ;
40
40
import com .google .firebase .installations .FirebaseInstallationsApi ;
@@ -62,10 +62,15 @@ public class FirebaseAppDistribution {
62
62
private AppDistributionReleaseInternal cachedNewRelease ;
63
63
64
64
private Task <AppDistributionRelease > cachedCheckForNewReleaseTask ;
65
- private AlertDialog updateDialog ;
65
+ private AlertDialog updateConfirmationDialog ;
66
66
private AlertDialog signInConfirmationDialog ;
67
- private boolean remakeUpdateDialog = false ;
67
+ @ Nullable private Activity dialogHostActivity = null ;
68
+
68
69
private boolean remakeSignInConfirmationDialog = false ;
70
+ private boolean remakeUpdateConfirmationDialog = false ;
71
+
72
+ private TaskCompletionSource <Void > showSignInDialogTask = null ;
73
+ private TaskCompletionSource <Void > showUpdateDialogTask = null ;
69
74
70
75
/** Constructor for FirebaseAppDistribution */
71
76
@ VisibleForTesting
@@ -85,7 +90,8 @@ public class FirebaseAppDistribution {
85
90
this .signInStorage = signInStorage ;
86
91
this .lifecycleNotifier = lifecycleNotifier ;
87
92
lifecycleNotifier .addOnActivityDestroyedListener (this ::onActivityDestroyed );
88
- lifecycleNotifier .addOnActivityCreatedListener (this ::onActivityCreated );
93
+ lifecycleNotifier .addOnActivityPausedListener (this ::onActivityPaused );
94
+ lifecycleNotifier .addOnActivityResumedListener (this ::onActivityResumed );
89
95
}
90
96
91
97
/** Constructor for FirebaseAppDistribution */
@@ -135,17 +141,20 @@ public static FirebaseAppDistribution getInstance() {
135
141
@ NonNull
136
142
public UpdateTask updateIfNewReleaseAvailable () {
137
143
synchronized (updateIfNewReleaseTaskLock ) {
138
- if (cachedUpdateIfNewReleaseTask != null && ! cachedUpdateIfNewReleaseTask . isComplete ()) {
144
+ if (updateIfNewReleaseAvailableIsTaskInProgress ()) {
139
145
return cachedUpdateIfNewReleaseTask ;
140
146
}
141
147
cachedUpdateIfNewReleaseTask = new UpdateTaskImpl ();
148
+ remakeSignInConfirmationDialog = false ;
149
+ remakeUpdateConfirmationDialog = false ;
150
+ dialogHostActivity = null ;
142
151
}
143
152
144
153
lifecycleNotifier
145
154
.applyToForegroundActivityTask (this ::showSignInConfirmationDialog )
146
155
// TODO(rachelprince): Revisit this comment once changes to checkForNewRelease are reviewed
147
156
// Even though checkForNewRelease() calls signInTester(), we explicitly call signInTester
148
- // here both for code clarifty , and because we plan to remove the signInTester() call
157
+ // here for code clarity , and because we plan to remove the signInTester() call
149
158
// from checkForNewRelease() in the near future
150
159
.onSuccessTask (unused -> signInTester ())
151
160
.onSuccessTask (unused -> checkForNewRelease ())
@@ -159,7 +168,7 @@ public UpdateTask updateIfNewReleaseAvailable() {
159
168
.setUpdateStatus (UpdateStatus .NEW_RELEASE_CHECK_FAILED )
160
169
.build ());
161
170
}
162
- // if the task failed, this get() will cause the error to propogate to the handler
171
+ // if the task failed, this get() will cause the error to propagate to the handler
163
172
// below
164
173
AppDistributionRelease release = task .getResult ();
165
174
if (release == null ) {
@@ -173,7 +182,7 @@ public UpdateTask updateIfNewReleaseAvailable() {
173
182
return Tasks .forResult (null );
174
183
}
175
184
return lifecycleNotifier .applyToForegroundActivityTask (
176
- activity -> showUpdateAlertDialog (activity , release ));
185
+ activity -> showUpdateConfirmationDialog (activity , release ));
177
186
})
178
187
.onSuccessTask (
179
188
unused ->
@@ -191,34 +200,39 @@ private Task<Void> showSignInConfirmationDialog(Activity hostActivity) {
191
200
return Tasks .forResult (null );
192
201
}
193
202
194
- TaskCompletionSource <Void > showDialogTask = new TaskCompletionSource <>();
203
+ if (showSignInDialogTask == null || showSignInDialogTask .getTask ().isComplete ()) {
204
+ showSignInDialogTask = new TaskCompletionSource <>();
205
+ }
195
206
196
207
signInConfirmationDialog = new AlertDialog .Builder (hostActivity ).create ();
208
+ dialogHostActivity = hostActivity ;
209
+
197
210
Context context = firebaseApp .getApplicationContext ();
198
211
signInConfirmationDialog .setTitle (context .getString (R .string .signin_dialog_title ));
199
212
signInConfirmationDialog .setMessage (context .getString (R .string .singin_dialog_message ));
200
213
201
214
signInConfirmationDialog .setButton (
202
215
AlertDialog .BUTTON_POSITIVE ,
203
216
context .getString (R .string .singin_yes_button ),
204
- (dialogInterface , i ) -> showDialogTask .setResult (null ));
217
+ (dialogInterface , i ) -> showSignInDialogTask .setResult (null ));
205
218
206
219
signInConfirmationDialog .setButton (
207
220
AlertDialog .BUTTON_NEGATIVE ,
208
221
context .getString (R .string .singin_no_button ),
209
222
(dialogInterface , i ) ->
210
- showDialogTask .setException (
223
+ showSignInDialogTask .setException (
211
224
new FirebaseAppDistributionException (
212
225
ErrorMessages .AUTHENTICATION_CANCELED , AUTHENTICATION_CANCELED )));
213
226
214
227
signInConfirmationDialog .setOnCancelListener (
215
228
dialogInterface ->
216
- showDialogTask .setException (
229
+ showSignInDialogTask .setException (
217
230
new FirebaseAppDistributionException (
218
231
ErrorMessages .AUTHENTICATION_CANCELED , AUTHENTICATION_CANCELED )));
219
232
220
233
signInConfirmationDialog .show ();
221
- return showDialogTask .getTask ();
234
+
235
+ return showSignInDialogTask .getTask ();
222
236
}
223
237
224
238
/** Signs in the App Distribution tester. Presents the tester with a Google sign in UI */
@@ -280,14 +294,14 @@ public UpdateTask updateApp() {
280
294
* basic configuration and false for advanced configuration.
281
295
*/
282
296
private UpdateTask updateApp (boolean showDownloadInNotificationManager ) {
283
- if (!isTesterSignedIn ()) {
284
- UpdateTaskImpl updateTask = new UpdateTaskImpl ();
285
- updateTask .setException (
286
- new FirebaseAppDistributionException (
287
- Constants .ErrorMessages .AUTHENTICATION_ERROR , AUTHENTICATION_FAILURE ));
288
- return updateTask ;
289
- }
290
297
synchronized (cachedNewReleaseLock ) {
298
+ if (!isTesterSignedIn ()) {
299
+ UpdateTaskImpl updateTask = new UpdateTaskImpl ();
300
+ updateTask .setException (
301
+ new FirebaseAppDistributionException (
302
+ Constants .ErrorMessages .AUTHENTICATION_ERROR , AUTHENTICATION_FAILURE ));
303
+ return updateTask ;
304
+ }
291
305
if (cachedNewRelease == null ) {
292
306
LogWrapper .getInstance ().v ("New release not found." );
293
307
return getErrorUpdateTask (
@@ -322,42 +336,48 @@ public void signOutTester() {
322
336
}
323
337
324
338
@ VisibleForTesting
325
- void onActivityDestroyed (@ NonNull Activity activity ) {
326
- if (activity instanceof SignInResultActivity ) {
327
- // SignInResult is internal to the SDK and is destroyed after creation
328
- return ;
329
- }
330
- if (activity .isChangingConfigurations ()) {
331
- remakeSignInConfirmationDialog =
332
- signInConfirmationDialog != null && signInConfirmationDialog .isShowing ();
333
- remakeUpdateDialog = updateDialog != null && updateDialog .isShowing ();
334
- dismissDialogs ();
335
- return ;
339
+ void onActivityResumed (Activity activity ) {
340
+ if (awaitingSignInDialogConfirmation ()) {
341
+ if (dialogHostActivity != null && dialogHostActivity != activity ) {
342
+ showSignInDialogTask .setException (
343
+ new FirebaseAppDistributionException (
344
+ ErrorMessages .HOST_ACTIVITY_INTERRUPTED , HOST_ACTIVITY_INTERRUPTED ));
345
+ } else {
346
+ showSignInConfirmationDialog (activity );
347
+ }
336
348
}
337
349
338
- if (signInConfirmationDialog != null && signInConfirmationDialog .isShowing ()) {
339
- setCachedUpdateIfNewReleaseCompletionError (
340
- new FirebaseAppDistributionException (
341
- ErrorMessages .AUTHENTICATION_CANCELED , AUTHENTICATION_CANCELED ));
350
+ if (awaitingUpdateDialogConfirmation ()) {
351
+ if (dialogHostActivity != null && dialogHostActivity != activity ) {
352
+ showUpdateDialogTask .setException (
353
+ new FirebaseAppDistributionException (
354
+ ErrorMessages .HOST_ACTIVITY_INTERRUPTED , HOST_ACTIVITY_INTERRUPTED ));
355
+ } else {
356
+ synchronized (cachedNewReleaseLock ) {
357
+ showUpdateConfirmationDialog (
358
+ activity , ReleaseUtils .convertToAppDistributionRelease (cachedNewRelease ));
359
+ }
360
+ }
342
361
}
362
+ }
343
363
344
- if (updateDialog != null && updateDialog .isShowing ()) {
345
- setCachedUpdateIfNewReleaseCompletionError (
346
- new FirebaseAppDistributionException (
347
- ErrorMessages .UPDATE_CANCELED , Status .INSTALLATION_CANCELED ));
364
+ @ VisibleForTesting
365
+ void onActivityPaused (Activity activity ) {
366
+ if (activity == dialogHostActivity ) {
367
+ remakeSignInConfirmationDialog =
368
+ signInConfirmationDialog != null && signInConfirmationDialog .isShowing ();
369
+ remakeUpdateConfirmationDialog =
370
+ updateConfirmationDialog != null && updateConfirmationDialog .isShowing ();
371
+ dismissDialogs ();
348
372
}
349
373
}
350
374
351
- void onActivityCreated (Activity activity ) {
352
- if (remakeSignInConfirmationDialog ) {
353
- remakeSignInConfirmationDialog = false ;
354
- showSignInConfirmationDialog (activity );
355
- } else if (remakeUpdateDialog ) {
356
- remakeUpdateDialog = false ;
357
- synchronized (cachedNewReleaseLock ) {
358
- showUpdateAlertDialog (
359
- activity , ReleaseUtils .convertToAppDistributionRelease (cachedNewRelease ));
360
- }
375
+ @ VisibleForTesting
376
+ void onActivityDestroyed (@ NonNull Activity activity ) {
377
+ // If the dialogHostActivity is being destroyed it is set to null. This is to ensure onResume
378
+ // shows the dialog on a configuration change and does not check the activity reference.
379
+ if (activity == dialogHostActivity ) {
380
+ dialogHostActivity = null ;
361
381
}
362
382
}
363
383
@@ -375,14 +395,18 @@ AppDistributionReleaseInternal getCachedNewRelease() {
375
395
}
376
396
}
377
397
378
- private Task <Void > showUpdateAlertDialog (
398
+ private Task <Void > showUpdateConfirmationDialog (
379
399
Activity hostActivity , AppDistributionRelease newRelease ) {
380
- TaskCompletionSource <Void > showUpdateDialogTask = new TaskCompletionSource <>();
400
+
401
+ if (showUpdateDialogTask == null || showUpdateDialogTask .getTask ().isComplete ()) {
402
+ showUpdateDialogTask = new TaskCompletionSource <>();
403
+ }
381
404
382
405
Context context = firebaseApp .getApplicationContext ();
383
406
384
- updateDialog = new AlertDialog .Builder (hostActivity ).create ();
385
- updateDialog .setTitle (context .getString (R .string .update_dialog_title ));
407
+ updateConfirmationDialog = new AlertDialog .Builder (hostActivity ).create ();
408
+ dialogHostActivity = hostActivity ;
409
+ updateConfirmationDialog .setTitle (context .getString (R .string .update_dialog_title ));
386
410
387
411
StringBuilder message =
388
412
new StringBuilder (
@@ -393,28 +417,28 @@ private Task<Void> showUpdateAlertDialog(
393
417
if (newRelease .getReleaseNotes () != null && !newRelease .getReleaseNotes ().isEmpty ()) {
394
418
message .append (String .format ("\n \n Release notes: %s" , newRelease .getReleaseNotes ()));
395
419
}
396
- updateDialog .setMessage (message );
420
+ updateConfirmationDialog .setMessage (message );
397
421
398
- updateDialog .setButton (
422
+ updateConfirmationDialog .setButton (
399
423
AlertDialog .BUTTON_POSITIVE ,
400
424
context .getString (R .string .update_yes_button ),
401
425
(dialogInterface , i ) -> showUpdateDialogTask .setResult (null ));
402
426
403
- updateDialog .setButton (
427
+ updateConfirmationDialog .setButton (
404
428
AlertDialog .BUTTON_NEGATIVE ,
405
429
context .getString (R .string .update_no_button ),
406
430
(dialogInterface , i ) ->
407
431
showUpdateDialogTask .setException (
408
432
new FirebaseAppDistributionException (
409
433
ErrorMessages .UPDATE_CANCELED , Status .INSTALLATION_CANCELED )));
410
434
411
- updateDialog .setOnCancelListener (
435
+ updateConfirmationDialog .setOnCancelListener (
412
436
dialogInterface ->
413
437
showUpdateDialogTask .setException (
414
438
new FirebaseAppDistributionException (
415
439
ErrorMessages .UPDATE_CANCELED , Status .INSTALLATION_CANCELED )));
416
440
417
- updateDialog .show ();
441
+ updateConfirmationDialog .show ();
418
442
419
443
return showUpdateDialogTask .getTask ();
420
444
}
@@ -445,8 +469,8 @@ private void dismissDialogs() {
445
469
if (signInConfirmationDialog != null && signInConfirmationDialog .isShowing ()) {
446
470
signInConfirmationDialog .dismiss ();
447
471
}
448
- if (updateDialog != null && updateDialog .isShowing ()) {
449
- updateDialog .dismiss ();
472
+ if (updateConfirmationDialog != null && updateConfirmationDialog .isShowing ()) {
473
+ updateConfirmationDialog .dismiss ();
450
474
}
451
475
}
452
476
@@ -455,4 +479,22 @@ private UpdateTaskImpl getErrorUpdateTask(Exception e) {
455
479
updateTask .setException (e );
456
480
return updateTask ;
457
481
}
482
+
483
+ private boolean updateIfNewReleaseAvailableIsTaskInProgress () {
484
+ synchronized (updateIfNewReleaseTaskLock ) {
485
+ return cachedUpdateIfNewReleaseTask != null && !cachedUpdateIfNewReleaseTask .isComplete ();
486
+ }
487
+ }
488
+
489
+ private boolean awaitingSignInDialogConfirmation () {
490
+ return (showSignInDialogTask != null
491
+ && !showSignInDialogTask .getTask ().isComplete ()
492
+ && remakeSignInConfirmationDialog );
493
+ }
494
+
495
+ private boolean awaitingUpdateDialogConfirmation () {
496
+ return (showUpdateDialogTask != null
497
+ && !showUpdateDialogTask .getTask ().isComplete ()
498
+ && remakeUpdateConfirmationDialog );
499
+ }
458
500
}
0 commit comments