Skip to content

Commit 2e27aaf

Browse files
[google_sign_in] Use an activity for credential requests (#9497)
`getCredentialManager` is documented to require an activity context, not an application context; this fixes it to use the activity context since in some configurations using the application context will throw an error. Fixes flutter/flutter#171122 ## Pre-Review Checklist [^1]: Regular contributors who have demonstrated familiarity with the repository guidelines only need to comment if the PR is not auto-exempted by repo tooling.
1 parent 0f80260 commit 2e27aaf

File tree

8 files changed

+91
-20
lines changed

8 files changed

+91
-20
lines changed

packages/google_sign_in/google_sign_in_android/CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
## 7.0.1
2+
3+
* Passes an activity context when requesting credentials, fixing an issue that
4+
prevented signing in on some devices.
5+
16
## 7.0.0
27

38
* **BREAKING CHANGE**: Switches to implementing version 3.0 of the platform

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

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,17 @@ public void getCredential(
219219
return;
220220
}
221221

222+
// getCredentialAsync requires an acitivity context, not an application context, per
223+
// the API docs.
224+
Activity activity = getActivity();
225+
if (activity == null) {
226+
ResultUtilsKt.completeWithGetCredentialFailure(
227+
callback,
228+
new GetCredentialFailure(
229+
GetCredentialFailureType.NO_ACTIVITY, "No activity available", null));
230+
return;
231+
}
232+
222233
String nonce = params.getNonce();
223234
GetCredentialRequest.Builder requestBuilder = new GetCredentialRequest.Builder();
224235
if (params.getUseButtonFlow()) {
@@ -243,7 +254,7 @@ public void getCredential(
243254

244255
CredentialManager credentialManager = credentialManagerFactory.create(context);
245256
credentialManager.getCredentialAsync(
246-
context,
257+
activity,
247258
requestBuilder.build(),
248259
null,
249260
Executors.newSingleThreadExecutor(),

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

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -48,18 +48,23 @@ enum class GetCredentialFailureType(val raw: Int) {
4848
UNEXPECTED_CREDENTIAL_TYPE(0),
4949
/** Indicates that a server client ID was not provided. */
5050
MISSING_SERVER_CLIENT_ID(1),
51+
/**
52+
* Indicates that the user needs to be prompted for authorization, but there is no current
53+
* activity to prompt in.
54+
*/
55+
NO_ACTIVITY(2),
5156
/** The request was internally interrupted. */
52-
INTERRUPTED(2),
57+
INTERRUPTED(3),
5358
/** The request was canceled by the user. */
54-
CANCELED(3),
59+
CANCELED(4),
5560
/** No matching credential was found. */
56-
NO_CREDENTIAL(4),
61+
NO_CREDENTIAL(5),
5762
/** The provider was not properly configured. */
58-
PROVIDER_CONFIGURATION_ISSUE(5),
63+
PROVIDER_CONFIGURATION_ISSUE(6),
5964
/** The credential manager is not supported on this device. */
60-
UNSUPPORTED(6),
65+
UNSUPPORTED(7),
6166
/** The request failed for an unknown reason. */
62-
UNKNOWN(7);
67+
UNKNOWN(8);
6368

6469
companion object {
6570
fun ofRaw(raw: Int): GetCredentialFailureType? {

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

Lines changed: 52 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,7 @@ public void getCredential_returnsAuthenticationInfo() {
191191
when(mockGoogleCredential.getIdToken()).thenReturn(idToken);
192192

193193
final Boolean[] callbackCalled = new Boolean[1];
194+
plugin.setActivity(mockActivity);
194195
plugin.getCredential(
195196
params,
196197
ResultCompat.asCompatCallback(
@@ -214,7 +215,7 @@ public void getCredential_returnsAuthenticationInfo() {
214215
callbackCaptor = ArgumentCaptor.forClass(CredentialManagerCallback.class);
215216
verify(mockCredentialManager)
216217
.getCredentialAsync(
217-
eq(mockContext),
218+
eq(mockActivity),
218219
any(GetCredentialRequest.class),
219220
any(),
220221
any(),
@@ -233,6 +234,7 @@ public void getCredential_usesGetSignInWithGoogleOptionForButtonFlow() {
233234
"serverClientId",
234235
null);
235236

237+
plugin.setActivity(mockActivity);
236238
plugin.getCredential(
237239
params,
238240
ResultCompat.asCompatCallback(
@@ -245,7 +247,7 @@ public void getCredential_usesGetSignInWithGoogleOptionForButtonFlow() {
245247
ArgumentCaptor<GetCredentialRequest> captor =
246248
ArgumentCaptor.forClass(GetCredentialRequest.class);
247249
verify(mockCredentialManager)
248-
.getCredentialAsync(eq(mockContext), captor.capture(), any(), any(), any());
250+
.getCredentialAsync(eq(mockActivity), captor.capture(), any(), any(), any());
249251

250252
assertEquals(1, captor.getValue().getCredentialOptions().size());
251253
assertTrue(
@@ -261,6 +263,7 @@ public void getCredential_usesGetGoogleIdOptionForNonButtonFlow() {
261263
"serverClientId",
262264
null);
263265

266+
plugin.setActivity(mockActivity);
264267
plugin.getCredential(
265268
params,
266269
ResultCompat.asCompatCallback(
@@ -274,7 +277,7 @@ public void getCredential_usesGetGoogleIdOptionForNonButtonFlow() {
274277
ArgumentCaptor<GetCredentialRequest> captor =
275278
ArgumentCaptor.forClass(GetCredentialRequest.class);
276279
verify(mockCredentialManager)
277-
.getCredentialAsync(eq(mockContext), captor.capture(), any(), any(), any());
280+
.getCredentialAsync(eq(mockActivity), captor.capture(), any(), any(), any());
278281

279282
assertEquals(1, captor.getValue().getCredentialOptions().size());
280283
assertTrue(captor.getValue().getCredentialOptions().get(0) instanceof GetGoogleIdOption);
@@ -290,6 +293,7 @@ public void getCredential_passesNonceInButtonFlow() {
290293
"serverClientId",
291294
nonce);
292295

296+
plugin.setActivity(mockActivity);
293297
plugin.getCredential(
294298
params,
295299
ResultCompat.asCompatCallback(
@@ -303,7 +307,7 @@ public void getCredential_passesNonceInButtonFlow() {
303307
ArgumentCaptor<GetCredentialRequest> captor =
304308
ArgumentCaptor.forClass(GetCredentialRequest.class);
305309
verify(mockCredentialManager)
306-
.getCredentialAsync(eq(mockContext), captor.capture(), any(), any(), any());
310+
.getCredentialAsync(eq(mockActivity), captor.capture(), any(), any(), any());
307311

308312
assertEquals(1, captor.getValue().getCredentialOptions().size());
309313
assertEquals(
@@ -321,6 +325,7 @@ public void getCredential_passesNonceInNonButtonFlow() {
321325
"serverClientId",
322326
nonce);
323327

328+
plugin.setActivity(mockActivity);
324329
plugin.getCredential(
325330
params,
326331
ResultCompat.asCompatCallback(
@@ -334,20 +339,48 @@ public void getCredential_passesNonceInNonButtonFlow() {
334339
ArgumentCaptor<GetCredentialRequest> captor =
335340
ArgumentCaptor.forClass(GetCredentialRequest.class);
336341
verify(mockCredentialManager)
337-
.getCredentialAsync(eq(mockContext), captor.capture(), any(), any(), any());
342+
.getCredentialAsync(eq(mockActivity), captor.capture(), any(), any(), any());
338343

339344
assertEquals(1, captor.getValue().getCredentialOptions().size());
340345
assertEquals(
341346
nonce, ((GetGoogleIdOption) captor.getValue().getCredentialOptions().get(0)).getNonce());
342347
}
343348

349+
@Test
350+
public void getCredential_reportsMissingActivity() {
351+
GetCredentialRequestParams params =
352+
new GetCredentialRequestParams(
353+
false,
354+
new GetCredentialRequestGoogleIdOptionParams(false, false),
355+
"serverClientId",
356+
null);
357+
358+
final Boolean[] callbackCalled = new Boolean[1];
359+
plugin.setActivity(null);
360+
plugin.getCredential(
361+
params,
362+
ResultCompat.asCompatCallback(
363+
reply -> {
364+
callbackCalled[0] = true;
365+
// This failure is a structured return value, not an exception.
366+
assertTrue(reply.isSuccess());
367+
GetCredentialResult result = reply.getOrNull();
368+
assertTrue(result instanceof GetCredentialFailure);
369+
GetCredentialFailure failure = (GetCredentialFailure) result;
370+
assertEquals(GetCredentialFailureType.NO_ACTIVITY, failure.getType());
371+
return null;
372+
}));
373+
assertTrue(callbackCalled[0]);
374+
}
375+
344376
@Test
345377
public void getCredential_reportsMissingServerClientId() {
346378
GetCredentialRequestParams params =
347379
new GetCredentialRequestParams(
348380
false, new GetCredentialRequestGoogleIdOptionParams(false, false), null, null);
349381

350382
final Boolean[] callbackCalled = new Boolean[1];
383+
plugin.setActivity(mockActivity);
351384
plugin.getCredential(
352385
params,
353386
ResultCompat.asCompatCallback(
@@ -374,6 +407,7 @@ public void getCredential_reportsWrongCredentialType() {
374407
null);
375408

376409
final Boolean[] callbackCalled = new Boolean[1];
410+
plugin.setActivity(mockActivity);
377411
plugin.getCredential(
378412
params,
379413
ResultCompat.asCompatCallback(
@@ -393,7 +427,7 @@ public void getCredential_reportsWrongCredentialType() {
393427
callbackCaptor = ArgumentCaptor.forClass(CredentialManagerCallback.class);
394428
verify(mockCredentialManager)
395429
.getCredentialAsync(
396-
eq(mockContext),
430+
eq(mockActivity),
397431
any(GetCredentialRequest.class),
398432
any(),
399433
any(),
@@ -417,6 +451,7 @@ public void getCredential_reportsCancellation() {
417451
null);
418452

419453
final Boolean[] callbackCalled = new Boolean[1];
454+
plugin.setActivity(mockActivity);
420455
plugin.getCredential(
421456
params,
422457
ResultCompat.asCompatCallback(
@@ -436,7 +471,7 @@ public void getCredential_reportsCancellation() {
436471
callbackCaptor = ArgumentCaptor.forClass(CredentialManagerCallback.class);
437472
verify(mockCredentialManager)
438473
.getCredentialAsync(
439-
eq(mockContext),
474+
eq(mockActivity),
440475
any(GetCredentialRequest.class),
441476
any(),
442477
any(),
@@ -456,6 +491,7 @@ public void getCredential_reportsInterrupted() {
456491
null);
457492

458493
final Boolean[] callbackCalled = new Boolean[1];
494+
plugin.setActivity(mockActivity);
459495
plugin.getCredential(
460496
params,
461497
ResultCompat.asCompatCallback(
@@ -475,7 +511,7 @@ public void getCredential_reportsInterrupted() {
475511
callbackCaptor = ArgumentCaptor.forClass(CredentialManagerCallback.class);
476512
verify(mockCredentialManager)
477513
.getCredentialAsync(
478-
eq(mockContext),
514+
eq(mockActivity),
479515
any(GetCredentialRequest.class),
480516
any(),
481517
any(),
@@ -495,6 +531,7 @@ public void getCredential_reportsProviderConfigurationIssue() {
495531
null);
496532

497533
final Boolean[] callbackCalled = new Boolean[1];
534+
plugin.setActivity(mockActivity);
498535
plugin.getCredential(
499536
params,
500537
ResultCompat.asCompatCallback(
@@ -515,7 +552,7 @@ public void getCredential_reportsProviderConfigurationIssue() {
515552
callbackCaptor = ArgumentCaptor.forClass(CredentialManagerCallback.class);
516553
verify(mockCredentialManager)
517554
.getCredentialAsync(
518-
eq(mockContext),
555+
eq(mockActivity),
519556
any(GetCredentialRequest.class),
520557
any(),
521558
any(),
@@ -535,6 +572,7 @@ public void getCredential_reportsUnsupported() {
535572
null);
536573

537574
final Boolean[] callbackCalled = new Boolean[1];
575+
plugin.setActivity(mockActivity);
538576
plugin.getCredential(
539577
params,
540578
ResultCompat.asCompatCallback(
@@ -554,7 +592,7 @@ public void getCredential_reportsUnsupported() {
554592
callbackCaptor = ArgumentCaptor.forClass(CredentialManagerCallback.class);
555593
verify(mockCredentialManager)
556594
.getCredentialAsync(
557-
eq(mockContext),
595+
eq(mockActivity),
558596
any(GetCredentialRequest.class),
559597
any(),
560598
any(),
@@ -574,6 +612,7 @@ public void getCredential_reportsNoCredential() {
574612
null);
575613

576614
final Boolean[] callbackCalled = new Boolean[1];
615+
plugin.setActivity(mockActivity);
577616
plugin.getCredential(
578617
params,
579618
ResultCompat.asCompatCallback(
@@ -593,7 +632,7 @@ public void getCredential_reportsNoCredential() {
593632
callbackCaptor = ArgumentCaptor.forClass(CredentialManagerCallback.class);
594633
verify(mockCredentialManager)
595634
.getCredentialAsync(
596-
eq(mockContext),
635+
eq(mockActivity),
597636
any(GetCredentialRequest.class),
598637
any(),
599638
any(),
@@ -613,6 +652,7 @@ public void getCredential_reportsUnknown() {
613652
null);
614653

615654
final Boolean[] callbackCalled = new Boolean[1];
655+
plugin.setActivity(mockActivity);
616656
plugin.getCredential(
617657
params,
618658
ResultCompat.asCompatCallback(
@@ -632,7 +672,7 @@ public void getCredential_reportsUnknown() {
632672
callbackCaptor = ArgumentCaptor.forClass(CredentialManagerCallback.class);
633673
verify(mockCredentialManager)
634674
.getCredentialAsync(
635-
eq(mockContext),
675+
eq(mockActivity),
636676
any(GetCredentialRequest.class),
637677
any(),
638678
any(),

packages/google_sign_in/google_sign_in_android/lib/google_sign_in_android.dart

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,8 @@ class GoogleSignInAndroid extends GoogleSignInPlatform {
164164
// distinct code.
165165
code = GoogleSignInExceptionCode.providerConfigurationError;
166166
message = 'Unexpected credential type: $message';
167+
case GetCredentialFailureType.noActivity:
168+
code = GoogleSignInExceptionCode.uiUnavailable;
167169
case GetCredentialFailureType.interrupted:
168170
code = GoogleSignInExceptionCode.interrupted;
169171
case GetCredentialFailureType.providerConfigurationIssue:

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,10 @@ enum GetCredentialFailureType {
2626
/// Indicates that a server client ID was not provided.
2727
missingServerClientId,
2828

29+
/// Indicates that the user needs to be prompted for authorization, but there
30+
/// is no current activity to prompt in.
31+
noActivity,
32+
2933
/// The request was internally interrupted.
3034
interrupted,
3135

packages/google_sign_in/google_sign_in_android/pigeons/messages.dart

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,10 @@ enum GetCredentialFailureType {
8181
/// Indicates that a server client ID was not provided.
8282
missingServerClientId,
8383

84+
/// Indicates that the user needs to be prompted for authorization, but there
85+
/// is no current activity to prompt in.
86+
noActivity,
87+
8488
// Types from https://developer.android.com/reference/android/credentials/GetCredentialException
8589
/// The request was internally interrupted.
8690
interrupted,

packages/google_sign_in/google_sign_in_android/pubspec.yaml

Lines changed: 1 addition & 1 deletion
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: 7.0.0
5+
version: 7.0.1
66

77
environment:
88
sdk: ^3.6.0

0 commit comments

Comments
 (0)