Skip to content

Commit f5f9113

Browse files
authored
[google_sign_in] Add Android account name field as optional (implementation package changes) (#8805)
Implementation package changes for [PR 8573](#8573). This PR adds support for a new passthrough field to explicitly set an account name while signing in for Android devices. Flutter issue: [163257](flutter/flutter#163257)
1 parent 08427b0 commit f5f9113

File tree

20 files changed

+167
-16
lines changed

20 files changed

+167
-16
lines changed

packages/google_sign_in/google_sign_in_android/CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
## 6.2.0
2+
3+
* Adds a sign-in field to allow clients to explicitly specify an account name.
4+
15
## 6.1.36
26

37
* Updates compileSdk 34 to flutter.compileSdkVersion.

packages/google_sign_in/google_sign_in_android/android/src/main/java/io/flutter/plugins/googlesignin/GoogleSignInPlugin.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -255,6 +255,11 @@ public void init(@NonNull Messages.InitParams params) {
255255
optionsBuilder.setHostedDomain(params.getHostedDomain());
256256
}
257257

258+
String forceAccountName = params.getForceAccountName();
259+
if (!isNullOrEmpty(forceAccountName)) {
260+
optionsBuilder.setAccountName(forceAccountName);
261+
}
262+
258263
signInClient = googleSignInWrapper.getClient(context, optionsBuilder.build());
259264
} catch (Exception e) {
260265
throw new FlutterError(ERROR_REASON_EXCEPTION, e.getMessage(), null);

packages/google_sign_in/google_sign_in_android/android/src/main/java/io/flutter/plugins/googlesignin/Messages.java

Lines changed: 44 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,16 @@ public void setForceCodeForRefreshToken(@NonNull Boolean setterArg) {
156156
this.forceCodeForRefreshToken = setterArg;
157157
}
158158

159+
private @Nullable String forceAccountName;
160+
161+
public @Nullable String getForceAccountName() {
162+
return forceAccountName;
163+
}
164+
165+
public void setForceAccountName(@Nullable String setterArg) {
166+
this.forceAccountName = setterArg;
167+
}
168+
159169
/** Constructor is non-public to enforce null safety; use Builder. */
160170
InitParams() {}
161171

@@ -173,13 +183,20 @@ public boolean equals(Object o) {
173183
&& Objects.equals(hostedDomain, that.hostedDomain)
174184
&& Objects.equals(clientId, that.clientId)
175185
&& Objects.equals(serverClientId, that.serverClientId)
176-
&& forceCodeForRefreshToken.equals(that.forceCodeForRefreshToken);
186+
&& forceCodeForRefreshToken.equals(that.forceCodeForRefreshToken)
187+
&& Objects.equals(forceAccountName, that.forceAccountName);
177188
}
178189

179190
@Override
180191
public int hashCode() {
181192
return Objects.hash(
182-
scopes, signInType, hostedDomain, clientId, serverClientId, forceCodeForRefreshToken);
193+
scopes,
194+
signInType,
195+
hostedDomain,
196+
clientId,
197+
serverClientId,
198+
forceCodeForRefreshToken,
199+
forceAccountName);
183200
}
184201

185202
public static final class Builder {
@@ -232,6 +249,14 @@ public static final class Builder {
232249
return this;
233250
}
234251

252+
private @Nullable String forceAccountName;
253+
254+
@CanIgnoreReturnValue
255+
public @NonNull Builder setForceAccountName(@Nullable String setterArg) {
256+
this.forceAccountName = setterArg;
257+
return this;
258+
}
259+
235260
public @NonNull InitParams build() {
236261
InitParams pigeonReturn = new InitParams();
237262
pigeonReturn.setScopes(scopes);
@@ -240,19 +265,21 @@ public static final class Builder {
240265
pigeonReturn.setClientId(clientId);
241266
pigeonReturn.setServerClientId(serverClientId);
242267
pigeonReturn.setForceCodeForRefreshToken(forceCodeForRefreshToken);
268+
pigeonReturn.setForceAccountName(forceAccountName);
243269
return pigeonReturn;
244270
}
245271
}
246272

247273
@NonNull
248274
ArrayList<Object> toList() {
249-
ArrayList<Object> toListResult = new ArrayList<>(6);
275+
ArrayList<Object> toListResult = new ArrayList<>(7);
250276
toListResult.add(scopes);
251277
toListResult.add(signInType);
252278
toListResult.add(hostedDomain);
253279
toListResult.add(clientId);
254280
toListResult.add(serverClientId);
255281
toListResult.add(forceCodeForRefreshToken);
282+
toListResult.add(forceAccountName);
256283
return toListResult;
257284
}
258285

@@ -270,6 +297,8 @@ ArrayList<Object> toList() {
270297
pigeonResult.setServerClientId((String) serverClientId);
271298
Object forceCodeForRefreshToken = pigeonVar_list.get(5);
272299
pigeonResult.setForceCodeForRefreshToken((Boolean) forceCodeForRefreshToken);
300+
Object forceAccountName = pigeonVar_list.get(6);
301+
pigeonResult.setForceAccountName((String) forceAccountName);
273302
return pigeonResult;
274303
}
275304
}
@@ -512,6 +541,7 @@ public interface Result<T> {
512541
/** Failure case callback method for handling errors. */
513542
void error(@NonNull Throwable error);
514543
}
544+
515545
/** Asynchronous error handling return type for nullable API method returns. */
516546
public interface NullableResult<T> {
517547
/** Success case callback method for handling returns. */
@@ -520,6 +550,7 @@ public interface NullableResult<T> {
520550
/** Failure case callback method for handling errors. */
521551
void error(@NonNull Throwable error);
522552
}
553+
523554
/** Asynchronous error handling return type for void API method returns. */
524555
public interface VoidResult {
525556
/** Success case callback method for handling returns. */
@@ -528,33 +559,43 @@ public interface VoidResult {
528559
/** Failure case callback method for handling errors. */
529560
void error(@NonNull Throwable error);
530561
}
562+
531563
/** Generated interface from Pigeon that represents a handler of messages from Flutter. */
532564
public interface GoogleSignInApi {
533565
/** Initializes a sign in request with the given parameters. */
534566
void init(@NonNull InitParams params);
567+
535568
/** Starts a silent sign in. */
536569
void signInSilently(@NonNull Result<UserData> result);
570+
537571
/** Starts a sign in with user interaction. */
538572
void signIn(@NonNull Result<UserData> result);
573+
539574
/** Requests the access token for the current sign in. */
540575
void getAccessToken(
541576
@NonNull String email, @NonNull Boolean shouldRecoverAuth, @NonNull Result<String> result);
577+
542578
/** Signs out the current user. */
543579
void signOut(@NonNull VoidResult result);
580+
544581
/** Revokes scope grants to the application. */
545582
void disconnect(@NonNull VoidResult result);
583+
546584
/** Returns whether the user is currently signed in. */
547585
@NonNull
548586
Boolean isSignedIn();
587+
549588
/** Clears the authentication caching for the given token, requiring a new sign in. */
550589
void clearAuthCache(@NonNull String token);
590+
551591
/** Requests access to the given scopes. */
552592
void requestScopes(@NonNull List<String> scopes, @NonNull Result<Boolean> result);
553593

554594
/** The codec used by GoogleSignInApi. */
555595
static @NonNull MessageCodec<Object> getCodec() {
556596
return PigeonCodec.INSTANCE;
557597
}
598+
558599
/**
559600
* Sets up an instance of `GoogleSignInApi` to handle messages through the `binaryMessenger`.
560601
*/

packages/google_sign_in/google_sign_in_android/android/src/test/java/io/flutter/plugins/googlesignin/GoogleSignInTest.java

Lines changed: 55 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
import static org.mockito.Mockito.verify;
1111
import static org.mockito.Mockito.when;
1212

13+
import android.accounts.Account;
1314
import android.app.Activity;
1415
import android.content.Context;
1516
import android.content.Intent;
@@ -32,6 +33,8 @@
3233
import org.junit.Test;
3334
import org.mockito.ArgumentCaptor;
3435
import org.mockito.Mock;
36+
import org.mockito.MockedConstruction;
37+
import org.mockito.Mockito;
3538
import org.mockito.MockitoAnnotations;
3639
import org.mockito.Spy;
3740

@@ -284,6 +287,25 @@ public void init_PassesForceCodeForRefreshTokenTrueWithServerClientIdFromResourc
284287
initAndAssertForceCodeForRefreshToken(params, true);
285288
}
286289

290+
@Test
291+
public void init_PassesForceAccountName() {
292+
String fakeAccountName = "[email protected]";
293+
294+
try (MockedConstruction<Account> mocked =
295+
Mockito.mockConstruction(
296+
Account.class,
297+
(mock, context) -> {
298+
when(mock.toString()).thenReturn(fakeAccountName);
299+
})) {
300+
InitParams params = buildInitParams("fakeClientId", "fakeServerClientId2", fakeAccountName);
301+
302+
initAndAssertForceAccountName(params, fakeAccountName);
303+
304+
List<Account> constructed = mocked.constructed();
305+
Assert.assertEquals(1, constructed.size());
306+
}
307+
}
308+
287309
public void initAndAssertServerClientId(InitParams params, String serverClientId) {
288310
ArgumentCaptor<GoogleSignInOptions> optionsCaptor =
289311
ArgumentCaptor.forClass(GoogleSignInOptions.class);
@@ -304,9 +326,23 @@ public void initAndAssertForceCodeForRefreshToken(
304326
forceCodeForRefreshToken, optionsCaptor.getValue().isForceCodeForRefreshToken());
305327
}
306328

329+
public void initAndAssertForceAccountName(InitParams params, String forceAccountName) {
330+
ArgumentCaptor<GoogleSignInOptions> optionsCaptor =
331+
ArgumentCaptor.forClass(GoogleSignInOptions.class);
332+
when(mockGoogleSignIn.getClient(any(Context.class), optionsCaptor.capture()))
333+
.thenReturn(mockClient);
334+
plugin.init(params);
335+
Assert.assertEquals(forceAccountName, optionsCaptor.getValue().getAccount().toString());
336+
}
337+
307338
private static InitParams buildInitParams(String clientId, String serverClientId) {
308339
return buildInitParams(
309-
Messages.SignInType.STANDARD, Collections.emptyList(), clientId, serverClientId, false);
340+
Messages.SignInType.STANDARD,
341+
Collections.emptyList(),
342+
clientId,
343+
serverClientId,
344+
false,
345+
null);
310346
}
311347

312348
private static InitParams buildInitParams(
@@ -316,15 +352,28 @@ private static InitParams buildInitParams(
316352
Collections.emptyList(),
317353
clientId,
318354
serverClientId,
319-
forceCodeForRefreshToken);
355+
forceCodeForRefreshToken,
356+
null);
357+
}
358+
359+
private static InitParams buildInitParams(
360+
String clientId, String serverClientId, String forceAccountName) {
361+
return buildInitParams(
362+
Messages.SignInType.STANDARD,
363+
Collections.emptyList(),
364+
clientId,
365+
serverClientId,
366+
false,
367+
forceAccountName);
320368
}
321369

322370
private static InitParams buildInitParams(
323371
Messages.SignInType signInType,
324372
List<String> scopes,
325373
String clientId,
326374
String serverClientId,
327-
boolean forceCodeForRefreshToken) {
375+
boolean forceCodeForRefreshToken,
376+
String forceAccountName) {
328377
InitParams.Builder builder = new InitParams.Builder();
329378
builder.setSignInType(signInType);
330379
builder.setScopes(scopes);
@@ -335,6 +384,9 @@ private static InitParams buildInitParams(
335384
builder.setServerClientId(serverClientId);
336385
}
337386
builder.setForceCodeForRefreshToken(forceCodeForRefreshToken);
387+
if (forceAccountName != null) {
388+
builder.setForceAccountName(forceAccountName);
389+
}
338390
return builder.build();
339391
}
340392
}

packages/google_sign_in/google_sign_in_android/example/pubspec.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ dependencies:
1616
# The example app is bundled with the plugin so we use a path dependency on
1717
# the parent directory to use the current plugin's version.
1818
path: ../
19-
google_sign_in_platform_interface: ^2.2.0
19+
google_sign_in_platform_interface: ^2.5.0
2020
http: ">=0.13.0 <2.0.0"
2121

2222
dev_dependencies:

packages/google_sign_in/google_sign_in_android/lib/google_sign_in_android.dart

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,12 +29,14 @@ class GoogleSignInAndroid extends GoogleSignInPlatform {
2929
SignInOption signInOption = SignInOption.standard,
3030
String? hostedDomain,
3131
String? clientId,
32+
String? forceAccountName,
3233
}) {
3334
return initWithParams(SignInInitParameters(
3435
signInOption: signInOption,
3536
scopes: scopes,
3637
hostedDomain: hostedDomain,
3738
clientId: clientId,
39+
forceAccountName: forceAccountName,
3840
));
3941
}
4042

@@ -47,6 +49,7 @@ class GoogleSignInAndroid extends GoogleSignInPlatform {
4749
clientId: params.clientId,
4850
serverClientId: params.serverClientId,
4951
forceCodeForRefreshToken: params.forceCodeForRefreshToken,
52+
forceAccountName: params.forceAccountName,
5053
));
5154
}
5255

packages/google_sign_in/google_sign_in_android/lib/src/messages.g.dart

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ class InitParams {
3838
this.clientId,
3939
this.serverClientId,
4040
this.forceCodeForRefreshToken = false,
41+
this.forceAccountName,
4142
});
4243

4344
List<String> scopes;
@@ -52,6 +53,8 @@ class InitParams {
5253

5354
bool forceCodeForRefreshToken;
5455

56+
String? forceAccountName;
57+
5558
Object encode() {
5659
return <Object?>[
5760
scopes,
@@ -60,6 +63,7 @@ class InitParams {
6063
clientId,
6164
serverClientId,
6265
forceCodeForRefreshToken,
66+
forceAccountName,
6367
];
6468
}
6569

@@ -72,6 +76,7 @@ class InitParams {
7276
clientId: result[3] as String?,
7377
serverClientId: result[4] as String?,
7478
forceCodeForRefreshToken: result[5]! as bool,
79+
forceAccountName: result[6] as String?,
7580
);
7681
}
7782
}

packages/google_sign_in/google_sign_in_android/pigeons/messages.dart

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ class InitParams {
3333
this.clientId,
3434
this.serverClientId,
3535
this.forceCodeForRefreshToken = false,
36+
this.forceAccountName,
3637
});
3738

3839
final List<String> scopes;
@@ -41,6 +42,7 @@ class InitParams {
4142
final String? clientId;
4243
final String? serverClientId;
4344
final bool forceCodeForRefreshToken;
45+
final String? forceAccountName;
4446
}
4547

4648
/// Pigeon version of GoogleSignInUserData.

packages/google_sign_in/google_sign_in_android/pubspec.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ name: google_sign_in_android
22
description: Android implementation of the google_sign_in plugin.
33
repository: https://github.com/flutter/packages/tree/main/packages/google_sign_in/google_sign_in_android
44
issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+google_sign_in%22
5-
version: 6.1.36
5+
version: 6.2.0
66

77
environment:
88
sdk: ^3.6.0
@@ -20,7 +20,7 @@ flutter:
2020
dependencies:
2121
flutter:
2222
sdk: flutter
23-
google_sign_in_platform_interface: ^2.2.0
23+
google_sign_in_platform_interface: ^2.5.0
2424

2525
dev_dependencies:
2626
build_runner: ^2.3.0

packages/google_sign_in/google_sign_in_android/test/google_sign_in_android_test.dart

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,7 @@ void main() {
143143
clientId: 'fakeClientId',
144144
serverClientId: 'fakeServerClientId',
145145
forceCodeForRefreshToken: true,
146+
forceAccountName: '[email protected]',
146147
);
147148

148149
await googleSignIn.initWithParams(initParams);
@@ -156,6 +157,7 @@ void main() {
156157
expect(passedParams.serverClientId, initParams.serverClientId);
157158
expect(passedParams.forceCodeForRefreshToken,
158159
initParams.forceCodeForRefreshToken);
160+
expect(passedParams.forceAccountName, initParams.forceAccountName);
159161
});
160162

161163
test('clearAuthCache passes arguments', () async {

packages/google_sign_in/google_sign_in_ios/CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
## 5.8.1
2+
3+
* Asserts that new `forceAccountName` parameter is null (not used in iOS).
4+
15
## 5.8.0
26

37
* Updates minimum supported SDK version to Flutter 3.22/Dart 3.4.

0 commit comments

Comments
 (0)