15
15
package com .google .firebase .appdistribution ;
16
16
17
17
import static com .google .firebase .appdistribution .FirebaseAppDistributionException .Status .AUTHENTICATION_CANCELED ;
18
- import static com .google .firebase .appdistribution .FirebaseAppDistributionException . Status . AUTHENTICATION_FAILURE ;
18
+ import static com .google .firebase .appdistribution .TaskUtils . combineWithResultOf ;
19
19
import static com .google .firebase .appdistribution .TaskUtils .safeSetTaskException ;
20
20
import static com .google .firebase .appdistribution .TaskUtils .safeSetTaskResult ;
21
21
28
28
import androidx .annotation .NonNull ;
29
29
import androidx .annotation .VisibleForTesting ;
30
30
import androidx .browser .customtabs .CustomTabsIntent ;
31
- import com .google .android .gms .tasks .OnSuccessListener ;
31
+ import com .google .android .gms .tasks .OnFailureListener ;
32
32
import com .google .android .gms .tasks .Task ;
33
33
import com .google .android .gms .tasks .TaskCompletionSource ;
34
34
import com .google .android .gms .tasks .Tasks ;
35
35
import com .google .firebase .FirebaseApp ;
36
36
import com .google .firebase .appdistribution .Constants .ErrorMessages ;
37
+ import com .google .firebase .appdistribution .FirebaseAppDistributionException .Status ;
37
38
import com .google .firebase .appdistribution .internal .InstallActivity ;
38
39
import com .google .firebase .appdistribution .internal .LogWrapper ;
39
40
import com .google .firebase .appdistribution .internal .SignInResultActivity ;
@@ -55,6 +56,9 @@ class TesterSignInManager {
55
56
56
57
private final Object signInTaskLock = new Object ();
57
58
59
+ @ GuardedBy ("signInTaskLock" )
60
+ private boolean hasBeenSentToBrowserForCurrentTask = false ;
61
+
58
62
@ GuardedBy ("signInTaskLock" )
59
63
private TaskCompletionSource <Void > signInTaskCompletionSource = null ;
60
64
@@ -87,8 +91,7 @@ class TesterSignInManager {
87
91
@ VisibleForTesting
88
92
void onActivityCreated (Activity activity ) {
89
93
// We call finish() in the onCreate method of the SignInResultActivity, so we must set the
90
- // result
91
- // of the signIn Task in the onActivityCreated callback
94
+ // result of the signIn Task in the onActivityCreated callback
92
95
if (activity instanceof SignInResultActivity ) {
93
96
LogWrapper .getInstance ().v ("Sign in completed" );
94
97
this .setSuccessfulSignInResult ();
@@ -104,50 +107,74 @@ void onActivityStarted(Activity activity) {
104
107
return ;
105
108
} else {
106
109
// Throw error if app reentered during sign in
107
- if (this .isCurrentlySigningIn ()) {
108
- LogWrapper .getInstance ().e ("App Resumed without sign in flow completing." );
109
- this .setCanceledAuthenticationError ();
110
+ synchronized (signInTaskLock ) {
111
+ if (awaitingResultFromBrowser ()) {
112
+ LogWrapper .getInstance ().e ("App Resumed without sign in flow completing." );
113
+ setSignInTaskCompletionError (
114
+ new FirebaseAppDistributionException (
115
+ Constants .ErrorMessages .AUTHENTICATION_CANCELED , AUTHENTICATION_CANCELED ));
116
+ }
110
117
}
111
118
}
112
119
}
113
120
114
121
@ NonNull
115
122
public Task <Void > signInTester () {
116
-
117
123
if (signInStorage .getSignInStatus ()) {
118
124
LogWrapper .getInstance ().v (TAG + "Tester is already signed in." );
119
125
return Tasks .forResult (null );
120
126
}
121
127
122
128
synchronized (signInTaskLock ) {
123
- if (this .isCurrentlySigningIn ()) {
129
+ if (signInTaskCompletionSource != null
130
+ && !signInTaskCompletionSource .getTask ().isComplete ()) {
124
131
LogWrapper .getInstance ()
125
132
.v (TAG + "Detected In-Progress sign in task. Returning the same task." );
126
133
return signInTaskCompletionSource .getTask ();
127
134
}
128
135
129
136
signInTaskCompletionSource = new TaskCompletionSource <>();
137
+ hasBeenSentToBrowserForCurrentTask = false ;
130
138
131
139
firebaseInstallationsApiProvider
132
140
.get ()
133
141
.getId ()
134
- .addOnSuccessListener (getFidGenerationOnSuccessListener ())
135
142
.addOnFailureListener (
136
- e -> {
137
- LogWrapper .getInstance ().e (TAG + "Fid retrieval failed." , e );
138
- setSignInTaskCompletionError (
139
- new FirebaseAppDistributionException (
140
- ErrorMessages .AUTHENTICATION_ERROR , AUTHENTICATION_FAILURE , e ));
141
- });
143
+ handleTaskFailure (ErrorMessages .AUTHENTICATION_ERROR , Status .AUTHENTICATION_FAILURE ))
144
+ .onSuccessTask (combineWithResultOf (() -> lifecycleNotifier .getForegroundActivity ()))
145
+ .addOnSuccessListener (
146
+ fidAndActivity -> {
147
+ // Launch the intent outside of the synchronized block because we don't need to wait
148
+ // for the lock, and we don't want to risk the activity leaving the foreground in
149
+ // the meantime.
150
+ openSignInFlowInBrowser (fidAndActivity .first (), fidAndActivity .second ());
151
+ // This synchronized block is required by the @GuardedBy annotation, but is not
152
+ // practically required in this case because the only reads of this variable are on
153
+ // the main thread, which this callback is also running on.
154
+ synchronized (signInTaskLock ) {
155
+ hasBeenSentToBrowserForCurrentTask = true ;
156
+ }
157
+ })
158
+ // No failures expected here, since getForegroundActivity() will wait indefinitely for a
159
+ // foreground activity, but catch any unexpected failures to be safe.
160
+ .addOnFailureListener (handleTaskFailure (ErrorMessages .UNKNOWN_ERROR , Status .UNKNOWN ));
142
161
143
162
return signInTaskCompletionSource .getTask ();
144
163
}
145
164
}
146
165
147
- private boolean isCurrentlySigningIn () {
166
+ private OnFailureListener handleTaskFailure (String message , Status status ) {
167
+ return e -> {
168
+ LogWrapper .getInstance ().e (TAG + message , e );
169
+ setSignInTaskCompletionError (new FirebaseAppDistributionException (message , status , e ));
170
+ };
171
+ }
172
+
173
+ private boolean awaitingResultFromBrowser () {
148
174
synchronized (signInTaskLock ) {
149
175
return signInTaskCompletionSource != null
150
- && !signInTaskCompletionSource .getTask ().isComplete ();
176
+ && !signInTaskCompletionSource .getTask ().isComplete ()
177
+ && hasBeenSentToBrowserForCurrentTask ;
151
178
}
152
179
}
153
180
@@ -157,33 +184,12 @@ private void setSignInTaskCompletionError(FirebaseAppDistributionException e) {
157
184
}
158
185
}
159
186
160
- private void setCanceledAuthenticationError () {
161
- setSignInTaskCompletionError (
162
- new FirebaseAppDistributionException (
163
- Constants .ErrorMessages .AUTHENTICATION_CANCELED , AUTHENTICATION_CANCELED ));
164
- }
165
-
166
187
private void setSuccessfulSignInResult () {
167
188
synchronized (signInTaskLock ) {
168
189
safeSetTaskResult (signInTaskCompletionSource , null );
169
190
}
170
191
}
171
192
172
- private OnSuccessListener <String > getFidGenerationOnSuccessListener () {
173
- return fid -> {
174
- Context context = firebaseApp .getApplicationContext ();
175
- Uri uri =
176
- Uri .parse (
177
- String .format (
178
- SIGNIN_REDIRECT_URL ,
179
- firebaseApp .getOptions ().getApplicationId (),
180
- fid ,
181
- getApplicationName (context ),
182
- context .getPackageName ()));
183
- openSignInFlowInBrowser (context , uri );
184
- };
185
- }
186
-
187
193
private static String getApplicationName (Context context ) {
188
194
try {
189
195
return context .getApplicationInfo ().loadLabel (context .getPackageManager ()).toString ();
@@ -193,22 +199,30 @@ private static String getApplicationName(Context context) {
193
199
}
194
200
}
195
201
196
- private void openSignInFlowInBrowser (Context applicationContext , Uri uri ) {
202
+ private void openSignInFlowInBrowser (String fid , Activity activity ) {
203
+ Context context = firebaseApp .getApplicationContext ();
204
+ Uri uri =
205
+ Uri .parse (
206
+ String .format (
207
+ SIGNIN_REDIRECT_URL ,
208
+ firebaseApp .getOptions ().getApplicationId (),
209
+ fid ,
210
+ getApplicationName (context ),
211
+ context .getPackageName ()));
197
212
LogWrapper .getInstance ().v (TAG + "Opening sign in flow in browser at " + uri );
198
- if (supportsCustomTabs (applicationContext )) {
213
+ if (supportsCustomTabs (context )) {
199
214
// If we can launch a chrome view, try that.
200
215
CustomTabsIntent customTabsIntent = new CustomTabsIntent .Builder ().build ();
201
216
Intent intent = customTabsIntent .intent ;
202
217
intent .addFlags (Intent .FLAG_ACTIVITY_NO_HISTORY );
203
218
intent .addFlags (Intent .FLAG_ACTIVITY_NEW_TASK );
204
- customTabsIntent .launchUrl (applicationContext , uri );
205
-
219
+ customTabsIntent .launchUrl (activity , uri );
206
220
} else {
207
221
// If we can't launch a chrome view try to launch anything that can handle a URL.
208
222
Intent browserIntent = new Intent (Intent .ACTION_VIEW , uri );
209
223
browserIntent .addFlags (Intent .FLAG_ACTIVITY_NO_HISTORY );
210
224
browserIntent .addFlags (Intent .FLAG_ACTIVITY_NEW_TASK );
211
- applicationContext .startActivity (browserIntent );
225
+ activity .startActivity (browserIntent );
212
226
}
213
227
}
214
228
0 commit comments