55import 'dart:async' ;
66import 'dart:ui' show hashValues;
77
8- import 'package:flutter/services.dart' show MethodChannel, PlatformException;
9- import 'package:meta/meta .dart' show visibleForTesting ;
8+ import 'package:flutter/services.dart' show PlatformException;
9+ import 'package:google_sign_in_platform_interface/google_sign_in_platform_interface .dart' ;
1010
1111import 'src/common.dart' ;
1212
1313export 'src/common.dart' ;
1414export 'widgets.dart' ;
1515
16- enum SignInOption { standard, games }
17-
1816class GoogleSignInAuthentication {
1917 GoogleSignInAuthentication ._(this ._data);
2018
21- final Map < String , dynamic > _data;
19+ final GoogleSignInTokenData _data;
2220
2321 /// An OpenID Connect ID token that identifies the user.
24- String get idToken => _data[ ' idToken' ] ;
22+ String get idToken => _data. idToken;
2523
2624 /// The OAuth2 access token to access Google services.
27- String get accessToken => _data[ ' accessToken' ] ;
25+ String get accessToken => _data. accessToken;
2826
2927 @override
3028 String toString () => 'GoogleSignInAuthentication:$_data ' ;
3129}
3230
3331class GoogleSignInAccount implements GoogleIdentity {
34- GoogleSignInAccount ._(this ._googleSignIn, Map < String , dynamic > data)
35- : displayName = data[ ' displayName' ] ,
36- email = data[ ' email' ] ,
37- id = data[ 'id' ] ,
38- photoUrl = data[ ' photoUrl' ] ,
39- _idToken = data[ ' idToken' ] {
32+ GoogleSignInAccount ._(this ._googleSignIn, GoogleSignInUserData data)
33+ : displayName = data. displayName,
34+ email = data. email,
35+ id = data.id ,
36+ photoUrl = data. photoUrl,
37+ _idToken = data. idToken {
4038 assert (id != null );
4139 }
4240
@@ -78,18 +76,16 @@ class GoogleSignInAccount implements GoogleIdentity {
7876 throw StateError ('User is no longer signed in.' );
7977 }
8078
81- final Map <String , dynamic > response =
82- await GoogleSignIn .channel.invokeMapMethod <String , dynamic >(
83- 'getTokens' ,
84- < String , dynamic > {
85- 'email' : email,
86- 'shouldRecoverAuth' : true ,
87- },
79+ final GoogleSignInTokenData response =
80+ await GoogleSignInPlatform .instance.getTokens (
81+ email: email,
82+ shouldRecoverAuth: true ,
8883 );
84+
8985 // On Android, there isn't an API for refreshing the idToken, so re-use
9086 // the one we obtained on login.
91- if (response[ ' idToken' ] == null ) {
92- response[ ' idToken' ] = _idToken;
87+ if (response. idToken == null ) {
88+ response. idToken = _idToken;
9389 }
9490 return GoogleSignInAuthentication ._(response);
9591 }
@@ -108,10 +104,7 @@ class GoogleSignInAccount implements GoogleIdentity {
108104 /// this method and grab `authHeaders` once again.
109105 Future <void > clearAuthCache () async {
110106 final String token = (await authentication).accessToken;
111- await GoogleSignIn .channel.invokeMethod <void >(
112- 'clearAuthCache' ,
113- < String , dynamic > {'token' : token},
114- );
107+ await GoogleSignInPlatform .instance.clearAuthCache (token: token);
115108 }
116109
117110 @override
@@ -146,7 +139,7 @@ class GoogleSignIn {
146139 /// Initializes global sign-in configuration settings.
147140 ///
148141 /// The [signInOption] determines the user experience. [SigninOption.games]
149- /// must not be used on iOS .
142+ /// is only supported on Android .
150143 ///
151144 /// The list of [scopes] are OAuth scope codes to request when signing in.
152145 /// These scope codes will determine the level of data access that is granted
@@ -157,18 +150,25 @@ class GoogleSignIn {
157150 /// The [hostedDomain] argument specifies a hosted domain restriction. By
158151 /// setting this, sign in will be restricted to accounts of the user in the
159152 /// specified domain. By default, the list of accounts will not be restricted.
160- GoogleSignIn ({this .signInOption, this .scopes, this .hostedDomain});
153+ GoogleSignIn ({
154+ this .signInOption = SignInOption .standard,
155+ this .scopes = const < String > [],
156+ this .hostedDomain,
157+ });
161158
162159 /// Factory for creating default sign in user experience.
163- factory GoogleSignIn .standard ({List <String > scopes, String hostedDomain}) {
160+ factory GoogleSignIn .standard ({
161+ List <String > scopes = const < String > [],
162+ String hostedDomain,
163+ }) {
164164 return GoogleSignIn (
165165 signInOption: SignInOption .standard,
166166 scopes: scopes,
167167 hostedDomain: hostedDomain);
168168 }
169169
170- /// Factory for creating sign in suitable for games. This option must not be
171- /// used on iOS because the games API is not supported .
170+ /// Factory for creating sign in suitable for games. This option is only
171+ /// supported on Android .
172172 factory GoogleSignIn .games () {
173173 return GoogleSignIn (signInOption: SignInOption .games);
174174 }
@@ -186,13 +186,8 @@ class GoogleSignIn {
186186 /// Error code indicating that attempt to sign in failed.
187187 static const String kSignInFailedError = 'sign_in_failed' ;
188188
189- /// The [MethodChannel] over which this class communicates.
190- @visibleForTesting
191- static const MethodChannel channel =
192- MethodChannel ('plugins.flutter.io/google_sign_in' );
193-
194- /// Option to determine the sign in user experience. [SignInOption.games] must
195- /// not be used on iOS.
189+ /// Option to determine the sign in user experience. [SignInOption.games] is
190+ /// only supported on Android.
196191 final SignInOption signInOption;
197192
198193 /// The list of [scopes] are OAuth scope codes requested when signing in.
@@ -211,12 +206,12 @@ class GoogleSignIn {
211206 // Future that completes when we've finished calling `init` on the native side
212207 Future <void > _initialization;
213208
214- Future <GoogleSignInAccount > _callMethod (String method) async {
209+ Future <GoogleSignInAccount > _callMethod (Function method) async {
215210 await _ensureInitialized ();
216211
217- final Map < String , dynamic > response =
218- await channel. invokeMapMethod < String , dynamic >(method);
219- return _setCurrentUser (response != null && response.isNotEmpty
212+ final dynamic response = await method ();
213+
214+ return _setCurrentUser (response != null && response is GoogleSignInUserData
220215 ? GoogleSignInAccount ._(this , response)
221216 : null );
222217 }
@@ -230,16 +225,14 @@ class GoogleSignIn {
230225 }
231226
232227 Future <void > _ensureInitialized () {
233- return _initialization ?? =
234- channel.invokeMethod <void >('init' , < String , dynamic > {
235- 'signInOption' : (signInOption ?? SignInOption .standard).toString (),
236- 'scopes' : scopes ?? < String > [],
237- 'hostedDomain' : hostedDomain,
238- })
239- ..catchError ((dynamic _) {
240- // Invalidate initialization if it errored out.
241- _initialization = null ;
242- });
228+ return _initialization ?? = GoogleSignInPlatform .instance.init (
229+ signInOption: signInOption,
230+ scopes: scopes,
231+ hostedDomain: hostedDomain,
232+ )..catchError ((dynamic _) {
233+ // Invalidate initialization if it errors out.
234+ _initialization = null ;
235+ });
243236 }
244237
245238 /// The most recently scheduled method call.
@@ -251,6 +244,7 @@ class GoogleSignIn {
251244 final Completer <void > completer = Completer <void >();
252245 future.whenComplete (completer.complete).catchError ((dynamic _) {
253246 // Ignore if previous call completed with an error.
247+ // TODO: Should we log errors here, if debug or similar?
254248 });
255249 return completer.future;
256250 }
@@ -259,26 +253,29 @@ class GoogleSignIn {
259253 ///
260254 /// At most one in flight call is allowed to prevent concurrent (out of order)
261255 /// updates to [currentUser] and [onCurrentUserChanged] .
262- Future <GoogleSignInAccount > _addMethodCall (String method) async {
256+ ///
257+ /// The optional, named parameter [canSkipCall] lets the plugin know that the
258+ /// method call may be skipped, if there's already [_currentUser] information.
259+ /// This is used from the [signIn] and [signInSilently] methods.
260+ Future <GoogleSignInAccount > _addMethodCall (
261+ Function method, {
262+ bool canSkipCall = false ,
263+ }) async {
263264 Future <GoogleSignInAccount > response;
264265 if (_lastMethodCall == null ) {
265266 response = _callMethod (method);
266267 } else {
267268 response = _lastMethodCall.then ((_) {
268269 // If after the last completed call `currentUser` is not `null` and requested
269- // method is a sign in method , re-use the same authenticated user
270+ // method can be skipped (`canSkipCall`) , re-use the same authenticated user
270271 // instead of making extra call to the native side.
271- const List <String > kSignInMethods = < String > [
272- 'signIn' ,
273- 'signInSilently'
274- ];
275- if (kSignInMethods.contains (method) && _currentUser != null ) {
272+ if (canSkipCall && _currentUser != null ) {
276273 return _currentUser;
277- } else {
278- return _callMethod (method);
279274 }
275+ return _callMethod (method);
280276 });
281277 }
278+ // Add the current response to the currently running Promise of all pending responses
282279 _lastMethodCall = _waitFor (response);
283280 return response;
284281 }
@@ -303,10 +300,12 @@ class GoogleSignIn {
303300 /// returned Future completes with [PlatformException] whose `code` can be
304301 /// either [kSignInRequiredError] (when there is no authenticated user) or
305302 /// [kSignInFailedError] (when an unknown error occurred).
306- Future <GoogleSignInAccount > signInSilently (
307- {bool suppressErrors = true }) async {
303+ Future <GoogleSignInAccount > signInSilently ({
304+ bool suppressErrors = true ,
305+ }) async {
308306 try {
309- return await _addMethodCall ('signInSilently' );
307+ return await _addMethodCall (GoogleSignInPlatform .instance.signInSilently,
308+ canSkipCall: true );
310309 } catch (_) {
311310 if (suppressErrors) {
312311 return null ;
@@ -319,7 +318,7 @@ class GoogleSignIn {
319318 /// Returns a future that resolves to whether a user is currently signed in.
320319 Future <bool > isSignedIn () async {
321320 await _ensureInitialized ();
322- return await channel. invokeMethod < bool >( ' isSignedIn' );
321+ return GoogleSignInPlatform .instance. isSignedIn ( );
323322 }
324323
325324 /// Starts the interactive sign-in process.
@@ -333,16 +332,19 @@ class GoogleSignIn {
333332 ///
334333 /// Re-authentication can be triggered only after [signOut] or [disconnect] .
335334 Future <GoogleSignInAccount > signIn () {
336- final Future <GoogleSignInAccount > result = _addMethodCall ('signIn' );
335+ final Future <GoogleSignInAccount > result =
336+ _addMethodCall (GoogleSignInPlatform .instance.signIn, canSkipCall: true );
337337 bool isCanceled (dynamic error) =>
338338 error is PlatformException && error.code == kSignInCanceledError;
339339 return result.catchError ((dynamic _) => null , test: isCanceled);
340340 }
341341
342342 /// Marks current user as being in the signed out state.
343- Future <GoogleSignInAccount > signOut () => _addMethodCall ('signOut' );
343+ Future <GoogleSignInAccount > signOut () =>
344+ _addMethodCall (GoogleSignInPlatform .instance.signOut);
344345
345346 /// Disconnects the current user from the app and revokes previous
346347 /// authentication.
347- Future <GoogleSignInAccount > disconnect () => _addMethodCall ('disconnect' );
348+ Future <GoogleSignInAccount > disconnect () =>
349+ _addMethodCall (GoogleSignInPlatform .instance.disconnect);
348350}
0 commit comments