Skip to content

Commit f69db30

Browse files
pranavrajgopalandrewbibiloniRachel Prince
authored
Making sure a change in current activity gets propogated to the signIn and updateApp clients (#2888)
* Remove task completion from updateApp * Refactor updateTaskImpl to have completionsource and task as local fields * Address docStubs and clean up UpdateAppClient * Making sure a change in current activity gets propogated to the sign in and updateApp clients Co-authored-by: andrewbibiloni <[email protected]> Co-authored-by: Rachel Prince <[email protected]>
1 parent 5a1ae17 commit f69db30

File tree

10 files changed

+135
-50
lines changed

10 files changed

+135
-50
lines changed

firebase-app-distribution/src/main/java/com/google/firebase/appdistribution/Constants.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,5 +34,9 @@ static class ErrorMessages {
3434
public static final String UPDATE_CANCELED = "Update canceled";
3535

3636
public static final String UNKNOWN_ERROR = "Unknown Error";
37+
38+
public static final String DOWNLOAD_URL_NOT_FOUND = "Download URL not found";
39+
40+
public static final String APP_BACKGROUNDED = "No foreground activity available";
3741
}
3842
}

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

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,7 @@ public synchronized Task<Void> updateToLatestRelease() {
136136
/** Signs in the App Distribution tester. Presents the tester with a Google sign in UI */
137137
@NonNull
138138
public Task<Void> signInTester() {
139-
return this.testerSignInClient.signInTester(currentActivity);
139+
return this.testerSignInClient.signInTester();
140140
}
141141

142142
/**
@@ -172,7 +172,7 @@ public synchronized Task<AppDistributionRelease> checkForUpdate() {
172172
* new release is cached from checkForUpdate
173173
*/
174174
@NonNull
175-
public synchronized UpdateTask updateApp() throws FirebaseAppDistributionException {
175+
public synchronized UpdateTask updateApp() {
176176

177177
if (!isTesterSignedIn()) {
178178
UpdateTaskImpl updateTask = new UpdateTaskImpl();
@@ -227,6 +227,8 @@ public void onActivityResumed(@NonNull Activity activity) {
227227
}
228228

229229
this.currentActivity = activity;
230+
this.updateAppClient.setCurrentActivity(activity);
231+
this.testerSignInClient.setCurrentActivity(activity);
230232
}
231233

232234
@Override
@@ -249,6 +251,8 @@ public void onActivityDestroyed(@NonNull Activity activity) {
249251
Log.d(TAG, "Destroyed activity: " + activity.getClass().getName());
250252
if (this.currentActivity == activity) {
251253
this.currentActivity = null;
254+
this.updateAppClient.setCurrentActivity(null);
255+
this.testerSignInClient.setCurrentActivity(null);
252256
}
253257
}
254258

@@ -301,15 +305,11 @@ private Task<Void> showUpdateAlertDialog(AppDistributionRelease latestRelease) {
301305
alertDialog.setButton(
302306
AlertDialog.BUTTON_POSITIVE,
303307
context.getString(R.string.update_yes_button),
304-
(dialogInterface, i) -> {
305-
try {
308+
(dialogInterface, i) ->
306309
updateApp()
307310
.addOnSuccessListener(unused -> updateAlertDialogTask.setResult(null))
308-
.addOnFailureListener(updateAlertDialogTask::setException);
309-
} catch (FirebaseAppDistributionException e) {
310-
updateAlertDialogTask.setException(e);
311-
}
312-
});
311+
.addOnFailureListener(updateAlertDialogTask::setException));
312+
313313
alertDialog.setButton(
314314
AlertDialog.BUTTON_NEGATIVE,
315315
context.getString(R.string.update_no_button),

firebase-app-distribution/src/main/java/com/google/firebase/appdistribution/TesterSignInClient.java

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,9 @@
2525
import android.content.pm.ResolveInfo;
2626
import android.net.Uri;
2727
import android.util.Log;
28+
import androidx.annotation.GuardedBy;
2829
import androidx.annotation.NonNull;
30+
import androidx.annotation.Nullable;
2931
import androidx.browser.customtabs.CustomTabsIntent;
3032
import com.google.android.gms.tasks.OnFailureListener;
3133
import com.google.android.gms.tasks.OnSuccessListener;
@@ -47,6 +49,11 @@ class TesterSignInClient {
4749
private final FirebaseInstallationsApi firebaseInstallationsApi;
4850
private final SignInStorage signInStorage;
4951

52+
@GuardedBy("activityLock")
53+
private Activity currentActivity;
54+
55+
private final Object activityLock = new Object();
56+
5057
TesterSignInClient(
5158
@NonNull FirebaseApp firebaseApp,
5259
@NonNull FirebaseInstallationsApi firebaseInstallationsApi,
@@ -57,7 +64,7 @@ class TesterSignInClient {
5764
}
5865

5966
@NonNull
60-
public synchronized Task<Void> signInTester(@NonNull Activity currentActivity) {
67+
public synchronized Task<Void> signInTester() {
6168
if (signInStorage.getSignInStatus()) {
6269
return Tasks.forResult(null);
6370
}
@@ -66,6 +73,13 @@ public synchronized Task<Void> signInTester(@NonNull Activity currentActivity) {
6673
return signInTaskCompletionSource.getTask();
6774
}
6875

76+
Activity currentActivity = getCurrentActivity();
77+
if (currentActivity == null) {
78+
return Tasks.forException(
79+
new FirebaseAppDistributionException(
80+
ErrorMessages.APP_BACKGROUNDED,
81+
FirebaseAppDistributionException.Status.UPDATE_NOT_AVAILABLE));
82+
}
6983
signInTaskCompletionSource = new TaskCompletionSource<>();
7084

7185
AlertDialog alertDialog = getSignInAlertDialog(currentActivity);
@@ -188,4 +202,17 @@ private boolean supportsCustomTabs(Context context) {
188202
context.getPackageManager().queryIntentServices(customTabIntent, 0);
189203
return resolveInfos != null && !resolveInfos.isEmpty();
190204
}
205+
206+
@Nullable
207+
Activity getCurrentActivity() {
208+
synchronized (activityLock) {
209+
return this.currentActivity;
210+
}
211+
}
212+
213+
void setCurrentActivity(@Nullable Activity activity) {
214+
synchronized (activityLock) {
215+
this.currentActivity = activity;
216+
}
217+
}
191218
}

firebase-app-distribution/src/main/java/com/google/firebase/appdistribution/UpdateApkClient.java

Lines changed: 32 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -21,12 +21,15 @@
2121
import android.content.Context;
2222
import android.content.Intent;
2323
import android.util.Log;
24+
import androidx.annotation.GuardedBy;
2425
import androidx.annotation.NonNull;
26+
import androidx.annotation.Nullable;
2527
import androidx.annotation.RestrictTo;
2628
import androidx.annotation.VisibleForTesting;
2729
import com.google.android.gms.tasks.CancellationTokenSource;
2830
import com.google.android.gms.tasks.Task;
2931
import com.google.android.gms.tasks.TaskCompletionSource;
32+
import com.google.android.gms.tasks.Tasks;
3033
import com.google.firebase.FirebaseApp;
3134
import java.io.BufferedOutputStream;
3235
import java.io.File;
@@ -50,22 +53,24 @@ class UpdateApkClient {
5053
private final FirebaseApp firebaseApp;
5154
private UpdateTaskImpl cachedUpdateTask;
5255

56+
@GuardedBy("activityLock")
57+
private Activity currentActivity;
58+
59+
private final Object activityLock = new Object();
60+
5361
public UpdateApkClient(@NonNull FirebaseApp firebaseApp) {
5462
this.downloadExecutor = Executors.newSingleThreadExecutor();
5563
this.firebaseApp = firebaseApp;
5664
}
5765

58-
public void updateApk(
59-
@NonNull UpdateTaskImpl updateTask,
60-
@NonNull String downloadUrl,
61-
@NonNull Activity currentActivity) {
66+
public void updateApk(@NonNull UpdateTaskImpl updateTask, @NonNull String downloadUrl) {
6267

6368
this.cachedUpdateTask = updateTask;
6469
downloadApk(downloadUrl)
6570
.addOnSuccessListener(
6671
downloadExecutor,
6772
file ->
68-
install(file.getPath(), currentActivity)
73+
install(file.getPath())
6974
.addOnFailureListener(
7075
e -> {
7176
postInstallationFailure(e, file.length());
@@ -222,7 +227,15 @@ private void setDownloadTaskCompletionErrorWithDefault(
222227
}
223228
}
224229

225-
private Task<Void> install(String path, Activity currentActivity) {
230+
private Task<Void> install(String path) {
231+
Activity currentActivity = getCurrentActivity();
232+
233+
if (currentActivity == null) {
234+
return Tasks.forException(
235+
new FirebaseAppDistributionException(
236+
Constants.ErrorMessages.APP_BACKGROUNDED,
237+
FirebaseAppDistributionException.Status.DOWNLOAD_FAILURE));
238+
}
226239
Intent intent = new Intent(currentActivity, InstallActivity.class);
227240
intent.putExtra("INSTALL_PATH", path);
228241
CancellationTokenSource installCancellationTokenSource = new CancellationTokenSource();
@@ -283,6 +296,19 @@ private void postInstallationFailure(Exception e, long fileLength) {
283296
}
284297
}
285298

299+
@Nullable
300+
Activity getCurrentActivity() {
301+
synchronized (activityLock) {
302+
return this.currentActivity;
303+
}
304+
}
305+
306+
void setCurrentActivity(@Nullable Activity activity) {
307+
synchronized (activityLock) {
308+
this.currentActivity = activity;
309+
}
310+
}
311+
286312
@RestrictTo(RestrictTo.Scope.TESTS)
287313
void setCachedUpdateTask(UpdateTaskImpl updateTask) {
288314
this.cachedUpdateTask = updateTask;

firebase-app-distribution/src/main/java/com/google/firebase/appdistribution/UpdateAppClient.java

Lines changed: 43 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -19,16 +19,23 @@
1919
import android.app.Activity;
2020
import android.content.Intent;
2121
import android.net.Uri;
22+
import androidx.annotation.GuardedBy;
2223
import androidx.annotation.NonNull;
24+
import androidx.annotation.Nullable;
2325
import com.google.android.gms.tasks.TaskCompletionSource;
2426
import com.google.firebase.FirebaseApp;
2527
import com.google.firebase.appdistribution.internal.AppDistributionReleaseInternal;
2628

2729
/** Client class for updateApp functionality in {@link FirebaseAppDistribution}. */
2830
public class UpdateAppClient {
2931

32+
private final UpdateApkClient updateApkClient;
33+
34+
@GuardedBy("activityLock")
35+
private Activity currentActivity;
36+
37+
private final Object activityLock = new Object();
3038
private TaskCompletionSource<Void> updateAppTaskCompletionSource = null;
31-
private UpdateApkClient updateApkClient;
3239
private UpdateTaskImpl cachedUpdateAppTask;
3340

3441
public UpdateAppClient(@NonNull FirebaseApp firebaseApp) {
@@ -37,8 +44,7 @@ public UpdateAppClient(@NonNull FirebaseApp firebaseApp) {
3744

3845
@NonNull
3946
synchronized UpdateTask updateApp(
40-
@NonNull AppDistributionReleaseInternal latestRelease, @NonNull Activity currentActivity)
41-
throws FirebaseAppDistributionException {
47+
@NonNull AppDistributionReleaseInternal latestRelease, @NonNull Activity currentActivity) {
4248

4349
if (cachedUpdateAppTask != null && !cachedUpdateAppTask.isComplete()) {
4450
return cachedUpdateAppTask;
@@ -53,24 +59,33 @@ synchronized UpdateTask updateApp(
5359
return cachedUpdateAppTask;
5460
}
5561

62+
if (latestRelease.getDownloadUrl() == null) {
63+
cachedUpdateAppTask.setException(
64+
new FirebaseAppDistributionException(
65+
Constants.ErrorMessages.DOWNLOAD_URL_NOT_FOUND,
66+
FirebaseAppDistributionException.Status.DOWNLOAD_FAILURE));
67+
return cachedUpdateAppTask;
68+
}
69+
5670
if (latestRelease.getBinaryType() == BinaryType.AAB) {
57-
redirectToPlayForAabUpdate(
58-
cachedUpdateAppTask, latestRelease.getDownloadUrl(), currentActivity);
71+
redirectToPlayForAabUpdate(cachedUpdateAppTask, latestRelease.getDownloadUrl());
5972
} else {
60-
this.updateApkClient.updateApk(
61-
cachedUpdateAppTask, latestRelease.getDownloadUrl(), currentActivity);
73+
this.updateApkClient.updateApk(cachedUpdateAppTask, latestRelease.getDownloadUrl());
6274
}
63-
6475
return cachedUpdateAppTask;
6576
}
6677

67-
private void redirectToPlayForAabUpdate(
68-
UpdateTaskImpl updateTask, String downloadUrl, Activity currentActivity)
69-
throws FirebaseAppDistributionException {
70-
if (downloadUrl == null) {
71-
throw new FirebaseAppDistributionException(
72-
"Download URL not found.", FirebaseAppDistributionException.Status.NETWORK_FAILURE);
78+
private void redirectToPlayForAabUpdate(UpdateTaskImpl updateTask, String downloadUrl) {
79+
Activity currentActivity = getCurrentActivity();
80+
81+
if (currentActivity == null) {
82+
updateTask.setException(
83+
new FirebaseAppDistributionException(
84+
Constants.ErrorMessages.APP_BACKGROUNDED,
85+
FirebaseAppDistributionException.Status.DOWNLOAD_FAILURE));
86+
return;
7387
}
88+
7489
Intent updateIntent = new Intent(Intent.ACTION_VIEW);
7590
Uri uri = Uri.parse(downloadUrl);
7691
updateIntent.setData(uri);
@@ -87,6 +102,19 @@ private void redirectToPlayForAabUpdate(
87102

88103
void setInstallationResult(int resultCode) {
89104
this.updateApkClient.setInstallationResult(resultCode);
90-
updateAppTaskCompletionSource.setResult(null);
105+
}
106+
107+
@Nullable
108+
Activity getCurrentActivity() {
109+
synchronized (activityLock) {
110+
return this.currentActivity;
111+
}
112+
}
113+
114+
void setCurrentActivity(@Nullable Activity activity) {
115+
synchronized (activityLock) {
116+
this.currentActivity = activity;
117+
this.updateApkClient.setCurrentActivity(activity);
118+
}
91119
}
92120
}

firebase-app-distribution/src/main/java/com/google/firebase/appdistribution/UpdateTask.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@
2020
import java.util.concurrent.Executor;
2121

2222
public abstract class UpdateTask extends Task<Void> {
23-
2423
/**
2524
* Adds a listener that is called periodically while the UpdateTask executes.
2625
*

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

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@
2020
import static org.junit.Assert.assertNotNull;
2121
import static org.junit.Assert.assertNull;
2222
import static org.junit.Assert.assertTrue;
23-
import static org.mockito.ArgumentMatchers.any;
2423
import static org.mockito.Mockito.never;
2524
import static org.mockito.Mockito.spy;
2625
import static org.mockito.Mockito.times;
@@ -93,7 +92,6 @@ public class FirebaseAppDistributionTest {
9392
@Mock private SignInStorage mockSignInStorage;
9493
@Mock private Bundle mockBundle;
9594
@Mock private SignInResultActivity mockSignInResultActivity;
96-
@Mock private OnProgressListener onProgressListener;
9795

9896
static class TestActivity extends Activity {}
9997

@@ -122,7 +120,7 @@ public void setup() {
122120
mockUpdateAppClient,
123121
mockSignInStorage));
124122

125-
when(mockTesterSignInClient.signInTester(any())).thenReturn(Tasks.forResult(null));
123+
when(mockTesterSignInClient.signInTester()).thenReturn(Tasks.forResult(null));
126124

127125
when(mockInstallationTokenResult.getToken()).thenReturn(TEST_AUTH_TOKEN);
128126

@@ -203,7 +201,7 @@ public void checkForUpdate_callsSignInTester() throws Exception {
203201
firebaseAppDistribution.onActivityResumed(activity);
204202
firebaseAppDistribution.checkForUpdate();
205203

206-
verify(mockTesterSignInClient, times(1)).signInTester(any());
204+
verify(mockTesterSignInClient, times(1)).signInTester();
207205
}
208206

209207
@Test
@@ -254,7 +252,7 @@ public void updateToLatestRelease_whenNewAabReleaseAvailable_showsUpdateDialog()
254252
firebaseAppDistribution.onActivityResumed(activity);
255253

256254
// Update flow
257-
verify(mockTesterSignInClient, times(1)).signInTester(activity);
255+
verify(mockTesterSignInClient, times(1)).signInTester();
258256
assertTrue(ShadowAlertDialog.getLatestDialog() instanceof AlertDialog);
259257
AlertDialog updateDialog = (AlertDialog) ShadowAlertDialog.getLatestDialog();
260258
assertEquals(
@@ -308,7 +306,7 @@ public void updateToLatestRelease_whenNoReleaseAvailable_updateDialogNotShown()
308306
@Test
309307
public void updateToLatestRelease_whenSignInCancelled_checkForUpdateNotCalled() {
310308
when(mockSignInStorage.getSignInStatus()).thenReturn(false);
311-
when(mockTesterSignInClient.signInTester(any()))
309+
when(mockTesterSignInClient.signInTester())
312310
.thenReturn(
313311
Tasks.forException(
314312
new FirebaseAppDistributionException(
@@ -321,7 +319,7 @@ public void updateToLatestRelease_whenSignInCancelled_checkForUpdateNotCalled()
321319
firebaseAppDistribution.onActivityCreated(mockSignInResultActivity, mockBundle);
322320
firebaseAppDistribution.onActivityResumed(activity);
323321

324-
verify(mockTesterSignInClient, times(1)).signInTester(activity);
322+
verify(mockTesterSignInClient, times(1)).signInTester();
325323
verify(mockCheckForUpdateClient, never()).checkForUpdate();
326324
assertTrue(task.getException() instanceof FirebaseAppDistributionException);
327325
FirebaseAppDistributionException e = (FirebaseAppDistributionException) task.getException();
@@ -332,7 +330,7 @@ public void updateToLatestRelease_whenSignInCancelled_checkForUpdateNotCalled()
332330
@Test
333331
public void updateToLatestRelease_whenSignInFailed_checkForUpdateNotCalled() {
334332
when(mockSignInStorage.getSignInStatus()).thenReturn(false);
335-
when(mockTesterSignInClient.signInTester(any()))
333+
when(mockTesterSignInClient.signInTester())
336334
.thenReturn(
337335
Tasks.forException(
338336
new FirebaseAppDistributionException(
@@ -374,7 +372,7 @@ public void updateToLatestRelease_callsSignInTester() {
374372
when(mockCheckForUpdateClient.checkForUpdate())
375373
.thenReturn(Tasks.forResult(TEST_RELEASE_NEWER_AAB_INTERNAL.build()));
376374
firebaseAppDistribution.updateToLatestRelease();
377-
verify(mockTesterSignInClient, times(1)).signInTester(any());
375+
verify(mockTesterSignInClient, times(1)).signInTester();
378376
}
379377

380378
@Test

0 commit comments

Comments
 (0)