[google_sign_in] Migrate to Android v2 embedder. (#2624)
diff --git a/packages/google_sign_in/google_sign_in/CHANGELOG.md b/packages/google_sign_in/google_sign_in/CHANGELOG.md
index a17b384..1a3011f 100644
--- a/packages/google_sign_in/google_sign_in/CHANGELOG.md
+++ b/packages/google_sign_in/google_sign_in/CHANGELOG.md
@@ -1,3 +1,7 @@
+## 4.4.0
+
+* Migrate to Android v2 embedder.
+
## 4.3.0
* Add support for method introduced in `google_sign_in_platform_interface` 1.1.0.
diff --git a/packages/google_sign_in/google_sign_in/android/src/main/java/io/flutter/plugins/googlesignin/GoogleSignInPlugin.java b/packages/google_sign_in/google_sign_in/android/src/main/java/io/flutter/plugins/googlesignin/GoogleSignInPlugin.java
index ebebfa0..d3ea915 100755
--- a/packages/google_sign_in/google_sign_in/android/src/main/java/io/flutter/plugins/googlesignin/GoogleSignInPlugin.java
+++ b/packages/google_sign_in/google_sign_in/android/src/main/java/io/flutter/plugins/googlesignin/GoogleSignInPlugin.java
@@ -8,6 +8,7 @@
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
+import androidx.annotation.NonNull;
import androidx.annotation.VisibleForTesting;
import com.google.android.gms.auth.GoogleAuthUtil;
import com.google.android.gms.auth.UserRecoverableAuthException;
@@ -24,6 +25,10 @@
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;
+import io.flutter.plugin.common.BinaryMessenger;
import io.flutter.plugin.common.MethodCall;
import io.flutter.plugin.common.MethodChannel;
import io.flutter.plugin.common.MethodChannel.MethodCallHandler;
@@ -38,7 +43,7 @@
import java.util.concurrent.Future;
/** Google sign-in plugin for Flutter. */
-public class GoogleSignInPlugin implements MethodCallHandler {
+public class GoogleSignInPlugin implements MethodCallHandler, FlutterPlugin, ActivityAware {
private static final String CHANNEL_NAME = "plugins.flutter.io/google_sign_in";
private static final String METHOD_INIT = "init";
@@ -51,17 +56,76 @@
private static final String METHOD_CLEAR_AUTH_CACHE = "clearAuthCache";
private static final String METHOD_REQUEST_SCOPES = "requestScopes";
- private final IDelegate delegate;
+ private Delegate delegate;
+ private MethodChannel channel;
+ private ActivityPluginBinding activityPluginBinding;
public static void registerWith(PluginRegistry.Registrar registrar) {
- final MethodChannel channel = new MethodChannel(registrar.messenger(), CHANNEL_NAME);
- final GoogleSignInPlugin instance =
- new GoogleSignInPlugin(registrar, new GoogleSignInWrapper());
- channel.setMethodCallHandler(instance);
+ GoogleSignInPlugin instance = new GoogleSignInPlugin();
+ instance.initInstance(registrar.messenger(), registrar.context(), new GoogleSignInWrapper());
+ instance.setUpRegistrar(registrar);
}
- GoogleSignInPlugin(PluginRegistry.Registrar registrar, GoogleSignInWrapper googleSignInWrapper) {
- delegate = new Delegate(registrar, googleSignInWrapper);
+ @VisibleForTesting
+ public void initInstance(
+ BinaryMessenger messenger, Context context, GoogleSignInWrapper googleSignInWrapper) {
+ channel = new MethodChannel(messenger, CHANNEL_NAME);
+ delegate = new Delegate(context, googleSignInWrapper);
+ channel.setMethodCallHandler(this);
+ }
+
+ @VisibleForTesting
+ public void setUpRegistrar(PluginRegistry.Registrar registrar) {
+ delegate.setUpRegistrar(registrar);
+ }
+
+ private void dispose() {
+ delegate = null;
+ channel.setMethodCallHandler(null);
+ channel = null;
+ }
+
+ private void attachToActivity(ActivityPluginBinding activityPluginBinding) {
+ this.activityPluginBinding = activityPluginBinding;
+ activityPluginBinding.addActivityResultListener(delegate);
+ delegate.setActivity(activityPluginBinding.getActivity());
+ }
+
+ private void disposeActivity() {
+ activityPluginBinding.removeActivityResultListener(delegate);
+ delegate.setActivity(null);
+ activityPluginBinding = null;
+ }
+
+ @Override
+ public void onAttachedToEngine(@NonNull FlutterPluginBinding binding) {
+ initInstance(
+ binding.getBinaryMessenger(), binding.getApplicationContext(), new GoogleSignInWrapper());
+ }
+
+ @Override
+ public void onDetachedFromEngine(@NonNull FlutterPluginBinding binding) {
+ dispose();
+ }
+
+ @Override
+ public void onAttachedToActivity(ActivityPluginBinding activityPluginBinding) {
+ attachToActivity(activityPluginBinding);
+ }
+
+ @Override
+ public void onDetachedFromActivityForConfigChanges() {
+ disposeActivity();
+ }
+
+ @Override
+ public void onReattachedToActivityForConfigChanges(ActivityPluginBinding activityPluginBinding) {
+ attachToActivity(activityPluginBinding);
+ }
+
+ @Override
+ public void onDetachedFromActivity() {
+ disposeActivity();
}
@Override
@@ -195,7 +259,11 @@
private static final String DEFAULT_SIGN_IN = "SignInOption.standard";
private static final String DEFAULT_GAMES_SIGN_IN = "SignInOption.games";
- private final PluginRegistry.Registrar registrar;
+ private final Context context;
+ // Only set registrar for v1 embedder.
+ private PluginRegistry.Registrar registrar;
+ // Only set activity for v2 embedder. Always access activity from getActivity() method.
+ private Activity activity;
private final BackgroundTaskRunner backgroundTaskRunner = new BackgroundTaskRunner(1);
private final GoogleSignInWrapper googleSignInWrapper;
@@ -203,12 +271,25 @@
private List<String> requestedScopes;
private PendingOperation pendingOperation;
- public Delegate(PluginRegistry.Registrar registrar, GoogleSignInWrapper googleSignInWrapper) {
- this.registrar = registrar;
+ public Delegate(Context context, GoogleSignInWrapper googleSignInWrapper) {
+ this.context = context;
this.googleSignInWrapper = googleSignInWrapper;
+ }
+
+ public void setUpRegistrar(PluginRegistry.Registrar registrar) {
+ this.registrar = registrar;
registrar.addActivityResultListener(this);
}
+ public void setActivity(Activity activity) {
+ this.activity = activity;
+ }
+
+ // Only access activity with this method.
+ public Activity getActivity() {
+ return registrar != null ? registrar.activity() : activity;
+ }
+
private void checkAndSetPendingOperation(String method, Result result) {
checkAndSetPendingOperation(method, result, null);
}
@@ -249,13 +330,11 @@
// TODO(jackson): Perhaps we should provide a mechanism to override this
// behavior.
int clientIdIdentifier =
- registrar
- .context()
+ context
.getResources()
- .getIdentifier(
- "default_web_client_id", "string", registrar.context().getPackageName());
+ .getIdentifier("default_web_client_id", "string", context.getPackageName());
if (clientIdIdentifier != 0) {
- optionsBuilder.requestIdToken(registrar.context().getString(clientIdIdentifier));
+ optionsBuilder.requestIdToken(context.getString(clientIdIdentifier));
}
for (String scope : requestedScopes) {
optionsBuilder.requestScopes(new Scope(scope));
@@ -265,7 +344,7 @@
}
this.requestedScopes = requestedScopes;
- signInClient = GoogleSignIn.getClient(registrar.context(), optionsBuilder.build());
+ signInClient = GoogleSignIn.getClient(context, optionsBuilder.build());
result.success(null);
} catch (Exception e) {
result.error(ERROR_REASON_EXCEPTION, e.getMessage(), null);
@@ -300,13 +379,13 @@
*/
@Override
public void signIn(Result result) {
- if (registrar.activity() == null) {
+ if (getActivity() == null) {
throw new IllegalStateException("signIn needs a foreground activity");
}
checkAndSetPendingOperation(METHOD_SIGN_IN, result);
Intent signInIntent = signInClient.getSignInIntent();
- registrar.activity().startActivityForResult(signInIntent, REQUEST_CODE_SIGNIN);
+ getActivity().startActivityForResult(signInIntent, REQUEST_CODE_SIGNIN);
}
/**
@@ -355,7 +434,7 @@
/** Checks if there is a signed in user. */
@Override
public void isSignedIn(final Result result) {
- boolean value = GoogleSignIn.getLastSignedInAccount(registrar.context()) != null;
+ boolean value = GoogleSignIn.getLastSignedInAccount(context) != null;
result.success(value);
}
@@ -363,7 +442,7 @@
public void requestScopes(Result result, List<String> scopes) {
checkAndSetPendingOperation(METHOD_REQUEST_SCOPES, result);
- GoogleSignInAccount account = googleSignInWrapper.getLastSignedInAccount(registrar.context());
+ GoogleSignInAccount account = googleSignInWrapper.getLastSignedInAccount(context);
if (account == null) {
result.error(ERROR_REASON_SIGN_IN_REQUIRED, "No account to grant scopes.", null);
return;
@@ -384,10 +463,7 @@
}
googleSignInWrapper.requestPermissions(
- registrar.activity(),
- REQUEST_CODE_REQUEST_SCOPE,
- account,
- wrappedScopes.toArray(new Scope[0]));
+ getActivity(), REQUEST_CODE_REQUEST_SCOPE, account, wrappedScopes.toArray(new Scope[0]));
}
private void onSignInResult(Task<GoogleSignInAccount> completedTask) {
@@ -456,7 +532,7 @@
new Callable<Void>() {
@Override
public Void call() throws Exception {
- GoogleAuthUtil.clearToken(registrar.context(), token);
+ GoogleAuthUtil.clearToken(context, token);
return null;
}
};
@@ -499,7 +575,7 @@
public String call() throws Exception {
Account account = new Account(email, "com.google");
String scopesStr = "oauth2:" + Joiner.on(' ').join(requestedScopes);
- return GoogleAuthUtil.getToken(registrar.context(), account, scopesStr);
+ return GoogleAuthUtil.getToken(context, account, scopesStr);
}
};
@@ -519,7 +595,7 @@
} catch (ExecutionException e) {
if (e.getCause() instanceof UserRecoverableAuthException) {
if (shouldRecoverAuth && pendingOperation == null) {
- Activity activity = registrar.activity();
+ Activity activity = getActivity();
if (activity == null) {
result.error(
ERROR_USER_RECOVERABLE_AUTH,
diff --git a/packages/google_sign_in/google_sign_in/example/android/app/src/main/AndroidManifest.xml b/packages/google_sign_in/google_sign_in/example/android/app/src/main/AndroidManifest.xml
index 7e93af7..df80f82 100755
--- a/packages/google_sign_in/google_sign_in/example/android/app/src/main/AndroidManifest.xml
+++ b/packages/google_sign_in/google_sign_in/example/android/app/src/main/AndroidManifest.xml
@@ -3,17 +3,23 @@
<uses-permission android:name="android.permission.INTERNET"/>
- <application android:name="io.flutter.app.FlutterApplication" android:label="Google Sign-In Example" android:icon="@mipmap/ic_launcher">
- <activity android:name=".MainActivity"
- android:launchMode="singleTop"
+ <application>
+ <activity android:name="io.flutter.embedding.android.FlutterActivity"
android:theme="@android:style/Theme.Black.NoTitleBar"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|layoutDirection"
android:hardwareAccelerated="true"
android:windowSoftInputMode="adjustResize">
<intent-filter>
- <action android:name="android.intent.action.MAIN"/>
- <category android:name="android.intent.category.LAUNCHER"/>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
+ <activity android:name=".EmbeddingV1Activity"
+ android:theme="@android:style/Theme.Black.NoTitleBar"
+ android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|layoutDirection"
+ android:hardwareAccelerated="true"
+ android:windowSoftInputMode="adjustResize">
+ </activity>
+ <meta-data android:name="flutterEmbedding" android:value="2"/>
</application>
</manifest>
diff --git a/packages/google_sign_in/google_sign_in/example/android/app/src/main/java/io/flutter/plugins/googlesigninexample/EmbeddingV1Activity.java b/packages/google_sign_in/google_sign_in/example/android/app/src/main/java/io/flutter/plugins/googlesigninexample/EmbeddingV1Activity.java
new file mode 100644
index 0000000..f7ea0c4
--- /dev/null
+++ b/packages/google_sign_in/google_sign_in/example/android/app/src/main/java/io/flutter/plugins/googlesigninexample/EmbeddingV1Activity.java
@@ -0,0 +1,19 @@
+// Copyright 2017 The Chromium 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.googlesigninexample;
+
+import android.os.Bundle;
+import io.flutter.app.FlutterActivity;
+import io.flutter.plugins.googlesignin.GoogleSignInPlugin;
+import io.flutter.view.FlutterMain;
+
+public class EmbeddingV1Activity extends FlutterActivity {
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ FlutterMain.startInitialization(this);
+ super.onCreate(savedInstanceState);
+ GoogleSignInPlugin.registerWith(registrarFor("io.flutter.plugins.googlesignin"));
+ }
+}
diff --git a/packages/google_sign_in/google_sign_in/example/android/app/src/main/java/io/flutter/plugins/googlesigninexample/EmbeddingV1ActivityTest.java b/packages/google_sign_in/google_sign_in/example/android/app/src/main/java/io/flutter/plugins/googlesigninexample/EmbeddingV1ActivityTest.java
new file mode 100644
index 0000000..8bddbff
--- /dev/null
+++ b/packages/google_sign_in/google_sign_in/example/android/app/src/main/java/io/flutter/plugins/googlesigninexample/EmbeddingV1ActivityTest.java
@@ -0,0 +1,17 @@
+// Copyright 2020 The Chromium 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.googlesigninexample;
+
+import androidx.test.rule.ActivityTestRule;
+import dev.flutter.plugins.e2e.FlutterRunner;
+import org.junit.Rule;
+import org.junit.runner.RunWith;
+
+@RunWith(FlutterRunner.class)
+public class EmbeddingV1ActivityTest {
+ @Rule
+ public ActivityTestRule<EmbeddingV1Activity> rule =
+ new ActivityTestRule<>(EmbeddingV1Activity.class);
+}
diff --git a/packages/google_sign_in/google_sign_in/example/android/app/src/main/java/io/flutter/plugins/googlesigninexample/FlutterActivityTest.java b/packages/google_sign_in/google_sign_in/example/android/app/src/main/java/io/flutter/plugins/googlesigninexample/FlutterActivityTest.java
new file mode 100644
index 0000000..77cdcee
--- /dev/null
+++ b/packages/google_sign_in/google_sign_in/example/android/app/src/main/java/io/flutter/plugins/googlesigninexample/FlutterActivityTest.java
@@ -0,0 +1,17 @@
+// Copyright 2020 The Chromium 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.googlesigninexample;
+
+import androidx.test.rule.ActivityTestRule;
+import dev.flutter.plugins.e2e.FlutterRunner;
+import io.flutter.embedding.android.FlutterActivity;
+import org.junit.Rule;
+import org.junit.runner.RunWith;
+
+@RunWith(FlutterRunner.class)
+public class FlutterActivityTest {
+ @Rule
+ public ActivityTestRule<FlutterActivity> rule = new ActivityTestRule<>(FlutterActivity.class);
+}
diff --git a/packages/google_sign_in/google_sign_in/example/android/app/src/main/java/io/flutter/plugins/googlesigninexample/MainActivity.java b/packages/google_sign_in/google_sign_in/example/android/app/src/main/java/io/flutter/plugins/googlesigninexample/MainActivity.java
deleted file mode 100644
index 026cec2..0000000
--- a/packages/google_sign_in/google_sign_in/example/android/app/src/main/java/io/flutter/plugins/googlesigninexample/MainActivity.java
+++ /dev/null
@@ -1,17 +0,0 @@
-// Copyright 2017 The Chromium 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.googlesigninexample;
-
-import android.os.Bundle;
-import io.flutter.app.FlutterActivity;
-import io.flutter.plugins.GeneratedPluginRegistrant;
-
-public class MainActivity extends FlutterActivity {
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- GeneratedPluginRegistrant.registerWith(this);
- }
-}
diff --git a/packages/google_sign_in/google_sign_in/example/android/app/src/test/java/io/flutter/plugins/googlesignin/GoogleSignInPluginTests.java b/packages/google_sign_in/google_sign_in/example/android/app/src/test/java/io/flutter/plugins/googlesignin/GoogleSignInPluginTests.java
index bd8e37a..1413063 100644
--- a/packages/google_sign_in/google_sign_in/example/android/app/src/test/java/io/flutter/plugins/googlesignin/GoogleSignInPluginTests.java
+++ b/packages/google_sign_in/google_sign_in/example/android/app/src/test/java/io/flutter/plugins/googlesignin/GoogleSignInPluginTests.java
@@ -45,7 +45,9 @@
when(mockRegistrar.messenger()).thenReturn(mockMessenger);
when(mockRegistrar.context()).thenReturn(mockContext);
when(mockRegistrar.activity()).thenReturn(mockActivity);
- plugin = new GoogleSignInPlugin(mockRegistrar, mockGoogleSignIn);
+ plugin = new GoogleSignInPlugin();
+ plugin.initInstance(mockRegistrar.messenger(), mockRegistrar.context(), mockGoogleSignIn);
+ plugin.setUpRegistrar(mockRegistrar);
}
@Test
diff --git a/packages/google_sign_in/google_sign_in/example/pubspec.yaml b/packages/google_sign_in/google_sign_in/example/pubspec.yaml
index 1530af2..e3ab95e 100755
--- a/packages/google_sign_in/google_sign_in/example/pubspec.yaml
+++ b/packages/google_sign_in/google_sign_in/example/pubspec.yaml
@@ -10,6 +10,13 @@
dev_dependencies:
pedantic: ^1.8.0
+ e2e: ^0.2.1
+ flutter_driver:
+ sdk: flutter
flutter:
uses-material-design: true
+
+environment:
+ sdk: ">=2.0.0-dev.28.0 <3.0.0"
+ flutter: ">=1.12.13+hotfix.4 <2.0.0"
diff --git a/packages/google_sign_in/google_sign_in/pubspec.yaml b/packages/google_sign_in/google_sign_in/pubspec.yaml
index 860369d..055b7c5 100644
--- a/packages/google_sign_in/google_sign_in/pubspec.yaml
+++ b/packages/google_sign_in/google_sign_in/pubspec.yaml
@@ -2,7 +2,7 @@
description: Flutter plugin for Google Sign-In, a secure authentication system
for signing in with a Google account on Android and iOS.
homepage: https://github.com/flutter/plugins/tree/master/packages/google_sign_in/google_sign_in
-version: 4.3.0
+version: 4.4.0
flutter:
plugin:
@@ -29,9 +29,12 @@
dev_dependencies:
http: ^0.12.0
+ flutter_driver:
+ sdk: flutter
flutter_test:
sdk: flutter
pedantic: ^1.8.0
+ e2e: ^0.2.1
environment:
sdk: ">=2.0.0-dev.28.0 <3.0.0"
diff --git a/packages/google_sign_in/google_sign_in/test/google_sign_in_e2e.dart b/packages/google_sign_in/google_sign_in/test/google_sign_in_e2e.dart
new file mode 100644
index 0000000..0c6431f
--- /dev/null
+++ b/packages/google_sign_in/google_sign_in/test/google_sign_in_e2e.dart
@@ -0,0 +1,12 @@
+import 'package:e2e/e2e.dart';
+import 'package:flutter_test/flutter_test.dart';
+import 'package:google_sign_in/google_sign_in.dart';
+
+void main() {
+ E2EWidgetsFlutterBinding.ensureInitialized();
+
+ testWidgets('Can initialize the plugin', (WidgetTester tester) async {
+ GoogleSignIn signIn = GoogleSignIn();
+ expect(signIn, isNotNull);
+ });
+}
diff --git a/packages/google_sign_in/google_sign_in/test_driver/google_sign_in_e2e_test.dart b/packages/google_sign_in/google_sign_in/test_driver/google_sign_in_e2e_test.dart
new file mode 100644
index 0000000..9f1704f
--- /dev/null
+++ b/packages/google_sign_in/google_sign_in/test_driver/google_sign_in_e2e_test.dart
@@ -0,0 +1,15 @@
+// Copyright 2020, the Chromium project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'dart:async';
+import 'dart:io';
+import 'package:flutter_driver/flutter_driver.dart';
+
+Future<void> main() async {
+ final FlutterDriver driver = await FlutterDriver.connect();
+ final String result =
+ await driver.requestData(null, timeout: const Duration(minutes: 1));
+ await driver.close();
+ exit(result == 'pass' ? 0 : 1);
+}