6
6
7
7
import android .accounts .Account ;
8
8
import android .app .Activity ;
9
+ import android .content .Context ;
9
10
import android .content .Intent ;
11
+ import androidx .annotation .VisibleForTesting ;
10
12
import com .google .android .gms .auth .GoogleAuthUtil ;
11
13
import com .google .android .gms .auth .UserRecoverableAuthException ;
12
14
import com .google .android .gms .auth .api .signin .GoogleSignIn ;
27
29
import io .flutter .plugin .common .MethodChannel .MethodCallHandler ;
28
30
import io .flutter .plugin .common .MethodChannel .Result ;
29
31
import io .flutter .plugin .common .PluginRegistry ;
32
+ import java .util .ArrayList ;
30
33
import java .util .HashMap ;
31
34
import java .util .List ;
32
35
import java .util .Map ;
@@ -46,17 +49,19 @@ public class GoogleSignInPlugin implements MethodCallHandler {
46
49
private static final String METHOD_DISCONNECT = "disconnect" ;
47
50
private static final String METHOD_IS_SIGNED_IN = "isSignedIn" ;
48
51
private static final String METHOD_CLEAR_AUTH_CACHE = "clearAuthCache" ;
52
+ private static final String METHOD_REQUEST_SCOPES = "requestScopes" ;
49
53
50
54
private final IDelegate delegate ;
51
55
52
56
public static void registerWith (PluginRegistry .Registrar registrar ) {
53
57
final MethodChannel channel = new MethodChannel (registrar .messenger (), CHANNEL_NAME );
54
- final GoogleSignInPlugin instance = new GoogleSignInPlugin (registrar );
58
+ final GoogleSignInPlugin instance =
59
+ new GoogleSignInPlugin (registrar , new GoogleSignInWrapper ());
55
60
channel .setMethodCallHandler (instance );
56
61
}
57
62
58
- private GoogleSignInPlugin (PluginRegistry .Registrar registrar ) {
59
- delegate = new Delegate (registrar );
63
+ GoogleSignInPlugin (PluginRegistry .Registrar registrar , GoogleSignInWrapper googleSignInWrapper ) {
64
+ delegate = new Delegate (registrar , googleSignInWrapper );
60
65
}
61
66
62
67
@ Override
@@ -100,6 +105,11 @@ public void onMethodCall(MethodCall call, Result result) {
100
105
delegate .isSignedIn (result );
101
106
break ;
102
107
108
+ case METHOD_REQUEST_SCOPES :
109
+ List <String > scopes = call .argument ("scopes" );
110
+ delegate .requestScopes (result , scopes );
111
+ break ;
112
+
103
113
default :
104
114
result .notImplemented ();
105
115
}
@@ -153,6 +163,9 @@ public void init(
153
163
154
164
/** Checks if there is a signed in user. */
155
165
public void isSignedIn (Result result );
166
+
167
+ /** Prompts the user to grant an additional Oauth scopes. */
168
+ public void requestScopes (final Result result , final List <String > scopes );
156
169
}
157
170
158
171
/**
@@ -167,6 +180,7 @@ public void init(
167
180
public static final class Delegate implements IDelegate , PluginRegistry .ActivityResultListener {
168
181
private static final int REQUEST_CODE_SIGNIN = 53293 ;
169
182
private static final int REQUEST_CODE_RECOVER_AUTH = 53294 ;
183
+ @ VisibleForTesting static final int REQUEST_CODE_REQUEST_SCOPE = 53295 ;
170
184
171
185
private static final String ERROR_REASON_EXCEPTION = "exception" ;
172
186
private static final String ERROR_REASON_STATUS = "status" ;
@@ -183,13 +197,15 @@ public static final class Delegate implements IDelegate, PluginRegistry.Activity
183
197
184
198
private final PluginRegistry .Registrar registrar ;
185
199
private final BackgroundTaskRunner backgroundTaskRunner = new BackgroundTaskRunner (1 );
200
+ private final GoogleSignInWrapper googleSignInWrapper ;
186
201
187
202
private GoogleSignInClient signInClient ;
188
203
private List <String > requestedScopes ;
189
204
private PendingOperation pendingOperation ;
190
205
191
- public Delegate (PluginRegistry .Registrar registrar ) {
206
+ public Delegate (PluginRegistry .Registrar registrar , GoogleSignInWrapper googleSignInWrapper ) {
192
207
this .registrar = registrar ;
208
+ this .googleSignInWrapper = googleSignInWrapper ;
193
209
registrar .addActivityResultListener (this );
194
210
}
195
211
@@ -343,6 +359,37 @@ public void isSignedIn(final Result result) {
343
359
result .success (value );
344
360
}
345
361
362
+ @ Override
363
+ public void requestScopes (Result result , List <String > scopes ) {
364
+ checkAndSetPendingOperation (METHOD_REQUEST_SCOPES , result );
365
+
366
+ GoogleSignInAccount account = googleSignInWrapper .getLastSignedInAccount (registrar .context ());
367
+ if (account == null ) {
368
+ result .error (ERROR_REASON_SIGN_IN_REQUIRED , "No account to grant scopes." , null );
369
+ return ;
370
+ }
371
+
372
+ List <Scope > wrappedScopes = new ArrayList <>();
373
+
374
+ for (String scope : scopes ) {
375
+ Scope wrappedScope = new Scope (scope );
376
+ if (!googleSignInWrapper .hasPermissions (account , wrappedScope )) {
377
+ wrappedScopes .add (wrappedScope );
378
+ }
379
+ }
380
+
381
+ if (wrappedScopes .isEmpty ()) {
382
+ result .success (true );
383
+ return ;
384
+ }
385
+
386
+ googleSignInWrapper .requestPermissions (
387
+ registrar .activity (),
388
+ REQUEST_CODE_REQUEST_SCOPE ,
389
+ account ,
390
+ wrappedScopes .toArray (new Scope [0 ]));
391
+ }
392
+
346
393
private void onSignInResult (Task <GoogleSignInAccount > completedTask ) {
347
394
try {
348
395
GoogleSignInAccount account = completedTask .getResult (ApiException .class );
@@ -527,9 +574,37 @@ public boolean onActivityResult(int requestCode, int resultCode, Intent data) {
527
574
finishWithError (ERROR_REASON_SIGN_IN_FAILED , "Signin failed" );
528
575
}
529
576
return true ;
577
+ case REQUEST_CODE_REQUEST_SCOPE :
578
+ finishWithSuccess (resultCode == Activity .RESULT_OK );
579
+ return true ;
530
580
default :
531
581
return false ;
532
582
}
533
583
}
534
584
}
535
585
}
586
+
587
+ /**
588
+ * A wrapper object that calls static method in GoogleSignIn.
589
+ *
590
+ * <p>Because GoogleSignIn uses static method mostly, which is hard for unit testing. We use this
591
+ * wrapper class to use instance method which calls the corresponding GoogleSignIn static methods.
592
+ *
593
+ * <p>Warning! This class should stay true that each method calls a GoogleSignIn static method with
594
+ * the same name and same parameters.
595
+ */
596
+ class GoogleSignInWrapper {
597
+
598
+ GoogleSignInAccount getLastSignedInAccount (Context context ) {
599
+ return GoogleSignIn .getLastSignedInAccount (context );
600
+ }
601
+
602
+ boolean hasPermissions (GoogleSignInAccount account , Scope scope ) {
603
+ return GoogleSignIn .hasPermissions (account , scope );
604
+ }
605
+
606
+ void requestPermissions (
607
+ Activity activity , int requestCode , GoogleSignInAccount account , Scope [] scopes ) {
608
+ GoogleSignIn .requestPermissions (activity , requestCode , account , scopes );
609
+ }
610
+ }
0 commit comments