5
5
import 'dart:async' ;
6
6
import 'dart:ui' show hashValues;
7
7
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' ;
10
10
11
11
import 'src/common.dart' ;
12
12
13
13
export 'src/common.dart' ;
14
14
export 'widgets.dart' ;
15
15
16
- enum SignInOption { standard, games }
17
-
18
16
class GoogleSignInAuthentication {
19
17
GoogleSignInAuthentication ._(this ._data);
20
18
21
- final Map < String , dynamic > _data;
19
+ final GoogleSignInTokenData _data;
22
20
23
21
/// An OpenID Connect ID token that identifies the user.
24
- String get idToken => _data[ ' idToken' ] ;
22
+ String get idToken => _data. idToken;
25
23
26
24
/// The OAuth2 access token to access Google services.
27
- String get accessToken => _data[ ' accessToken' ] ;
25
+ String get accessToken => _data. accessToken;
28
26
29
27
@override
30
28
String toString () => 'GoogleSignInAuthentication:$_data ' ;
31
29
}
32
30
33
31
class 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 {
40
38
assert (id != null );
41
39
}
42
40
@@ -78,18 +76,16 @@ class GoogleSignInAccount implements GoogleIdentity {
78
76
throw StateError ('User is no longer signed in.' );
79
77
}
80
78
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 ,
88
83
);
84
+
89
85
// On Android, there isn't an API for refreshing the idToken, so re-use
90
86
// the one we obtained on login.
91
- if (response[ ' idToken' ] == null ) {
92
- response[ ' idToken' ] = _idToken;
87
+ if (response. idToken == null ) {
88
+ response. idToken = _idToken;
93
89
}
94
90
return GoogleSignInAuthentication ._(response);
95
91
}
@@ -108,10 +104,7 @@ class GoogleSignInAccount implements GoogleIdentity {
108
104
/// this method and grab `authHeaders` once again.
109
105
Future <void > clearAuthCache () async {
110
106
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);
115
108
}
116
109
117
110
@override
@@ -146,7 +139,7 @@ class GoogleSignIn {
146
139
/// Initializes global sign-in configuration settings.
147
140
///
148
141
/// The [signInOption] determines the user experience. [SigninOption.games]
149
- /// must not be used on iOS .
142
+ /// is only supported on Android .
150
143
///
151
144
/// The list of [scopes] are OAuth scope codes to request when signing in.
152
145
/// These scope codes will determine the level of data access that is granted
@@ -157,18 +150,25 @@ class GoogleSignIn {
157
150
/// The [hostedDomain] argument specifies a hosted domain restriction. By
158
151
/// setting this, sign in will be restricted to accounts of the user in the
159
152
/// 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
+ });
161
158
162
159
/// 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
+ }) {
164
164
return GoogleSignIn (
165
165
signInOption: SignInOption .standard,
166
166
scopes: scopes,
167
167
hostedDomain: hostedDomain);
168
168
}
169
169
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 .
172
172
factory GoogleSignIn .games () {
173
173
return GoogleSignIn (signInOption: SignInOption .games);
174
174
}
@@ -186,13 +186,8 @@ class GoogleSignIn {
186
186
/// Error code indicating that attempt to sign in failed.
187
187
static const String kSignInFailedError = 'sign_in_failed' ;
188
188
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.
196
191
final SignInOption signInOption;
197
192
198
193
/// The list of [scopes] are OAuth scope codes requested when signing in.
@@ -211,12 +206,12 @@ class GoogleSignIn {
211
206
// Future that completes when we've finished calling `init` on the native side
212
207
Future <void > _initialization;
213
208
214
- Future <GoogleSignInAccount > _callMethod (String method) async {
209
+ Future <GoogleSignInAccount > _callMethod (Function method) async {
215
210
await _ensureInitialized ();
216
211
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
220
215
? GoogleSignInAccount ._(this , response)
221
216
: null );
222
217
}
@@ -230,16 +225,14 @@ class GoogleSignIn {
230
225
}
231
226
232
227
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
+ });
243
236
}
244
237
245
238
/// The most recently scheduled method call.
@@ -251,6 +244,7 @@ class GoogleSignIn {
251
244
final Completer <void > completer = Completer <void >();
252
245
future.whenComplete (completer.complete).catchError ((dynamic _) {
253
246
// Ignore if previous call completed with an error.
247
+ // TODO: Should we log errors here, if debug or similar?
254
248
});
255
249
return completer.future;
256
250
}
@@ -259,26 +253,29 @@ class GoogleSignIn {
259
253
///
260
254
/// At most one in flight call is allowed to prevent concurrent (out of order)
261
255
/// 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 {
263
264
Future <GoogleSignInAccount > response;
264
265
if (_lastMethodCall == null ) {
265
266
response = _callMethod (method);
266
267
} else {
267
268
response = _lastMethodCall.then ((_) {
268
269
// 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
270
271
// 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 ) {
276
273
return _currentUser;
277
- } else {
278
- return _callMethod (method);
279
274
}
275
+ return _callMethod (method);
280
276
});
281
277
}
278
+ // Add the current response to the currently running Promise of all pending responses
282
279
_lastMethodCall = _waitFor (response);
283
280
return response;
284
281
}
@@ -303,10 +300,12 @@ class GoogleSignIn {
303
300
/// returned Future completes with [PlatformException] whose `code` can be
304
301
/// either [kSignInRequiredError] (when there is no authenticated user) or
305
302
/// [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 {
308
306
try {
309
- return await _addMethodCall ('signInSilently' );
307
+ return await _addMethodCall (GoogleSignInPlatform .instance.signInSilently,
308
+ canSkipCall: true );
310
309
} catch (_) {
311
310
if (suppressErrors) {
312
311
return null ;
@@ -319,7 +318,7 @@ class GoogleSignIn {
319
318
/// Returns a future that resolves to whether a user is currently signed in.
320
319
Future <bool > isSignedIn () async {
321
320
await _ensureInitialized ();
322
- return await channel. invokeMethod < bool >( ' isSignedIn' );
321
+ return GoogleSignInPlatform .instance. isSignedIn ( );
323
322
}
324
323
325
324
/// Starts the interactive sign-in process.
@@ -333,16 +332,19 @@ class GoogleSignIn {
333
332
///
334
333
/// Re-authentication can be triggered only after [signOut] or [disconnect] .
335
334
Future <GoogleSignInAccount > signIn () {
336
- final Future <GoogleSignInAccount > result = _addMethodCall ('signIn' );
335
+ final Future <GoogleSignInAccount > result =
336
+ _addMethodCall (GoogleSignInPlatform .instance.signIn, canSkipCall: true );
337
337
bool isCanceled (dynamic error) =>
338
338
error is PlatformException && error.code == kSignInCanceledError;
339
339
return result.catchError ((dynamic _) => null , test: isCanceled);
340
340
}
341
341
342
342
/// 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);
344
345
345
346
/// Disconnects the current user from the app and revokes previous
346
347
/// authentication.
347
- Future <GoogleSignInAccount > disconnect () => _addMethodCall ('disconnect' );
348
+ Future <GoogleSignInAccount > disconnect () =>
349
+ _addMethodCall (GoogleSignInPlatform .instance.disconnect);
348
350
}
0 commit comments