Skip to content

Commit cbe2601

Browse files
manny-jimenezManny Jimenez
andauthored
Fad api changes (#2951)
* First attempt and changing API * Adding in update task is now returned by main config * Adding executor * removing executor and closing some input streams * Cleaning up import and resolving merge conflict * Responding to comments * Resolving tests Co-authored-by: Manny Jimenez <[email protected]>
1 parent 6b009b0 commit cbe2601

File tree

8 files changed

+242
-80
lines changed

8 files changed

+242
-80
lines changed

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

Lines changed: 115 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,11 @@
2121
import android.app.Application;
2222
import android.content.Context;
2323
import android.os.Bundle;
24+
import androidx.annotation.GuardedBy;
2425
import androidx.annotation.NonNull;
2526
import androidx.annotation.VisibleForTesting;
2627
import com.google.android.gms.common.internal.Preconditions;
2728
import com.google.android.gms.tasks.Task;
28-
import com.google.android.gms.tasks.TaskCompletionSource;
2929
import com.google.android.gms.tasks.Tasks;
3030
import com.google.firebase.FirebaseApp;
3131
import 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\nRelease notes: %s", latestRelease.getReleaseNotes()));
317+
if (newRelease.getReleaseNotes() != null && !newRelease.getReleaseNotes().isEmpty()) {
318+
message.append(String.format("\n\nRelease 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
}

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

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,10 +61,13 @@ void updateNotification(long totalBytes, long downloadedBytes, UpdateStatus stat
6161
notificationManager.notify(NOTIFICATION_TAG, /*id =*/ 0, builder.build());
6262
}
6363

64+
// CHECK THIS LATER
6465
private boolean isErrorState(UpdateStatus status) {
6566
return status.equals(UpdateStatus.DOWNLOAD_FAILED)
6667
|| status.equals(UpdateStatus.INSTALL_FAILED)
67-
|| status.equals(UpdateStatus.INSTALL_CANCELED);
68+
|| status.equals(UpdateStatus.INSTALL_CANCELED)
69+
|| status.equals(UpdateStatus.NEW_RELEASE_CHECK_FAILED)
70+
|| status.equals(UpdateStatus.UPDATE_CANCELED);
6871
}
6972

7073
private NotificationManager createNotificationManager(Context context) {

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,8 @@ class FirebaseAppDistributionTesterApiClient {
5757
connection.setRequestProperty(API_KEY_HEADER, apiKey);
5858
connection.setRequestProperty(INSTALLATION_AUTH_HEADER, authToken);
5959

60-
JSONObject latestReleaseJson = readFetchReleaseInputStream(connection.getInputStream());
60+
InputStream inputStream = connection.getInputStream();
61+
JSONObject latestReleaseJson = readFetchReleaseInputStream(inputStream);
6162
final String displayVersion = latestReleaseJson.getString(DISPLAY_VERSION_JSON_KEY);
6263
final String buildVersion = latestReleaseJson.getString(BUILD_VERSION_JSON_KEY);
6364
String releaseNotes = tryGetValue(latestReleaseJson, RELEASE_NOTES_JSON_KEY);
@@ -80,6 +81,7 @@ class FirebaseAppDistributionTesterApiClient {
8081
.setCodeHash(codeHash)
8182
.setDownloadUrl(downloadUrl)
8283
.build();
84+
inputStream.close();
8385

8486
} catch (IOException | JSONException e) {
8587
if (e instanceof JSONException) {

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -218,7 +218,8 @@ private void downloadToDisk(
218218

219219
try {
220220
// check that file is actual JAR
221-
new JarFile(apkFile);
221+
new JarFile(apkFile).close();
222+
222223
} catch (Exception e) {
223224
postUpdateProgress(
224225
totalSize,

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

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,11 +27,17 @@ public static UpdateProgress.Builder builder() {
2727
return new AutoValue_UpdateProgress.Builder();
2828
}
2929

30-
/** The number of bytes downloaded so far for the APK. Returns -1 if called on an AAB. */
30+
/**
31+
* The number of bytes downloaded so far for the APK. Returns -1 if called on an AAB or if no new
32+
* release is available.
33+
*/
3134
@NonNull
3235
public abstract long getApkBytesDownloaded();
3336

34-
/** The file size of the APK file to download in bytes. Returns -1 if called on an AAB. */
37+
/**
38+
* The file size of the APK file to download in bytes. Returns -1 if called on an AAB or if no new
39+
* release is available.
40+
*/
3541
@NonNull
3642
public abstract long getApkFileTotalBytes();
3743

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

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,4 +36,13 @@ public enum UpdateStatus {
3636

3737
/** AAB flow (directed to Play) */
3838
REDIRECTED_TO_PLAY,
39+
40+
/** Currently on the latest release */
41+
NEW_RELEASE_NOT_AVAILABLE,
42+
43+
/** Release check failed before download started */
44+
NEW_RELEASE_CHECK_FAILED,
45+
46+
/** Customer canceled the update */
47+
UPDATE_CANCELED,
3948
}

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

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import androidx.annotation.NonNull;
2020
import androidx.annotation.Nullable;
2121
import androidx.annotation.RestrictTo;
22+
import com.google.android.gms.tasks.OnCanceledListener;
2223
import com.google.android.gms.tasks.OnCompleteListener;
2324
import com.google.android.gms.tasks.OnFailureListener;
2425
import com.google.android.gms.tasks.OnSuccessListener;
@@ -171,6 +172,26 @@ public Task<Void> addOnCompleteListener(
171172
return this.task.addOnCompleteListener(activity, onCompleteListener);
172173
}
173174

175+
@NonNull
176+
@Override
177+
public Task<Void> addOnCanceledListener(@NonNull OnCanceledListener onCanceledListener) {
178+
return this.task.addOnCanceledListener(onCanceledListener);
179+
}
180+
181+
@NonNull
182+
@Override
183+
public Task<Void> addOnCanceledListener(
184+
@NonNull Executor executor, @NonNull OnCanceledListener onCanceledListener) {
185+
return this.task.addOnCanceledListener(executor, onCanceledListener);
186+
}
187+
188+
@NonNull
189+
@Override
190+
public Task<Void> addOnCanceledListener(
191+
@NonNull Activity activity, @NonNull OnCanceledListener onCanceledListener) {
192+
return this.task.addOnCanceledListener(activity, onCanceledListener);
193+
}
194+
174195
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
175196
public void setResult() {
176197

0 commit comments

Comments
 (0)