capacitor-native-google-one-tap-signin 0.2.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (56) hide show
  1. package/CapacitorNativeGoogleOneTapSignin.podspec +16 -15
  2. package/README.md +61 -5
  3. package/android/build.gradle +3 -2
  4. package/android/src/main/java/com/proventask/capacitor/GoogleOneTapAuth/GoogleOneTapAuth.java +137 -67
  5. package/dist/docs.json +268 -0
  6. package/dist/esm/GoogleOneTapAuth.d.ts +13 -0
  7. package/dist/esm/GoogleOneTapAuth.js +72 -0
  8. package/dist/esm/GoogleOneTapAuth.js.map +1 -0
  9. package/dist/esm/definitions.d.ts +45 -42
  10. package/dist/esm/definitions.js.map +1 -1
  11. package/dist/esm/helpers.d.ts +2 -0
  12. package/dist/esm/helpers.js +15 -0
  13. package/dist/esm/helpers.js.map +1 -0
  14. package/dist/esm/index.d.ts +1 -2
  15. package/dist/esm/index.js +1 -4
  16. package/dist/esm/index.js.map +1 -1
  17. package/dist/esm/web.d.ts +9 -13
  18. package/dist/esm/web.js +47 -63
  19. package/dist/esm/web.js.map +1 -1
  20. package/dist/plugin.cjs.js +232 -0
  21. package/dist/plugin.cjs.js.map +1 -0
  22. package/dist/plugin.js +233 -0
  23. package/dist/plugin.js.map +1 -0
  24. package/package.json +60 -34
  25. package/android/.gradle/7.3.3/checksums/checksums.lock +0 -0
  26. package/android/.gradle/7.3.3/dependencies-accessors/dependencies-accessors.lock +0 -0
  27. package/android/.gradle/7.3.3/dependencies-accessors/gc.properties +0 -0
  28. package/android/.gradle/7.3.3/executionHistory/executionHistory.bin +0 -0
  29. package/android/.gradle/7.3.3/executionHistory/executionHistory.lock +0 -0
  30. package/android/.gradle/7.3.3/fileChanges/last-build.bin +0 -0
  31. package/android/.gradle/7.3.3/fileHashes/fileHashes.bin +0 -0
  32. package/android/.gradle/7.3.3/fileHashes/fileHashes.lock +0 -0
  33. package/android/.gradle/7.3.3/fileHashes/resourceHashesCache.bin +0 -0
  34. package/android/.gradle/7.3.3/gc.properties +0 -0
  35. package/android/.gradle/buildOutputCleanup/buildOutputCleanup.lock +0 -0
  36. package/android/.gradle/buildOutputCleanup/cache.properties +0 -2
  37. package/android/.gradle/buildOutputCleanup/outputFiles.bin +0 -0
  38. package/android/.gradle/vcs-1/gc.properties +0 -0
  39. package/android/.idea/workspace.xml +0 -89
  40. package/android/gradle/wrapper/gradle-wrapper.jar +0 -0
  41. package/android/gradle/wrapper/gradle-wrapper.properties +0 -6
  42. package/android/gradle.properties +0 -16
  43. package/android/gradlew +0 -160
  44. package/android/gradlew.bat +0 -90
  45. package/android/proguard-rules.pro +0 -21
  46. package/android/settings.gradle +0 -2
  47. package/android/src/androidTest/java/com/getcapacitor/android/ExampleInstrumentedTest.java +0 -26
  48. package/android/src/test/java/com/getcapacitor/ExampleUnitTest.java +0 -17
  49. package/ios/Plugin.xcodeproj/project.pbxproj +0 -565
  50. package/ios/Plugin.xcodeproj/project.xcworkspace/contents.xcworkspacedata +0 -7
  51. package/ios/Plugin.xcworkspace/contents.xcworkspacedata +0 -10
  52. package/ios/Plugin.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +0 -8
  53. package/ios/PluginTests/Info.plist +0 -22
  54. package/ios/PluginTests/PluginTests.swift +0 -35
  55. package/ios/Podfile +0 -23
  56. package/ios/Podfile.lock +0 -47
@@ -1,16 +1,17 @@
1
+ require 'json'
1
2
 
2
- Pod::Spec.new do |s|
3
- s.name = 'CapacitorNativeGoogleOneTapSignin'
4
- s.version = '0.0.1'
5
- s.summary = 'Google One Tap sign-in plugin for capacitor.'
6
- s.license = 'MIT'
7
- s.homepage = 'https://github.com/inouiw/capacitor-native-google-one-tap-signin'
8
- s.author = 'David Neuy'
9
- s.source = { :git => 'https://github.com/inouiw/capacitor-native-google-one-tap-signin.git', :tag => s.version.to_s }
10
- s.source_files = 'ios/Plugin/**/*.{swift,h,m,c,cc,mm,cpp}'
11
- s.ios.deployment_target = '12.0'
12
- s.dependency 'Capacitor'
13
- s.dependency 'GoogleSignIn', '~> 6.2.4'
14
- s.static_framework = true,
15
- s.swift_version = '5.1'
16
- end
3
+ package = JSON.parse(File.read(File.join(__dir__, 'package.json')))
4
+
5
+ Pod::Spec.new do |s|
6
+ s.name = 'CapacitorNativeGoogleOneTapSignin'
7
+ s.version = package['version']
8
+ s.summary = package['description']
9
+ s.license = package['license']
10
+ s.homepage = package['repository']['url']
11
+ s.author = package['author']
12
+ s.source = { :git => package['repository']['url'], :tag => s.version.to_s }
13
+ s.source_files = 'ios/Plugin/**/*.{swift,h,m,c,cc,mm,cpp}'
14
+ s.ios.deployment_target = '13.0'
15
+ s.dependency 'Capacitor'
16
+ s.swift_version = '5.1'
17
+ end
package/README.md CHANGED
@@ -2,17 +2,21 @@
2
2
 
3
3
  Wraps the native android One Tap api for ionic capacitor apps. One Tap Sign In is part of Sign in with Google and offers features as automatic sign-in on return visits.
4
4
 
5
+ This library intends to provide the best google authentication experience for each platform.
6
+
5
7
  <img src="screenshots/one-tap-sign-in-demo-initial-sign-in.jpg" alt="One tap signin screenshot initial sign-in" width=250/>
6
8
  <img src="screenshots/one-tap-sign-in-demo-sign-back-in.jpg" alt="One tap signin screenshot sign back in" width=250/>
7
9
  <img src="screenshots/one-tap-sign-in-demo-without-filter-by-authorized-acounts.jpg" alt="One tap signin screenshot without filter by authorized accounts" width=250/>
8
10
 
9
11
 
10
- Currently the idToken, id (e-mail), displayName, givenName, familyName, profilePictureUri are returned. You may check the idToken contents on https://jwt.io/.
12
+ After a successful authentication, the idToken is returned as base64 and as object.
11
13
 
12
14
  The user is automatically signed-in, without a prompt after the first sign-in.
13
15
 
14
- API version 29 or higher seems to be required for one-tap sign-in to work.
16
+ The andorid minSdkVersion is 24, however the one-tap library seems to require API version 29. For lower API versions GoogleSignIn is used.
15
17
 
18
+ # Supported platforms
19
+ Android, iOS, Web.
16
20
 
17
21
  # Demos
18
22
  See the `demo` folder.
@@ -20,7 +24,50 @@ See the `demo` folder.
20
24
  # Setup
21
25
  You need to provide the plug-in a client ID of type "Web application". However you will also need to create a client ID of type "Android" as stated in the [One Tap Get started docu](https://developers.google.cn/identity/one-tap/android/get-started) in the [Google Cloud Console](https://console.cloud.google.com/apis/dashboard).
22
26
 
23
- To test it using an emulator, you need to create an emulator with android play services and API version 29 or higher. See [my stackoverflow answer](https://stackoverflow.com/questions/71325279/missing-featurename-auth-api-credentials-begin-sign-in-version-6/75285717#75285717).
27
+ To test it using an emulator, you need to create an emulator with android play services and API version 24 or higher. See [my stackoverflow answer](https://stackoverflow.com/questions/71325279/missing-featurename-auth-api-credentials-begin-sign-in-version-6/75285717#75285717). For older API levels like 24, you may need to update Chrome after creating the emulator because the plugin targets JavaScript es2017.
28
+
29
+ # Usage recommendation
30
+
31
+ For web, it will auto-sign in even if the user signs out.
32
+ For android, after sign-out sill be able to add another google account.
33
+
34
+ # Detailed description of different use cases
35
+
36
+ See https://developers.google.com/identity/gsi/web/guides/features
37
+
38
+ # Exposed api
39
+ See `src/definitions.ts` for a complete definition.
40
+
41
+ ```TypeScript
42
+ /**
43
+ * For the web platform, starts pre-loading the google one tap JavaScript library.
44
+ * @param options
45
+ */
46
+ initialize(options: InitializeOptions): Promise<void>;
47
+ /**
48
+ * Tries to auto-sign in the user.
49
+ * If there is a single google account and that account has previously signed into the app, then that user is auto signed in. A short popover is displayed during sign-in.
50
+ * If there are multiple google accounts and more than one have previously signed into the app then a user selection screen is shown.
51
+ * If there is no active google session or if no user session has logged in previously in the app or if the user has opt out of One Tap, the auto sign-in will fail.
52
+ * See https://developers.google.com/identity/gsi/web/guides/features
53
+ */
54
+ tryAutoSignIn(): Promise<SignInResult>;
55
+ /**
56
+ *
57
+ * @param parentElementId
58
+ * @param options
59
+ * @param gsiButtonConfiguration Not all button configuration options are supported on android.
60
+ */
61
+ renderSignInButton(parentElementId: string, options: RenderSignInButtonOptions, gsiButtonConfiguration?: google.GsiButtonConfiguration): Promise<SignInResult>;
62
+ /**
63
+ * Ends the session.
64
+ */
65
+ signOut(): Promise<SignOutResult>;
66
+ /**
67
+ * Gets the last user defined or auto-created nonce.
68
+ */
69
+ getNonce(): string;
70
+ ```
24
71
 
25
72
  # Design decisions
26
73
  Promises will not be rejected for anticipated unsuccessful control flow. For example, if the one tap auto-login does not succeed with the reason `opt_out_or_no_session` then this is not exceptional but expected in many cases. Rejecting the promise would mean the caller would have to catch an exception when calling `await xy` and then run follow-up code in the catch block. Instead of rejecting promises the resolved promise contains a `isSuccess` property.
@@ -44,9 +91,18 @@ npx cap update
44
91
 
45
92
  ## Usage
46
93
 
47
- ### Android
94
+ ```TypeScript
95
+ import { GoogleOneTapAuth, SignInResult } from 'capacitor-native-google-one-tap-signin';
48
96
 
49
- todo
97
+ await GoogleOneTapAuth.initialize({ clientId: clientId });
98
+ let signInResult = await GoogleOneTapAuth.tryAutoSignIn();
99
+ if (!signInResult.isSuccess) {
100
+ signInResult = await GoogleOneTapAuth.renderSignInButton('appleid-signin', {}, { locale: 'en-us', theme: 'outline', text: 'continue_with', shape: 'rectangular' });
101
+ }
102
+ console.log(signInResult);
103
+
104
+ // See the demo folder for an example application.
105
+ ```
50
106
 
51
107
  ## License
52
108
 
@@ -4,7 +4,7 @@ buildscript {
4
4
  google()
5
5
  }
6
6
  dependencies {
7
- classpath 'com.android.tools.build:gradle:7.2.1'
7
+ classpath 'com.android.tools.build:gradle:7.4.0'
8
8
  classpath 'com.google.gms:google-services:4.3.13'
9
9
  }
10
10
  }
@@ -14,7 +14,7 @@ apply plugin: 'com.android.library'
14
14
  android {
15
15
  compileSdkVersion 31
16
16
  defaultConfig {
17
- minSdkVersion 29
17
+ minSdkVersion 24
18
18
  targetSdkVersion 31
19
19
  versionCode 1
20
20
  versionName "1.0"
@@ -51,5 +51,6 @@ dependencies {
51
51
  implementation 'com.google.android.gms:play-services-auth:20.4.1'
52
52
  implementation 'com.google.android.gms:play-services-tasks:18.0.2'
53
53
  implementation 'com.google.android.gms:play-services-base:18.0.1'
54
+ implementation 'org.json:json:20220924'
54
55
  }
55
56
 
@@ -14,39 +14,65 @@ import com.getcapacitor.JSObject;
14
14
  import com.getcapacitor.Plugin;
15
15
  import com.getcapacitor.PluginCall;
16
16
  import com.getcapacitor.PluginMethod;
17
+ import com.getcapacitor.annotation.ActivityCallback;
17
18
  import com.getcapacitor.annotation.CapacitorPlugin;
18
19
  import com.google.android.gms.auth.api.identity.BeginSignInRequest;
19
20
  import com.google.android.gms.auth.api.identity.Identity;
20
21
  import com.google.android.gms.auth.api.identity.SignInClient;
22
+ import com.google.android.gms.auth.api.signin.GoogleSignIn;
23
+ import com.google.android.gms.auth.api.signin.GoogleSignInAccount;
24
+ import com.google.android.gms.auth.api.signin.GoogleSignInClient;
25
+ import com.google.android.gms.auth.api.signin.GoogleSignInOptions;
26
+ import com.google.android.gms.auth.api.signin.GoogleSignInStatusCodes;
21
27
  import com.google.android.gms.common.ConnectionResult;
22
28
  import com.google.android.gms.common.GoogleApiAvailability;
23
29
  import com.google.android.gms.common.api.ApiException;
30
+ import com.google.android.gms.tasks.Task;
31
+
32
+ import org.json.JSONException;
33
+ import org.json.JSONObject;
24
34
 
25
- import java.util.Base64;
26
35
  import java.util.concurrent.CompletableFuture;
27
36
  import java.util.concurrent.ExecutionException;
28
37
  import java.util.concurrent.Future;
29
38
 
30
39
  @CapacitorPlugin()
31
40
  public class GoogleOneTapAuth extends Plugin {
41
+ private enum SignInMethod {NotSignedIn, OneTap, GoogleSignIn}
32
42
  private static final String TAG = "GoogleOneTapAuth Plugin";
33
43
  private String clientId;
44
+ private String nonce;
34
45
  private SignInClient oneTapClient;
46
+ private GoogleSignInClient googleSignInClient;
35
47
  private ActivityResultLauncher<IntentSenderRequest> googleOneTapSignInActivityResultHandlerIntentSenderRequest;
36
- private CompletableFuture<JSObject> signInFuture;
48
+ private CompletableFuture<JSObject> oneTapSignInFuture;
49
+ private CompletableFuture<JSObject> googleSignInFuture;
50
+ private SignInMethod signInMethod = SignInMethod.NotSignedIn;
37
51
 
38
52
  @Override
39
53
  public void load() {
40
- clientId = getConfig().getString("clientId");
54
+ registerSignInResultHandler();
55
+ }
41
56
 
42
- if (clientId == null || clientId.endsWith("apps.googleusercontent.com") == false) {
43
- throw new RuntimeException("clientId must end with 'apps.googleusercontent.com' but is: " + clientId + ". Check capacitor.config.ts.");
44
- }
57
+ @PluginMethod()
58
+ public void initialize(PluginCall call) {
59
+ initializeOptionsFromCall(call);
45
60
  oneTapClient = Identity.getSignInClient(this.getActivity());
46
- RegisterSignInResultHandler();
61
+
62
+ var gso = new GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
63
+ .requestEmail()
64
+ .requestIdToken(clientId)
65
+ .build();
66
+ googleSignInClient = GoogleSignIn.getClient(this.getActivity(), gso);
67
+ call.resolve();
47
68
  }
48
69
 
49
- private void RegisterSignInResultHandler() {
70
+ private void initializeOptionsFromCall(PluginCall call) {
71
+ clientId = call.getString("clientId");
72
+ nonce = call.getString("nonce");
73
+ }
74
+
75
+ private void registerSignInResultHandler() {
50
76
  googleOneTapSignInActivityResultHandlerIntentSenderRequest = bridge.registerForActivityResult(
51
77
  new ActivityResultContracts.StartIntentSenderForResult(),
52
78
  result -> {
@@ -56,59 +82,41 @@ public class GoogleOneTapAuth extends Plugin {
56
82
  try {
57
83
  Intent data = result.getData();
58
84
  var credential = oneTapClient.getSignInCredentialFromIntent(data);
59
- var email = credential.getId();
60
- signInFuture.complete(createSuccessResponse(credential.getGoogleIdToken(), email));
85
+ signInMethod = SignInMethod.OneTap;
86
+ oneTapSignInFuture.complete(createSuccessResponse(credential.getGoogleIdToken()));
61
87
  } catch (ApiException e) {
62
- signInFuture.complete(createErrorResponse(e.toString()));
88
+ oneTapSignInFuture.complete(createErrorResponse(null, e.toString()));
63
89
  }
64
90
  } else {
65
91
  var resultCodeString = ActivityResult.resultCodeToString(resultCode);
66
- signInFuture.complete(createErrorResponse(resultCodeString));
92
+ oneTapSignInFuture.complete(createErrorResponse(null, resultCodeString));
67
93
  }
68
94
  });
69
95
  }
70
96
 
71
- @PluginMethod()
72
- public void initialize(PluginCall call) {
73
- // Currently no code to run here.
74
- call.resolve();
75
- }
76
-
77
97
  @PluginMethod()
78
98
  public void tryAutoSignIn(PluginCall call) {
79
99
  try {
80
100
  var signInResult = beginSignIn(true).get();
101
+ if (!signInResult.getBool("isSuccess")) {
102
+ signInResult = beginSignIn(false).get();
103
+ }
104
+ if (!signInResult.getBool("isSuccess")) {
105
+ signInResult = googleSilentSignIn(call).get();
106
+ }
81
107
  call.resolve(signInResult);
82
- } catch (ExecutionException e) {
83
- call.reject(e.toString());
84
- } catch (InterruptedException e) {
85
- call.reject(e.toString());
86
- }
87
- }
88
-
89
- @PluginMethod()
90
- public void trySignInWithPrompt(PluginCall call) {
91
- try {
92
- var signInResult = beginSignIn(false).get();
93
- call.resolve(signInResult);
94
- } catch (ExecutionException e) {
95
- call.reject(e.toString());
96
- } catch (InterruptedException e) {
108
+ } catch (ExecutionException | InterruptedException e) {
97
109
  call.reject(e.toString());
98
110
  }
99
111
  }
100
112
 
113
+ // This method is not part of the api but only called from GoogleOneTapAuth.ts.
101
114
  @PluginMethod()
102
- public void tryAutoSignInThenTrySignInWithPrompt(PluginCall call) {
115
+ public void triggerGoogleSignIn(PluginCall call) {
103
116
  try {
104
- var signInResult = beginSignIn(true).get();
105
- if (!signInResult.getBool("isSuccess")) {
106
- signInResult = beginSignIn(false).get();
107
- }
117
+ var signInResult = googleSignIn(call).get();
108
118
  call.resolve(signInResult);
109
- } catch (ExecutionException e) {
110
- call.reject(e.toString());
111
- } catch (InterruptedException e) {
119
+ } catch (ExecutionException | InterruptedException e) {
112
120
  call.reject(e.toString());
113
121
  }
114
122
  }
@@ -116,7 +124,7 @@ public class GoogleOneTapAuth extends Plugin {
116
124
  private Future<JSObject> beginSignIn(boolean filterByAuthorizedAccounts) {
117
125
  var context = this.getContext();
118
126
  var beginSignInRequest = createBeginSignInRequest(filterByAuthorizedAccounts);
119
- signInFuture = new CompletableFuture<>();
127
+ oneTapSignInFuture = new CompletableFuture<>();
120
128
 
121
129
  oneTapClient.beginSignIn(beginSignInRequest)
122
130
  .addOnCompleteListener(task -> {
@@ -126,11 +134,7 @@ public class GoogleOneTapAuth extends Plugin {
126
134
  var intentSenderRequest = new IntentSenderRequest.Builder(intentSender).build();
127
135
  googleOneTapSignInActivityResultHandlerIntentSenderRequest.launch(intentSenderRequest);
128
136
  } else {
129
- if (filterByAuthorizedAccounts) {
130
- Log.i(TAG, "beginSignIn with FilterByAuthorizedAccounts=true failed", task.getException());
131
- } else {
132
- Log.e(TAG, "beginSignIn with FilterByAuthorizedAccounts=false failed", task.getException());
133
- }
137
+ Log.i(TAG, "beginSignIn with FilterByAuthorizedAccounts=" + filterByAuthorizedAccounts + " did not succeed.", task.getException());
134
138
  var errorMessage = task.getException().toString();
135
139
 
136
140
  if (!isGooglePlayServicesAvailable(context)) {
@@ -140,22 +144,23 @@ public class GoogleOneTapAuth extends Plugin {
140
144
  errorMessage += "\nGooglePlay is not installed or must be logged in to setup.";
141
145
  }
142
146
  if (errorMessage.contains("ApiException: 8")) {
143
- errorMessage += "\nOne reason for the exception is when the device has no internet.";
147
+ errorMessage += "\nOne reason for the exception is when the device has no internet, or one-tap is not possible.";
144
148
  }
145
149
  // Other errors:
146
150
  // com.google.android.gms.common.api.ApiException: 10: Caller not whitelisted to call this API.
147
151
  // --> Try if restarting the phone fixes the problem.
148
152
  // --> Test to sign in several times to ensure it works repeatedly.
149
- signInFuture.complete(createErrorResponse(errorMessage));
153
+ oneTapSignInFuture.complete(createErrorResponse(null, errorMessage));
150
154
  }
151
155
  });
152
- return signInFuture;
156
+ return oneTapSignInFuture;
153
157
  }
154
158
 
155
159
  private BeginSignInRequest createBeginSignInRequest(boolean filterByAuthorizedAccounts) {
156
160
  return BeginSignInRequest.builder()
157
161
  .setGoogleIdTokenRequestOptions(BeginSignInRequest.GoogleIdTokenRequestOptions.builder()
158
162
  .setSupported(true)
163
+ .setNonce(nonce)
159
164
  // A OAuth client ID with application type "Web application".
160
165
  .setServerClientId(clientId)
161
166
  // If true, only the Google accounts that the user has authorized before will show up in the credential list. This can
@@ -167,27 +172,38 @@ public class GoogleOneTapAuth extends Plugin {
167
172
  .build();
168
173
  }
169
174
 
170
- private JSObject createErrorResponse(String noSuccessAdditionalInfo) {
175
+ private JSObject createErrorResponse(String reasonCode, String noSuccessAdditionalInfo) {
171
176
  var result = new JSObject();
172
177
  result.put("isSuccess", false);
178
+ result.put("noSuccessReasonCode", reasonCode);
173
179
  result.put("noSuccessAdditionalInfo", noSuccessAdditionalInfo);
174
180
  return result;
175
181
  }
176
182
 
177
- private JSObject createSuccessResponse(String idToken, String email) {
183
+ private JSObject createSuccessResponse(String idToken) {
184
+ var decodedIdToken = decodeJwtBody(idToken);
185
+ JSONObject decodedIdTokenJson = null;
186
+ String userId = null;
187
+ String email = null;
188
+ try {
189
+ decodedIdTokenJson = new JSONObject(decodedIdToken);
190
+ userId = decodedIdTokenJson.getString("sub");
191
+ email = decodedIdTokenJson.getString("email");
192
+ } catch (JSONException e) {
193
+ // Do nothing.
194
+ }
178
195
  var result = new JSObject();
179
196
  result.put("isSuccess", true);
180
197
  result.put("idToken", idToken);
198
+ result.put("userId", userId);
181
199
  result.put("email", email);
182
- result.put("decodedIdToken", decodeJwtBody(idToken));
200
+ result.put("decodedIdToken", decodedIdTokenJson);
183
201
  return result;
184
202
  }
185
203
 
186
204
  private String decodeJwtBody(String jwtToken) {
187
- var decoder = Base64.getUrlDecoder();
188
205
  var parts = jwtToken.split("\\.");
189
- var payloadJson = new String(decoder.decode(parts[1]));
190
- return payloadJson;
206
+ return new String(android.util.Base64.decode(parts[1], android.util.Base64.DEFAULT));
191
207
  }
192
208
 
193
209
  private boolean isGooglePlayServicesAvailable(Context context) {
@@ -199,16 +215,70 @@ public class GoogleOneTapAuth extends Plugin {
199
215
  @PluginMethod()
200
216
  public void signOut(final PluginCall call) {
201
217
  oneTapClient.signOut()
202
- .addOnCompleteListener(task -> {
203
- var signOutResult = new JSObject();
204
- if (task.isSuccessful()) {
205
- signOutResult.put("isSuccess", true);
206
- } else {
207
- Log.e(TAG, "signOut failed", task.getException());
208
- signOutResult.put("isSuccess", false);
209
- signOutResult.put("error", task.getException().toString());
210
- }
211
- call.resolve(signOutResult);
212
- });
218
+ .addOnCompleteListener(task -> call.resolve(signOutResultTaskToJSObject(task)));
219
+ googleSignInClient.signOut()
220
+ .addOnCompleteListener(task -> call.resolve(signOutResultTaskToJSObject(task)));
221
+ }
222
+
223
+ private JSObject signOutResultTaskToJSObject(Task<Void> completedTask) {
224
+ var signOutResult = new JSObject();
225
+ if (completedTask.isSuccessful()) {
226
+ signOutResult.put("isSuccess", true);
227
+ } else {
228
+ Log.e(TAG, "signOut failed", completedTask.getException());
229
+ signOutResult.put("isSuccess", false);
230
+ signOutResult.put("error", completedTask.getException().toString());
231
+ }
232
+ return signOutResult;
233
+ }
234
+
235
+ private JSObject createSuccessResult() {
236
+ var jsObject = new JSObject();
237
+ jsObject.put("isSuccess", true);
238
+ return jsObject;
239
+ }
240
+
241
+ private Future<JSObject> googleSilentSignIn(final PluginCall call) {
242
+ googleSignInFuture = new CompletableFuture<>();
243
+ googleSignInClient.silentSignIn()
244
+ .addOnCompleteListener(task -> handleGoogleSignInResult(call, task, "silentSignIn"));
245
+ return googleSignInFuture;
246
+ }
247
+
248
+ private Future<JSObject> googleSignIn(final PluginCall call) {
249
+ googleSignInFuture = new CompletableFuture<>();
250
+ var intent = googleSignInClient.getSignInIntent();
251
+ startActivityForResult(call, intent, "googleSignInResultIntentCallback");
252
+ return googleSignInFuture;
253
+ }
254
+
255
+ @ActivityCallback
256
+ private void googleSignInResultIntentCallback(PluginCall call, ActivityResult result) {
257
+ if (call == null) {
258
+ return;
259
+ }
260
+ Intent data = result.getData();
261
+ var task = GoogleSignIn.getSignedInAccountFromIntent(data);
262
+ handleGoogleSignInResult(call, task, "GoogleSignInClient.getSignInIntent");
263
+ }
264
+
265
+ private void handleGoogleSignInResult(PluginCall call, Task<GoogleSignInAccount> completedTask, String triggerMethodName) {
266
+ try {
267
+ var account = completedTask.getResult(ApiException.class);
268
+ signInMethod = SignInMethod.GoogleSignIn;
269
+ Log.i(TAG, triggerMethodName + " result " + account.toString());
270
+ googleSignInFuture.complete(createSuccessResponse(account.getIdToken()));
271
+ } catch (ApiException e) {
272
+ int statusCode = e.getStatusCode();
273
+ var statusCodeString = GoogleSignInStatusCodes.getStatusCodeString(statusCode);
274
+ Log.i(TAG, triggerMethodName + " not successful, statusCodeString " + statusCodeString, e);
275
+ if (statusCode == GoogleSignInStatusCodes.SIGN_IN_REQUIRED
276
+ || statusCode == GoogleSignInStatusCodes.SIGN_IN_CANCELLED) {
277
+ googleSignInFuture.complete(createErrorResponse(statusCodeString, null));
278
+ }
279
+ else {
280
+ googleSignInFuture.completeExceptionally(e);
281
+ }
282
+ }
213
283
  }
214
284
  }