Skip to content

Commit f6b2266

Browse files
[google_sign_in] Adopt task queues for Android (#8622)
Replaces manual offloading of some method implementations to a background task queue with just using the new method channel task queue system to have those methods be called on a background queue automatically. This simplifies the code, and also allows removing the Guava dependency with a couple of other minor changes: - Replaces `Joiner` with Java 8's `String.join`. - Replaces `Strings.isEmptyOrNull` with a local helper method. Fixes flutter/flutter#13991
1 parent e9598f2 commit f6b2266

File tree

9 files changed

+74
-220
lines changed

9 files changed

+74
-220
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.1.35
2+
3+
* Removes the dependency on the Guava library.
4+
15
## 6.1.34
26

37
* Removes unnecessary native code.

packages/google_sign_in/google_sign_in_android/android/build.gradle

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,6 @@ android {
5757

5858
dependencies {
5959
implementation 'com.google.android.gms:play-services-auth:21.0.0'
60-
implementation 'com.google.guava:guava:33.3.1-android'
6160
testImplementation 'junit:junit:4.13.2'
6261
testImplementation 'org.mockito:mockito-inline:5.0.0'
6362
}

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

Lines changed: 0 additions & 85 deletions
This file was deleted.

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

Lines changed: 0 additions & 36 deletions
This file was deleted.

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

Lines changed: 51 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99
import android.app.Activity;
1010
import android.content.Context;
1111
import android.content.Intent;
12+
import android.os.Handler;
13+
import android.os.Looper;
1214
import android.util.Log;
1315
import androidx.annotation.NonNull;
1416
import androidx.annotation.Nullable;
@@ -25,8 +27,6 @@
2527
import com.google.android.gms.common.api.Scope;
2628
import com.google.android.gms.tasks.RuntimeExecutionException;
2729
import com.google.android.gms.tasks.Task;
28-
import com.google.common.base.Joiner;
29-
import com.google.common.base.Strings;
3030
import io.flutter.embedding.engine.plugins.FlutterPlugin;
3131
import io.flutter.embedding.engine.plugins.activity.ActivityAware;
3232
import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding;
@@ -37,8 +37,6 @@
3737
import java.util.ArrayList;
3838
import java.util.List;
3939
import java.util.Objects;
40-
import java.util.concurrent.Callable;
41-
import java.util.concurrent.ExecutionException;
4240

4341
/** Google sign-in plugin for Flutter. */
4442
public class GoogleSignInPlugin implements FlutterPlugin, ActivityAware {
@@ -135,8 +133,6 @@ public static class Delegate implements PluginRegistry.ActivityResultListener, G
135133
private final @NonNull Context context;
136134
// Only set activity for v2 embedder. Always access activity from getActivity() method.
137135
private @Nullable Activity activity;
138-
// TODO(stuartmorgan): See whether this can be replaced with background channels.
139-
private final BackgroundTaskRunner backgroundTaskRunner = new BackgroundTaskRunner(1);
140136
private final GoogleSignInWrapper googleSignInWrapper;
141137

142138
private GoogleSignInClient signInClient;
@@ -224,15 +220,15 @@ public void init(@NonNull Messages.InitParams params) {
224220
// https://developers.google.com/android/guides/client-auth
225221
// https://developers.google.com/identity/sign-in/android/start#configure-a-google-api-project
226222
String serverClientId = params.getServerClientId();
227-
if (!Strings.isNullOrEmpty(params.getClientId()) && Strings.isNullOrEmpty(serverClientId)) {
223+
if (!isNullOrEmpty(params.getClientId()) && isNullOrEmpty(serverClientId)) {
228224
Log.w(
229225
"google_sign_in",
230226
"clientId is not supported on Android and is interpreted as serverClientId. "
231227
+ "Use serverClientId instead to suppress this warning.");
232228
serverClientId = params.getClientId();
233229
}
234230

235-
if (Strings.isNullOrEmpty(serverClientId)) {
231+
if (isNullOrEmpty(serverClientId)) {
236232
// Only requests a clientId if google-services.json was present and parsed
237233
// by the google-services Gradle script.
238234
// TODO(jackson): Perhaps we should provide a mechanism to override this
@@ -246,7 +242,7 @@ public void init(@NonNull Messages.InitParams params) {
246242
serverClientId = context.getString(webClientIdIdentifier);
247243
}
248244
}
249-
if (!Strings.isNullOrEmpty(serverClientId)) {
245+
if (!isNullOrEmpty(serverClientId)) {
250246
optionsBuilder.requestIdToken(serverClientId);
251247
optionsBuilder.requestServerAuthCode(
252248
serverClientId, params.getForceCodeForRefreshToken());
@@ -255,7 +251,7 @@ public void init(@NonNull Messages.InitParams params) {
255251
for (String scope : requestedScopes) {
256252
optionsBuilder.requestScopes(new Scope(scope));
257253
}
258-
if (!Strings.isNullOrEmpty(params.getHostedDomain())) {
254+
if (!isNullOrEmpty(params.getHostedDomain())) {
259255
optionsBuilder.setHostedDomain(params.getHostedDomain());
260256
}
261257

@@ -450,6 +446,10 @@ private void finishWithError(String errorCode, String errorMessage) {
450446
pendingOperation = null;
451447
}
452448

449+
private static boolean isNullOrEmpty(@Nullable String s) {
450+
return s == null || s.isEmpty();
451+
}
452+
453453
private static class PendingOperation {
454454
final @NonNull String method;
455455
final @Nullable Messages.Result<Messages.UserData> userDataResult;
@@ -478,37 +478,26 @@ private static class PendingOperation {
478478
}
479479
}
480480

481-
/** Clears the token kept in the client side cache. */
481+
/**
482+
* Clears the token kept in the client side cache.
483+
*
484+
* <p>Runs on a background task queue.
485+
*/
482486
@Override
483-
public void clearAuthCache(@NonNull String token, @NonNull Messages.VoidResult result) {
484-
Callable<Void> clearTokenTask =
485-
() -> {
486-
GoogleAuthUtil.clearToken(context, token);
487-
return null;
488-
};
489-
490-
backgroundTaskRunner.runInBackground(
491-
clearTokenTask,
492-
clearTokenFuture -> {
493-
try {
494-
clearTokenFuture.get();
495-
result.success();
496-
} catch (ExecutionException e) {
497-
@Nullable Throwable cause = e.getCause();
498-
result.error(
499-
new FlutterError(
500-
ERROR_REASON_EXCEPTION, cause == null ? null : cause.getMessage(), null));
501-
} catch (InterruptedException e) {
502-
result.error(new FlutterError(ERROR_REASON_EXCEPTION, e.getMessage(), null));
503-
Thread.currentThread().interrupt();
504-
}
505-
});
487+
public void clearAuthCache(@NonNull String token) {
488+
try {
489+
GoogleAuthUtil.clearToken(context, token);
490+
} catch (Exception e) {
491+
throw new FlutterError(ERROR_REASON_EXCEPTION, e.getMessage(), null);
492+
}
506493
}
507494

508495
/**
509496
* Gets an OAuth access token with the scopes that were specified during initialization for the
510497
* user with the specified email address.
511498
*
499+
* <p>Runs on a background task queue.
500+
*
512501
* <p>If shouldRecoverAuth is set to true and user needs to recover authentication for method to
513502
* complete, the method will attempt to recover authentication and rerun method.
514503
*/
@@ -517,53 +506,39 @@ public void getAccessToken(
517506
@NonNull String email,
518507
@NonNull Boolean shouldRecoverAuth,
519508
@NonNull Messages.Result<String> result) {
520-
Callable<String> getTokenTask =
521-
() -> {
522-
Account account = new Account(email, "com.google");
523-
String scopesStr = "oauth2:" + Joiner.on(' ').join(requestedScopes);
524-
return GoogleAuthUtil.getToken(context, account, scopesStr);
525-
};
526-
527-
// Background task runner has a single thread effectively serializing
528-
// the getToken calls. 1p apps can then enjoy the token cache if multiple
529-
// getToken calls are coming in.
530-
backgroundTaskRunner.runInBackground(
531-
getTokenTask,
532-
tokenFuture -> {
533-
try {
534-
result.success(tokenFuture.get());
535-
} catch (ExecutionException e) {
536-
if (e.getCause() instanceof UserRecoverableAuthException) {
537-
if (shouldRecoverAuth && pendingOperation == null) {
538-
Activity activity = getActivity();
539-
if (activity == null) {
540-
result.error(
541-
new FlutterError(
542-
ERROR_USER_RECOVERABLE_AUTH,
543-
"Cannot recover auth because app is not in foreground. "
544-
+ e.getLocalizedMessage(),
545-
null));
546-
} else {
547-
checkAndSetPendingAccessTokenOperation("getTokens", result, email);
548-
Intent recoveryIntent =
549-
((UserRecoverableAuthException) e.getCause()).getIntent();
550-
activity.startActivityForResult(recoveryIntent, REQUEST_CODE_RECOVER_AUTH);
551-
}
552-
} else {
509+
try {
510+
Account account = new Account(email, "com.google");
511+
String scopesStr = "oauth2:" + String.join(" ", requestedScopes);
512+
String token = GoogleAuthUtil.getToken(context, account, scopesStr);
513+
result.success(token);
514+
} catch (UserRecoverableAuthException e) {
515+
// This method runs in a background task queue; hop to the main thread for interactions with
516+
// plugin state and activities.
517+
final Handler handler = new Handler(Looper.getMainLooper());
518+
handler.post(
519+
() -> {
520+
if (shouldRecoverAuth && pendingOperation == null) {
521+
Activity activity = getActivity();
522+
if (activity == null) {
553523
result.error(
554-
new FlutterError(ERROR_USER_RECOVERABLE_AUTH, e.getLocalizedMessage(), null));
524+
new FlutterError(
525+
ERROR_USER_RECOVERABLE_AUTH,
526+
"Cannot recover auth because app is not in foreground. "
527+
+ e.getLocalizedMessage(),
528+
null));
529+
} else {
530+
checkAndSetPendingAccessTokenOperation("getTokens", result, email);
531+
Intent recoveryIntent = e.getIntent();
532+
activity.startActivityForResult(recoveryIntent, REQUEST_CODE_RECOVER_AUTH);
555533
}
556534
} else {
557-
@Nullable Throwable cause = e.getCause();
558535
result.error(
559-
new FlutterError(
560-
ERROR_REASON_EXCEPTION, cause == null ? null : cause.getMessage(), null));
536+
new FlutterError(ERROR_USER_RECOVERABLE_AUTH, e.getLocalizedMessage(), null));
561537
}
562-
} catch (InterruptedException e) {
563-
result.error(new FlutterError(ERROR_REASON_EXCEPTION, e.getMessage(), null));
564-
Thread.currentThread().interrupt();
565-
}
566-
});
538+
});
539+
} catch (Exception e) {
540+
result.error(new FlutterError(ERROR_REASON_EXCEPTION, e.getMessage(), null));
541+
}
567542
}
568543

569544
@Override

0 commit comments

Comments
 (0)