diff --git a/packages/google_sign_in/google_sign_in_android/CHANGELOG.md b/packages/google_sign_in/google_sign_in_android/CHANGELOG.md index 75dcc58af33..5d2419a2c83 100644 --- a/packages/google_sign_in/google_sign_in_android/CHANGELOG.md +++ b/packages/google_sign_in/google_sign_in_android/CHANGELOG.md @@ -1,3 +1,7 @@ +## 6.1.35 + +* Removes the dependency on the Guava library. + ## 6.1.34 * Removes unnecessary native code. diff --git a/packages/google_sign_in/google_sign_in_android/android/build.gradle b/packages/google_sign_in/google_sign_in_android/android/build.gradle index 11fb6c7a710..6ce621afccb 100644 --- a/packages/google_sign_in/google_sign_in_android/android/build.gradle +++ b/packages/google_sign_in/google_sign_in_android/android/build.gradle @@ -57,7 +57,6 @@ android { dependencies { implementation 'com.google.android.gms:play-services-auth:21.0.0' - implementation 'com.google.guava:guava:33.3.1-android' testImplementation 'junit:junit:4.13.2' testImplementation 'org.mockito:mockito-inline:5.0.0' } diff --git a/packages/google_sign_in/google_sign_in_android/android/src/main/java/io/flutter/plugins/googlesignin/BackgroundTaskRunner.java b/packages/google_sign_in/google_sign_in_android/android/src/main/java/io/flutter/plugins/googlesignin/BackgroundTaskRunner.java deleted file mode 100644 index 4fc0afadd4d..00000000000 --- a/packages/google_sign_in/google_sign_in_android/android/src/main/java/io/flutter/plugins/googlesignin/BackgroundTaskRunner.java +++ /dev/null @@ -1,85 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -package io.flutter.plugins.googlesignin; - -import androidx.annotation.NonNull; -import com.google.common.util.concurrent.ListenableFuture; -import com.google.common.util.concurrent.SettableFuture; -import java.util.concurrent.BlockingQueue; -import java.util.concurrent.Callable; -import java.util.concurrent.Future; -import java.util.concurrent.LinkedBlockingQueue; -import java.util.concurrent.ThreadPoolExecutor; -import java.util.concurrent.TimeUnit; - -/** - * A class for running tasks in a background thread. - * - *

TODO(jackson): If this class is useful for other plugins, consider including it in a shared - * library or in the Flutter engine - */ -public final class BackgroundTaskRunner { - - /** - * Interface that callers of this API can implement to be notified when a {@link - * #runInBackground(Callable,Callback) background task} has completed. - */ - public interface Callback { - /** - * Invoked on the UI thread when the specified future has completed (calling {@code get()} on - * the future is guaranteed not to block). If the future completed with an exception, then - * {@code get()} will throw an {@code ExecutionException}. - */ - void run(@NonNull Future future); - } - - private final ThreadPoolExecutor executor; - - /** - * Creates a new background processor with the given number of threads. - * - * @param threads The fixed number of threads in ther pool. - */ - public BackgroundTaskRunner(int threads) { - BlockingQueue workQueue = new LinkedBlockingQueue<>(); - // Only keeps idle threads open for 1 second if we've got more threads than cores. - executor = new ThreadPoolExecutor(threads, threads, 1, TimeUnit.SECONDS, workQueue); - } - - /** - * Executes the specified task in a background thread and notifies the specified callback once the - * task has completed (either successfully or with an exception). - * - *

The callback will be notified on the UI thread. - */ - public void runInBackground(@NonNull Callable task, final @NonNull Callback callback) { - final ListenableFuture future = runInBackground(task); - future.addListener(() -> callback.run(future), Executors.uiThreadExecutor()); - } - - /** - * Executes the specified task in a background thread and returns a future with which the caller - * can be notified of task completion. - * - *

Note: the future will be notified on the background thread. To be notified on the UI thread, - * use {@link #runInBackground(Callable,Callback)}. - */ - public @NonNull ListenableFuture runInBackground(final @NonNull Callable task) { - final SettableFuture future = SettableFuture.create(); - - executor.execute( - () -> { - if (!future.isCancelled()) { - try { - future.set(task.call()); - } catch (Throwable t) { - future.setException(t); - } - } - }); - - return future; - } -} diff --git a/packages/google_sign_in/google_sign_in_android/android/src/main/java/io/flutter/plugins/googlesignin/Executors.java b/packages/google_sign_in/google_sign_in_android/android/src/main/java/io/flutter/plugins/googlesignin/Executors.java deleted file mode 100644 index 89f5c22acf2..00000000000 --- a/packages/google_sign_in/google_sign_in_android/android/src/main/java/io/flutter/plugins/googlesignin/Executors.java +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -package io.flutter.plugins.googlesignin; - -import android.os.Handler; -import android.os.Looper; -import androidx.annotation.NonNull; -import java.util.concurrent.Executor; - -/** - * Factory and utility methods for {@code Executor}. - * - *

TODO(jackson): If this class is useful for other plugins, consider including it in a shared - * library or in the Flutter engine - */ -public final class Executors { - - static final class UiThreadExecutor implements Executor { - private static final Handler UI_THREAD = new Handler(Looper.getMainLooper()); - - @Override - public void execute(Runnable command) { - UI_THREAD.post(command); - } - } - - /** Returns an {@code Executor} that will post commands to the UI thread. */ - public static @NonNull Executor uiThreadExecutor() { - return new UiThreadExecutor(); - } - - // Should never be instantiated. - private Executors() {} -} diff --git a/packages/google_sign_in/google_sign_in_android/android/src/main/java/io/flutter/plugins/googlesignin/GoogleSignInPlugin.java b/packages/google_sign_in/google_sign_in_android/android/src/main/java/io/flutter/plugins/googlesignin/GoogleSignInPlugin.java index 92f3f9927df..127c7c281f8 100644 --- a/packages/google_sign_in/google_sign_in_android/android/src/main/java/io/flutter/plugins/googlesignin/GoogleSignInPlugin.java +++ b/packages/google_sign_in/google_sign_in_android/android/src/main/java/io/flutter/plugins/googlesignin/GoogleSignInPlugin.java @@ -9,6 +9,8 @@ import android.app.Activity; import android.content.Context; import android.content.Intent; +import android.os.Handler; +import android.os.Looper; import android.util.Log; import androidx.annotation.NonNull; import androidx.annotation.Nullable; @@ -25,8 +27,6 @@ import com.google.android.gms.common.api.Scope; import com.google.android.gms.tasks.RuntimeExecutionException; import com.google.android.gms.tasks.Task; -import com.google.common.base.Joiner; -import com.google.common.base.Strings; import io.flutter.embedding.engine.plugins.FlutterPlugin; import io.flutter.embedding.engine.plugins.activity.ActivityAware; import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding; @@ -37,8 +37,6 @@ import java.util.ArrayList; import java.util.List; import java.util.Objects; -import java.util.concurrent.Callable; -import java.util.concurrent.ExecutionException; /** Google sign-in plugin for Flutter. */ public class GoogleSignInPlugin implements FlutterPlugin, ActivityAware { @@ -135,8 +133,6 @@ public static class Delegate implements PluginRegistry.ActivityResultListener, G private final @NonNull Context context; // Only set activity for v2 embedder. Always access activity from getActivity() method. private @Nullable Activity activity; - // TODO(stuartmorgan): See whether this can be replaced with background channels. - private final BackgroundTaskRunner backgroundTaskRunner = new BackgroundTaskRunner(1); private final GoogleSignInWrapper googleSignInWrapper; private GoogleSignInClient signInClient; @@ -224,7 +220,7 @@ public void init(@NonNull Messages.InitParams params) { // https://developers.google.com/android/guides/client-auth // https://developers.google.com/identity/sign-in/android/start#configure-a-google-api-project String serverClientId = params.getServerClientId(); - if (!Strings.isNullOrEmpty(params.getClientId()) && Strings.isNullOrEmpty(serverClientId)) { + if (!isNullOrEmpty(params.getClientId()) && isNullOrEmpty(serverClientId)) { Log.w( "google_sign_in", "clientId is not supported on Android and is interpreted as serverClientId. " @@ -232,7 +228,7 @@ public void init(@NonNull Messages.InitParams params) { serverClientId = params.getClientId(); } - if (Strings.isNullOrEmpty(serverClientId)) { + if (isNullOrEmpty(serverClientId)) { // Only requests a clientId if google-services.json was present and parsed // by the google-services Gradle script. // TODO(jackson): Perhaps we should provide a mechanism to override this @@ -246,7 +242,7 @@ public void init(@NonNull Messages.InitParams params) { serverClientId = context.getString(webClientIdIdentifier); } } - if (!Strings.isNullOrEmpty(serverClientId)) { + if (!isNullOrEmpty(serverClientId)) { optionsBuilder.requestIdToken(serverClientId); optionsBuilder.requestServerAuthCode( serverClientId, params.getForceCodeForRefreshToken()); @@ -255,7 +251,7 @@ public void init(@NonNull Messages.InitParams params) { for (String scope : requestedScopes) { optionsBuilder.requestScopes(new Scope(scope)); } - if (!Strings.isNullOrEmpty(params.getHostedDomain())) { + if (!isNullOrEmpty(params.getHostedDomain())) { optionsBuilder.setHostedDomain(params.getHostedDomain()); } @@ -450,6 +446,10 @@ private void finishWithError(String errorCode, String errorMessage) { pendingOperation = null; } + private static boolean isNullOrEmpty(@Nullable String s) { + return s == null || s.isEmpty(); + } + private static class PendingOperation { final @NonNull String method; final @Nullable Messages.Result userDataResult; @@ -478,37 +478,26 @@ private static class PendingOperation { } } - /** Clears the token kept in the client side cache. */ + /** + * Clears the token kept in the client side cache. + * + *

Runs on a background task queue. + */ @Override - public void clearAuthCache(@NonNull String token, @NonNull Messages.VoidResult result) { - Callable clearTokenTask = - () -> { - GoogleAuthUtil.clearToken(context, token); - return null; - }; - - backgroundTaskRunner.runInBackground( - clearTokenTask, - clearTokenFuture -> { - try { - clearTokenFuture.get(); - result.success(); - } catch (ExecutionException e) { - @Nullable Throwable cause = e.getCause(); - result.error( - new FlutterError( - ERROR_REASON_EXCEPTION, cause == null ? null : cause.getMessage(), null)); - } catch (InterruptedException e) { - result.error(new FlutterError(ERROR_REASON_EXCEPTION, e.getMessage(), null)); - Thread.currentThread().interrupt(); - } - }); + public void clearAuthCache(@NonNull String token) { + try { + GoogleAuthUtil.clearToken(context, token); + } catch (Exception e) { + throw new FlutterError(ERROR_REASON_EXCEPTION, e.getMessage(), null); + } } /** * Gets an OAuth access token with the scopes that were specified during initialization for the * user with the specified email address. * + *

Runs on a background task queue. + * *

If shouldRecoverAuth is set to true and user needs to recover authentication for method to * complete, the method will attempt to recover authentication and rerun method. */ @@ -517,53 +506,39 @@ public void getAccessToken( @NonNull String email, @NonNull Boolean shouldRecoverAuth, @NonNull Messages.Result result) { - Callable getTokenTask = - () -> { - Account account = new Account(email, "com.google"); - String scopesStr = "oauth2:" + Joiner.on(' ').join(requestedScopes); - return GoogleAuthUtil.getToken(context, account, scopesStr); - }; - - // Background task runner has a single thread effectively serializing - // the getToken calls. 1p apps can then enjoy the token cache if multiple - // getToken calls are coming in. - backgroundTaskRunner.runInBackground( - getTokenTask, - tokenFuture -> { - try { - result.success(tokenFuture.get()); - } catch (ExecutionException e) { - if (e.getCause() instanceof UserRecoverableAuthException) { - if (shouldRecoverAuth && pendingOperation == null) { - Activity activity = getActivity(); - if (activity == null) { - result.error( - new FlutterError( - ERROR_USER_RECOVERABLE_AUTH, - "Cannot recover auth because app is not in foreground. " - + e.getLocalizedMessage(), - null)); - } else { - checkAndSetPendingAccessTokenOperation("getTokens", result, email); - Intent recoveryIntent = - ((UserRecoverableAuthException) e.getCause()).getIntent(); - activity.startActivityForResult(recoveryIntent, REQUEST_CODE_RECOVER_AUTH); - } - } else { + try { + Account account = new Account(email, "com.google"); + String scopesStr = "oauth2:" + String.join(" ", requestedScopes); + String token = GoogleAuthUtil.getToken(context, account, scopesStr); + result.success(token); + } catch (UserRecoverableAuthException e) { + // This method runs in a background task queue; hop to the main thread for interactions with + // plugin state and activities. + final Handler handler = new Handler(Looper.getMainLooper()); + handler.post( + () -> { + if (shouldRecoverAuth && pendingOperation == null) { + Activity activity = getActivity(); + if (activity == null) { result.error( - new FlutterError(ERROR_USER_RECOVERABLE_AUTH, e.getLocalizedMessage(), null)); + new FlutterError( + ERROR_USER_RECOVERABLE_AUTH, + "Cannot recover auth because app is not in foreground. " + + e.getLocalizedMessage(), + null)); + } else { + checkAndSetPendingAccessTokenOperation("getTokens", result, email); + Intent recoveryIntent = e.getIntent(); + activity.startActivityForResult(recoveryIntent, REQUEST_CODE_RECOVER_AUTH); } } else { - @Nullable Throwable cause = e.getCause(); result.error( - new FlutterError( - ERROR_REASON_EXCEPTION, cause == null ? null : cause.getMessage(), null)); + new FlutterError(ERROR_USER_RECOVERABLE_AUTH, e.getLocalizedMessage(), null)); } - } catch (InterruptedException e) { - result.error(new FlutterError(ERROR_REASON_EXCEPTION, e.getMessage(), null)); - Thread.currentThread().interrupt(); - } - }); + }); + } catch (Exception e) { + result.error(new FlutterError(ERROR_REASON_EXCEPTION, e.getMessage(), null)); + } } @Override diff --git a/packages/google_sign_in/google_sign_in_android/android/src/main/java/io/flutter/plugins/googlesignin/Messages.java b/packages/google_sign_in/google_sign_in_android/android/src/main/java/io/flutter/plugins/googlesignin/Messages.java index 2bf6b6c1e06..e93f2bc6689 100644 --- a/packages/google_sign_in/google_sign_in_android/android/src/main/java/io/flutter/plugins/googlesignin/Messages.java +++ b/packages/google_sign_in/google_sign_in_android/android/src/main/java/io/flutter/plugins/googlesignin/Messages.java @@ -1,7 +1,7 @@ // Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// Autogenerated from Pigeon (v22.4.2), do not edit directly. +// Autogenerated from Pigeon (v24.2.0), do not edit directly. // See also: https://pub.dev/packages/pigeon package io.flutter.plugins.googlesignin; @@ -547,7 +547,7 @@ void getAccessToken( @NonNull Boolean isSignedIn(); /** Clears the authentication caching for the given token, requiring a new sign in. */ - void clearAuthCache(@NonNull String token, @NonNull VoidResult result); + void clearAuthCache(@NonNull String token); /** Requests access to the given scopes. */ void requestScopes(@NonNull List scopes, @NonNull Result result); @@ -567,6 +567,7 @@ static void setUp( @NonNull String messageChannelSuffix, @Nullable GoogleSignInApi api) { messageChannelSuffix = messageChannelSuffix.isEmpty() ? "" : "." + messageChannelSuffix; + BinaryMessenger.TaskQueue taskQueue = binaryMessenger.makeBackgroundTaskQueue(); { BasicMessageChannel channel = new BasicMessageChannel<>( @@ -658,7 +659,8 @@ public void error(Throwable error) { binaryMessenger, "dev.flutter.pigeon.google_sign_in_android.GoogleSignInApi.getAccessToken" + messageChannelSuffix, - getCodec()); + getCodec(), + taskQueue); if (api != null) { channel.setMessageHandler( (message, reply) -> { @@ -774,27 +776,21 @@ public void error(Throwable error) { binaryMessenger, "dev.flutter.pigeon.google_sign_in_android.GoogleSignInApi.clearAuthCache" + messageChannelSuffix, - getCodec()); + getCodec(), + taskQueue); if (api != null) { channel.setMessageHandler( (message, reply) -> { ArrayList wrapped = new ArrayList<>(); ArrayList args = (ArrayList) message; String tokenArg = (String) args.get(0); - VoidResult resultCallback = - new VoidResult() { - public void success() { - wrapped.add(0, null); - reply.reply(wrapped); - } - - public void error(Throwable error) { - ArrayList wrappedError = wrapError(error); - reply.reply(wrappedError); - } - }; - - api.clearAuthCache(tokenArg, resultCallback); + try { + api.clearAuthCache(tokenArg); + wrapped.add(0, null); + } catch (Throwable exception) { + wrapped = wrapError(exception); + } + reply.reply(wrapped); }); } else { channel.setMessageHandler(null); diff --git a/packages/google_sign_in/google_sign_in_android/lib/src/messages.g.dart b/packages/google_sign_in/google_sign_in_android/lib/src/messages.g.dart index c5a7367318c..1fb49d87a2e 100644 --- a/packages/google_sign_in/google_sign_in_android/lib/src/messages.g.dart +++ b/packages/google_sign_in/google_sign_in_android/lib/src/messages.g.dart @@ -1,7 +1,7 @@ // Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// Autogenerated from Pigeon (v22.4.2), do not edit directly. +// Autogenerated from Pigeon (v24.2.0), do not edit directly. // See also: https://pub.dev/packages/pigeon // ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import, unnecessary_parenthesis, prefer_null_aware_operators, omit_local_variable_types, unused_shown_name, unnecessary_import, no_leading_underscores_for_local_identifiers diff --git a/packages/google_sign_in/google_sign_in_android/pigeons/messages.dart b/packages/google_sign_in/google_sign_in_android/pigeons/messages.dart index 69a452277df..e241e211555 100644 --- a/packages/google_sign_in/google_sign_in_android/pigeons/messages.dart +++ b/packages/google_sign_in/google_sign_in_android/pigeons/messages.dart @@ -79,6 +79,7 @@ abstract class GoogleSignInApi { /// Requests the access token for the current sign in. @async + @TaskQueue(type: TaskQueueType.serialBackgroundThread) String getAccessToken(String email, bool shouldRecoverAuth); /// Signs out the current user. @@ -94,7 +95,7 @@ abstract class GoogleSignInApi { /// Clears the authentication caching for the given token, requiring a /// new sign in. - @async + @TaskQueue(type: TaskQueueType.serialBackgroundThread) void clearAuthCache(String token); /// Requests access to the given scopes. diff --git a/packages/google_sign_in/google_sign_in_android/pubspec.yaml b/packages/google_sign_in/google_sign_in_android/pubspec.yaml index b47fba90059..24e1f19973c 100644 --- a/packages/google_sign_in/google_sign_in_android/pubspec.yaml +++ b/packages/google_sign_in/google_sign_in_android/pubspec.yaml @@ -2,7 +2,7 @@ name: google_sign_in_android description: Android implementation of the google_sign_in plugin. repository: https://github.com/flutter/packages/tree/main/packages/google_sign_in/google_sign_in_android issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+google_sign_in%22 -version: 6.1.34 +version: 6.1.35 environment: sdk: ^3.5.0 @@ -29,7 +29,7 @@ dev_dependencies: integration_test: sdk: flutter mockito: ^5.4.4 - pigeon: ^22.4.2 + pigeon: ^24.2.0 topics: - authentication