Skip to content
This repository was archived by the owner on Feb 22, 2023. It is now read-only.

Commit 1b5eae5

Browse files
authored
[google_sign_in] Port plugin to use the federated Platform Interface (#2266)
* Port google_sign_in.dart to use the federated Platform Interface, instead of MethodChannels. * Ignore google_sign_in_platform_interface on all-plugins-app call. * Refactor endlessly growing --exclude param value into a string that gets generated for a list of plugin names.
1 parent d22502a commit 1b5eae5

File tree

5 files changed

+89
-71
lines changed

5 files changed

+89
-71
lines changed

packages/google_sign_in/google_sign_in/CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
## 4.0.14
2+
3+
* Port plugin code to use the federated Platform Interface, instead of a MethodChannel directly.
4+
15
## 4.0.13
26

37
* Fix `GoogleUserCircleAvatar` to handle new style profile image URLs.

packages/google_sign_in/google_sign_in/lib/google_sign_in.dart

Lines changed: 70 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -5,38 +5,36 @@
55
import 'dart:async';
66
import '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

1111
import 'src/common.dart';
1212

1313
export 'src/common.dart';
1414
export 'widgets.dart';
1515

16-
enum SignInOption { standard, games }
17-
1816
class 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

3331
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 {
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
}

packages/google_sign_in/google_sign_in/pubspec.yaml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ description: Flutter plugin for Google Sign-In, a secure authentication system
33
for signing in with a Google account on Android and iOS.
44
author: Flutter Team <[email protected]>
55
homepage: https://github.com/flutter/plugins/tree/master/packages/google_sign_in/google_sign_in
6-
version: 4.0.13
6+
version: 4.0.14
77

88
flutter:
99
plugin:
@@ -12,6 +12,7 @@ flutter:
1212
pluginClass: GoogleSignInPlugin
1313

1414
dependencies:
15+
google_sign_in_platform_interface: ^1.0.0
1516
flutter:
1617
sdk: flutter
1718
meta: ^1.0.4

packages/google_sign_in/google_sign_in/test/google_sign_in_test.dart

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import 'dart:async';
66

77
import 'package:flutter/services.dart';
88
import 'package:flutter_test/flutter_test.dart';
9+
import 'package:google_sign_in_platform_interface/google_sign_in_platform_interface.dart';
910
import 'package:google_sign_in/google_sign_in.dart';
1011
import 'package:google_sign_in/testing.dart';
1112

@@ -391,7 +392,9 @@ void main() {
391392
GoogleSignIn googleSignIn;
392393

393394
setUp(() {
394-
GoogleSignIn.channel.setMockMethodCallHandler(
395+
final MethodChannelGoogleSignIn platformInstance =
396+
GoogleSignInPlatform.instance;
397+
platformInstance.channel.setMockMethodCallHandler(
395398
(FakeSignInBackend()..user = kUserData).handleMethodCall);
396399
googleSignIn = GoogleSignIn();
397400
});

script/build_all_plugins_app.sh

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,15 @@ readonly REPO_DIR="$(dirname "$SCRIPT_DIR")"
1010
source "$SCRIPT_DIR/common.sh"
1111
check_changed_packages > /dev/null
1212

13-
(cd "$REPO_DIR" && pub global run flutter_plugin_tools all-plugins-app --exclude instrumentation_adapter,url_launcher_platform_interface)
13+
readonly EXCLUDED_PLUGINS_LIST=(
14+
"instrumentation_adapter"
15+
"url_launcher_platform_interface"
16+
"google_sign_in_platform_interface"
17+
)
18+
# Comma-separated string of the list above
19+
readonly EXCLUDED=$(IFS=, ; echo "${EXCLUDED_PLUGINS_LIST[*]}")
20+
21+
(cd "$REPO_DIR" && pub global run flutter_plugin_tools all-plugins-app --exclude $EXCLUDED)
1422

1523
function error() {
1624
echo "$@" 1>&2

0 commit comments

Comments
 (0)