9
9
import android .app .Activity ;
10
10
import android .content .Context ;
11
11
import android .content .Intent ;
12
+ import android .os .Handler ;
13
+ import android .os .Looper ;
12
14
import android .util .Log ;
13
15
import androidx .annotation .NonNull ;
14
16
import androidx .annotation .Nullable ;
25
27
import com .google .android .gms .common .api .Scope ;
26
28
import com .google .android .gms .tasks .RuntimeExecutionException ;
27
29
import com .google .android .gms .tasks .Task ;
28
- import com .google .common .base .Joiner ;
29
- import com .google .common .base .Strings ;
30
30
import io .flutter .embedding .engine .plugins .FlutterPlugin ;
31
31
import io .flutter .embedding .engine .plugins .activity .ActivityAware ;
32
32
import io .flutter .embedding .engine .plugins .activity .ActivityPluginBinding ;
37
37
import java .util .ArrayList ;
38
38
import java .util .List ;
39
39
import java .util .Objects ;
40
- import java .util .concurrent .Callable ;
41
- import java .util .concurrent .ExecutionException ;
42
40
43
41
/** Google sign-in plugin for Flutter. */
44
42
public class GoogleSignInPlugin implements FlutterPlugin , ActivityAware {
@@ -135,8 +133,6 @@ public static class Delegate implements PluginRegistry.ActivityResultListener, G
135
133
private final @ NonNull Context context ;
136
134
// Only set activity for v2 embedder. Always access activity from getActivity() method.
137
135
private @ Nullable Activity activity ;
138
- // TODO(stuartmorgan): See whether this can be replaced with background channels.
139
- private final BackgroundTaskRunner backgroundTaskRunner = new BackgroundTaskRunner (1 );
140
136
private final GoogleSignInWrapper googleSignInWrapper ;
141
137
142
138
private GoogleSignInClient signInClient ;
@@ -224,15 +220,15 @@ public void init(@NonNull Messages.InitParams params) {
224
220
// https://developers.google.com/android/guides/client-auth
225
221
// https://developers.google.com/identity/sign-in/android/start#configure-a-google-api-project
226
222
String serverClientId = params .getServerClientId ();
227
- if (!Strings . isNullOrEmpty (params .getClientId ()) && Strings . isNullOrEmpty (serverClientId )) {
223
+ if (!isNullOrEmpty (params .getClientId ()) && isNullOrEmpty (serverClientId )) {
228
224
Log .w (
229
225
"google_sign_in" ,
230
226
"clientId is not supported on Android and is interpreted as serverClientId. "
231
227
+ "Use serverClientId instead to suppress this warning." );
232
228
serverClientId = params .getClientId ();
233
229
}
234
230
235
- if (Strings . isNullOrEmpty (serverClientId )) {
231
+ if (isNullOrEmpty (serverClientId )) {
236
232
// Only requests a clientId if google-services.json was present and parsed
237
233
// by the google-services Gradle script.
238
234
// TODO(jackson): Perhaps we should provide a mechanism to override this
@@ -246,7 +242,7 @@ public void init(@NonNull Messages.InitParams params) {
246
242
serverClientId = context .getString (webClientIdIdentifier );
247
243
}
248
244
}
249
- if (!Strings . isNullOrEmpty (serverClientId )) {
245
+ if (!isNullOrEmpty (serverClientId )) {
250
246
optionsBuilder .requestIdToken (serverClientId );
251
247
optionsBuilder .requestServerAuthCode (
252
248
serverClientId , params .getForceCodeForRefreshToken ());
@@ -255,7 +251,7 @@ public void init(@NonNull Messages.InitParams params) {
255
251
for (String scope : requestedScopes ) {
256
252
optionsBuilder .requestScopes (new Scope (scope ));
257
253
}
258
- if (!Strings . isNullOrEmpty (params .getHostedDomain ())) {
254
+ if (!isNullOrEmpty (params .getHostedDomain ())) {
259
255
optionsBuilder .setHostedDomain (params .getHostedDomain ());
260
256
}
261
257
@@ -450,6 +446,10 @@ private void finishWithError(String errorCode, String errorMessage) {
450
446
pendingOperation = null ;
451
447
}
452
448
449
+ private static boolean isNullOrEmpty (@ Nullable String s ) {
450
+ return s == null || s .isEmpty ();
451
+ }
452
+
453
453
private static class PendingOperation {
454
454
final @ NonNull String method ;
455
455
final @ Nullable Messages .Result <Messages .UserData > userDataResult ;
@@ -478,37 +478,26 @@ private static class PendingOperation {
478
478
}
479
479
}
480
480
481
- /** Clears the token kept in the client side cache. */
481
+ /**
482
+ * Clears the token kept in the client side cache.
483
+ *
484
+ * <p>Runs on a background task queue.
485
+ */
482
486
@ Override
483
- public void clearAuthCache (@ NonNull String token , @ NonNull Messages .VoidResult result ) {
484
- Callable <Void > clearTokenTask =
485
- () -> {
486
- GoogleAuthUtil .clearToken (context , token );
487
- return null ;
488
- };
489
-
490
- backgroundTaskRunner .runInBackground (
491
- clearTokenTask ,
492
- clearTokenFuture -> {
493
- try {
494
- clearTokenFuture .get ();
495
- result .success ();
496
- } catch (ExecutionException e ) {
497
- @ Nullable Throwable cause = e .getCause ();
498
- result .error (
499
- new FlutterError (
500
- ERROR_REASON_EXCEPTION , cause == null ? null : cause .getMessage (), null ));
501
- } catch (InterruptedException e ) {
502
- result .error (new FlutterError (ERROR_REASON_EXCEPTION , e .getMessage (), null ));
503
- Thread .currentThread ().interrupt ();
504
- }
505
- });
487
+ public void clearAuthCache (@ NonNull String token ) {
488
+ try {
489
+ GoogleAuthUtil .clearToken (context , token );
490
+ } catch (Exception e ) {
491
+ throw new FlutterError (ERROR_REASON_EXCEPTION , e .getMessage (), null );
492
+ }
506
493
}
507
494
508
495
/**
509
496
* Gets an OAuth access token with the scopes that were specified during initialization for the
510
497
* user with the specified email address.
511
498
*
499
+ * <p>Runs on a background task queue.
500
+ *
512
501
* <p>If shouldRecoverAuth is set to true and user needs to recover authentication for method to
513
502
* complete, the method will attempt to recover authentication and rerun method.
514
503
*/
@@ -517,53 +506,39 @@ public void getAccessToken(
517
506
@ NonNull String email ,
518
507
@ NonNull Boolean shouldRecoverAuth ,
519
508
@ NonNull Messages .Result <String > result ) {
520
- Callable <String > getTokenTask =
521
- () -> {
522
- Account account = new Account (email , "com.google" );
523
- String scopesStr = "oauth2:" + Joiner .on (' ' ).join (requestedScopes );
524
- return GoogleAuthUtil .getToken (context , account , scopesStr );
525
- };
526
-
527
- // Background task runner has a single thread effectively serializing
528
- // the getToken calls. 1p apps can then enjoy the token cache if multiple
529
- // getToken calls are coming in.
530
- backgroundTaskRunner .runInBackground (
531
- getTokenTask ,
532
- tokenFuture -> {
533
- try {
534
- result .success (tokenFuture .get ());
535
- } catch (ExecutionException e ) {
536
- if (e .getCause () instanceof UserRecoverableAuthException ) {
537
- if (shouldRecoverAuth && pendingOperation == null ) {
538
- Activity activity = getActivity ();
539
- if (activity == null ) {
540
- result .error (
541
- new FlutterError (
542
- ERROR_USER_RECOVERABLE_AUTH ,
543
- "Cannot recover auth because app is not in foreground. "
544
- + e .getLocalizedMessage (),
545
- null ));
546
- } else {
547
- checkAndSetPendingAccessTokenOperation ("getTokens" , result , email );
548
- Intent recoveryIntent =
549
- ((UserRecoverableAuthException ) e .getCause ()).getIntent ();
550
- activity .startActivityForResult (recoveryIntent , REQUEST_CODE_RECOVER_AUTH );
551
- }
552
- } else {
509
+ try {
510
+ Account account = new Account (email , "com.google" );
511
+ String scopesStr = "oauth2:" + String .join (" " , requestedScopes );
512
+ String token = GoogleAuthUtil .getToken (context , account , scopesStr );
513
+ result .success (token );
514
+ } catch (UserRecoverableAuthException e ) {
515
+ // This method runs in a background task queue; hop to the main thread for interactions with
516
+ // plugin state and activities.
517
+ final Handler handler = new Handler (Looper .getMainLooper ());
518
+ handler .post (
519
+ () -> {
520
+ if (shouldRecoverAuth && pendingOperation == null ) {
521
+ Activity activity = getActivity ();
522
+ if (activity == null ) {
553
523
result .error (
554
- new FlutterError (ERROR_USER_RECOVERABLE_AUTH , e .getLocalizedMessage (), null ));
524
+ new FlutterError (
525
+ ERROR_USER_RECOVERABLE_AUTH ,
526
+ "Cannot recover auth because app is not in foreground. "
527
+ + e .getLocalizedMessage (),
528
+ null ));
529
+ } else {
530
+ checkAndSetPendingAccessTokenOperation ("getTokens" , result , email );
531
+ Intent recoveryIntent = e .getIntent ();
532
+ activity .startActivityForResult (recoveryIntent , REQUEST_CODE_RECOVER_AUTH );
555
533
}
556
534
} else {
557
- @ Nullable Throwable cause = e .getCause ();
558
535
result .error (
559
- new FlutterError (
560
- ERROR_REASON_EXCEPTION , cause == null ? null : cause .getMessage (), null ));
536
+ new FlutterError (ERROR_USER_RECOVERABLE_AUTH , e .getLocalizedMessage (), null ));
561
537
}
562
- } catch (InterruptedException e ) {
563
- result .error (new FlutterError (ERROR_REASON_EXCEPTION , e .getMessage (), null ));
564
- Thread .currentThread ().interrupt ();
565
- }
566
- });
538
+ });
539
+ } catch (Exception e ) {
540
+ result .error (new FlutterError (ERROR_REASON_EXCEPTION , e .getMessage (), null ));
541
+ }
567
542
}
568
543
569
544
@ Override
0 commit comments