16
16
17
17
import static com .google .firebase .appdistribution .FirebaseAppDistributionException .Status .DOWNLOAD_FAILURE ;
18
18
import static com .google .firebase .appdistribution .FirebaseAppDistributionException .Status .NETWORK_FAILURE ;
19
+ import static com .google .firebase .appdistribution .TaskUtils .combineWithResultOf ;
20
+ import static com .google .firebase .appdistribution .TaskUtils .runAsyncInTask ;
19
21
import static com .google .firebase .appdistribution .TaskUtils .safeSetTaskException ;
20
22
21
23
import android .app .Activity ;
22
- import android .content .Context ;
23
24
import android .content .Intent ;
24
25
import android .net .Uri ;
25
26
import androidx .annotation .GuardedBy ;
@@ -42,29 +43,28 @@ class AabUpdater {
42
43
private final HttpsUrlConnectionFactory httpsUrlConnectionFactory ;
43
44
private final Executor executor ;
44
45
46
+ private final Object updateAabLock = new Object ();
47
+
45
48
@ GuardedBy ("updateAabLock" )
46
49
private UpdateTaskImpl cachedUpdateTask ;
47
50
48
51
@ GuardedBy ("updateAabLock" )
49
52
private AppDistributionReleaseInternal aabReleaseInProgress ;
50
53
51
- private final Object updateAabLock = new Object ();
52
- private final Context context ;
54
+ @ GuardedBy ( "updateAabLock" )
55
+ private boolean hasBeenSentToPlayForCurrentTask = false ;
53
56
54
- AabUpdater (@ NonNull Context context ) {
57
+ AabUpdater () {
55
58
this (
56
- context ,
57
59
FirebaseAppDistributionLifecycleNotifier .getInstance (),
58
60
new HttpsUrlConnectionFactory (),
59
61
Executors .newSingleThreadExecutor ());
60
62
}
61
63
62
64
AabUpdater (
63
- @ NonNull Context context ,
64
65
@ NonNull FirebaseAppDistributionLifecycleNotifier lifecycleNotifier ,
65
66
@ NonNull HttpsUrlConnectionFactory httpsUrlConnectionFactory ,
66
67
@ NonNull Executor executor ) {
67
- this .context = context ;
68
68
this .lifecycleNotifier = lifecycleNotifier ;
69
69
this .httpsUrlConnectionFactory = httpsUrlConnectionFactory ;
70
70
lifecycleNotifier .addOnActivityStartedListener (this ::onActivityStarted );
@@ -76,9 +76,13 @@ void onActivityStarted(Activity activity) {
76
76
if (activity instanceof SignInResultActivity || activity instanceof InstallActivity ) {
77
77
return ;
78
78
}
79
- // If app resumes and aab update task is in progress, assume that installation didn't happen so
80
- // cancel the task
81
- this .tryCancelAabUpdateTask ();
79
+ // If app resumes and update is in progress, assume that installation didn't happen and cancel
80
+ // the task
81
+ synchronized (updateAabLock ) {
82
+ if (awaitingUpdateFromPlay ()) {
83
+ this .tryCancelAabUpdateTask ();
84
+ }
85
+ }
82
86
}
83
87
84
88
UpdateTaskImpl updateAab (@ NonNull AppDistributionReleaseInternal newRelease ) {
@@ -89,7 +93,15 @@ UpdateTaskImpl updateAab(@NonNull AppDistributionReleaseInternal newRelease) {
89
93
90
94
cachedUpdateTask = new UpdateTaskImpl ();
91
95
aabReleaseInProgress = newRelease ;
92
- redirectToPlayForAabUpdate (newRelease .getDownloadUrl ());
96
+ hasBeenSentToPlayForCurrentTask = false ;
97
+
98
+ // On a background thread, fetch the redirect URL and open it in the Play app
99
+ runAsyncInTask (executor , () -> fetchDownloadRedirectUrl (newRelease .getDownloadUrl ()))
100
+ .onSuccessTask (combineWithResultOf (() -> lifecycleNotifier .getForegroundActivity ()))
101
+ .addOnSuccessListener (
102
+ urlAndActivity ->
103
+ openRedirectUrlInPlay (urlAndActivity .first (), urlAndActivity .second ()))
104
+ .addOnFailureListener (this ::setUpdateTaskCompletionError );
93
105
94
106
return cachedUpdateTask ;
95
107
}
@@ -138,38 +150,30 @@ private static boolean isRedirectResponse(int responseCode) {
138
150
return responseCode >= 300 && responseCode < 400 ;
139
151
}
140
152
141
- private void redirectToPlayForAabUpdate (String downloadUrl ) {
142
- // The 302 redirect is obtained here to open the play store directly and avoid opening chrome
143
- executor .execute ( // Execute the network calls on a background thread
144
- () -> {
145
- String redirectUrl ;
146
- try {
147
- redirectUrl = fetchDownloadRedirectUrl (downloadUrl );
148
- } catch (FirebaseAppDistributionException e ) {
149
- setUpdateTaskCompletionError (e );
150
- return ;
151
- }
152
-
153
- Intent updateIntent = new Intent (Intent .ACTION_VIEW );
154
- updateIntent .setData (Uri .parse (redirectUrl ));
155
- updateIntent .addFlags (Intent .FLAG_ACTIVITY_NEW_TASK );
156
- LogWrapper .getInstance ().v (TAG + "Redirecting to play" );
157
-
158
- synchronized (updateAabLock ) {
159
- context .startActivity (updateIntent );
160
- cachedUpdateTask .updateProgress (
161
- UpdateProgress .builder ()
162
- .setApkBytesDownloaded (-1 )
163
- .setApkFileTotalBytes (-1 )
164
- .setUpdateStatus (UpdateStatus .REDIRECTED_TO_PLAY )
165
- .build ());
166
- }
167
- });
153
+ private void openRedirectUrlInPlay (String redirectUrl , Activity hostActivity ) {
154
+ Intent updateIntent = new Intent (Intent .ACTION_VIEW );
155
+ updateIntent .setData (Uri .parse (redirectUrl ));
156
+ updateIntent .addFlags (Intent .FLAG_ACTIVITY_NEW_TASK );
157
+ LogWrapper .getInstance ().v (TAG + "Redirecting to play" );
158
+
159
+ // Launch the intent before the synchronized block to avoid failing to update in the rare
160
+ // scenario where the activity moves to the background while we're awaiting the lock.
161
+ hostActivity .startActivity (updateIntent );
162
+
163
+ synchronized (updateAabLock ) {
164
+ cachedUpdateTask .updateProgress (
165
+ UpdateProgress .builder ()
166
+ .setApkBytesDownloaded (-1 )
167
+ .setApkFileTotalBytes (-1 )
168
+ .setUpdateStatus (UpdateStatus .REDIRECTED_TO_PLAY )
169
+ .build ());
170
+ hasBeenSentToPlayForCurrentTask = true ;
171
+ }
168
172
}
169
173
170
- private void setUpdateTaskCompletionError (FirebaseAppDistributionException e ) {
174
+ private void setUpdateTaskCompletionError (Exception e ) {
171
175
synchronized (updateAabLock ) {
172
- safeSetTaskException (cachedUpdateTask , e );
176
+ safeSetTaskException (cachedUpdateTask , FirebaseAppDistributionException . wrap ( e ) );
173
177
}
174
178
}
175
179
@@ -183,4 +187,12 @@ void tryCancelAabUpdateTask() {
183
187
ReleaseUtils .convertToAppDistributionRelease (aabReleaseInProgress )));
184
188
}
185
189
}
190
+
191
+ private boolean awaitingUpdateFromPlay () {
192
+ synchronized (updateAabLock ) {
193
+ return cachedUpdateTask != null
194
+ && !cachedUpdateTask .isComplete ()
195
+ && hasBeenSentToPlayForCurrentTask ;
196
+ }
197
+ }
186
198
}
0 commit comments