From 88550453b0d321ba6c17542c3c80c558fb0065ec Mon Sep 17 00:00:00 2001 From: Michael Klimushyn Date: Tue, 8 Oct 2019 17:51:34 -0700 Subject: [PATCH 1/4] Migrate flutter_webview to the new embedding This depends on a new bugfix on the engine for text input to work with the new embedding (flutter/engine#13015). This also introduces some interesting and strange new failure cases. With the new embedding, a FlutterView can be attached and detached from the plugin at any point. It "should never" but technically can happen that the plugin gets into a state where there is an active proxy adapter view but no valid FlutterView to use for input proxying. For now I've mostly added error logs and guards against NPEs detecting whenever we've gotten into one of this situations. But I think we should probably rigorously test this in an a2a specific situation to truly vet for a2a support. DO NOT MERGE until we can land tests for this. --- .../webview_flutter/android/gradle.properties | 2 + .../webviewflutter/FlutterCookieManager.java | 16 ++-- .../webviewflutter/FlutterWebView.java | 14 +++- .../webviewflutter/InputAwareWebView.java | 38 ++++++++- .../webviewflutter/WebViewFactory.java | 5 +- .../webviewflutter/WebViewFlutterPlugin.java | 53 ++++++++++++- .../example/android/app/build.gradle | 2 + .../android/app/src/main/AndroidManifest.xml | 79 +++++++++++-------- .../EmbeddingV1Activity.java | 17 ++++ .../webviewflutterexample/MainActivity.java | 15 ++-- .../example/android/gradle.properties | 2 +- .../example/android/settings_aar.gradle | 1 + 12 files changed, 182 insertions(+), 62 deletions(-) create mode 100644 packages/webview_flutter/example/android/app/src/main/java/io/flutter/plugins/webviewflutterexample/EmbeddingV1Activity.java create mode 100644 packages/webview_flutter/example/android/settings_aar.gradle diff --git a/packages/webview_flutter/android/gradle.properties b/packages/webview_flutter/android/gradle.properties index 8bd86f680510..08f2b5f91bff 100644 --- a/packages/webview_flutter/android/gradle.properties +++ b/packages/webview_flutter/android/gradle.properties @@ -1 +1,3 @@ org.gradle.jvmargs=-Xmx1536M +android.enableJetifier=true +android.useAndroidX=true diff --git a/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/FlutterCookieManager.java b/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/FlutterCookieManager.java index 908f877fb922..f9bbb93b2240 100644 --- a/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/FlutterCookieManager.java +++ b/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/FlutterCookieManager.java @@ -15,17 +15,15 @@ import io.flutter.plugin.common.MethodChannel.Result; class FlutterCookieManager implements MethodCallHandler { + private final MethodChannel methodChannel; - private FlutterCookieManager() { - // Do not instantiate. - // This class should only be used in context of a BinaryMessenger. - // Use FlutterCookieManager#registerWith instead. + FlutterCookieManager(BinaryMessenger messenger) { + methodChannel = new MethodChannel(messenger, "plugins.flutter.io/cookie_manager"); + methodChannel.setMethodCallHandler(this); } static void registerWith(BinaryMessenger messenger) { - MethodChannel methodChannel = new MethodChannel(messenger, "plugins.flutter.io/cookie_manager"); - FlutterCookieManager cookieManager = new FlutterCookieManager(); - methodChannel.setMethodCallHandler(cookieManager); + new FlutterCookieManager(messenger); } @Override @@ -39,6 +37,10 @@ public void onMethodCall(MethodCall methodCall, Result result) { } } + void dispose() { + methodChannel.setMethodCallHandler(null); + } + private static void clearCookies(final Result result) { CookieManager cookieManager = CookieManager.getInstance(); final boolean hasCookies = cookieManager.hasCookies(); diff --git a/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/FlutterWebView.java b/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/FlutterWebView.java index a7f2db308e15..ac326ed7f27b 100644 --- a/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/FlutterWebView.java +++ b/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/FlutterWebView.java @@ -12,6 +12,8 @@ import android.view.View; import android.webkit.WebStorage; import android.webkit.WebViewClient; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import io.flutter.plugin.common.BinaryMessenger; import io.flutter.plugin.common.MethodCall; import io.flutter.plugin.common.MethodChannel; @@ -36,7 +38,7 @@ public class FlutterWebView implements PlatformView, MethodCallHandler { BinaryMessenger messenger, int id, Map params, - final View containerView) { + @Nullable View containerView) { DisplayListenerProxy displayListenerProxy = new DisplayListenerProxy(); DisplayManager displayManager = @@ -95,6 +97,16 @@ public void onInputConnectionLocked() { webView.lockInputConnection(); } + @Override + public void onFlutterViewAttached(@NonNull View flutterView) { + webView.setContainerView(flutterView); + } + + @Override + public void onFlutterViewDetached() { + webView.setContainerView(null); + } + @Override public void onMethodCall(MethodCall methodCall, Result result) { switch (methodCall.method) { diff --git a/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/InputAwareWebView.java b/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/InputAwareWebView.java index 9275c380fb56..a8168a99b2b3 100644 --- a/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/InputAwareWebView.java +++ b/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/InputAwareWebView.java @@ -7,9 +7,11 @@ import static android.content.Context.INPUT_METHOD_SERVICE; import android.content.Context; +import android.util.Log; import android.view.View; import android.view.inputmethod.InputMethodManager; import android.webkit.WebView; +import androidx.annotation.Nullable; /** * A WebView subclass that mirrors the same implementation hacks that the system WebView does in @@ -22,16 +24,29 @@ *

See also {@link ThreadedInputConnectionProxyAdapterView}. */ final class InputAwareWebView extends WebView { - private final View containerView; - + private static final String TAG = "InputAwareWebView"; private View threadedInputConnectionProxyView; private ThreadedInputConnectionProxyAdapterView proxyAdapterView; + private @Nullable View containerView; - InputAwareWebView(Context context, View containerView) { + InputAwareWebView(Context context, @Nullable View containerView) { super(context); this.containerView = containerView; } + void setContainerView(@Nullable View containerView) { + this.containerView = containerView; + + if (proxyAdapterView == null) { + return; + } + + Log.e(TAG, "The containerView has changed while the proxyAdapterView exists."); + if (containerView != null) { + setInputConnectionTarget(proxyAdapterView); + } + } + /** * Set our proxy adapter view to use its cached input connection instead of creating new ones. * @@ -81,6 +96,12 @@ public boolean checkInputConnectionProxy(final View view) { // This isn't a new ThreadedInputConnectionProxyView. Ignore it. return super.checkInputConnectionProxy(view); } + if (containerView == null) { + Log.e( + TAG, + "Can't create a proxy view because there's no container view. Text input may not work."); + return super.checkInputConnectionProxy(view); + } // We've never seen this before, so we make the assumption that this is WebView's // ThreadedInputConnectionProxyView. We are making the assumption that the only view that could @@ -120,6 +141,10 @@ private void resetInputConnection() { // No need to reset the InputConnection to the default thread if we've never changed it. return; } + if (containerView == null) { + Log.e(TAG, "Can't reset the input connection to the container view because there is none."); + return; + } setInputConnectionTarget(/*targetView=*/ containerView); } @@ -132,6 +157,13 @@ private void resetInputConnection() { * InputConnections should be created on. */ private void setInputConnectionTarget(final View targetView) { + if (containerView == null) { + Log.e( + TAG, + "Can't set the input connection target because there is no containerView to use as a handler."); + return; + } + targetView.requestFocus(); containerView.post( new Runnable() { diff --git a/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/WebViewFactory.java b/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/WebViewFactory.java index 6fdc36fbe545..fe62e3a35540 100644 --- a/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/WebViewFactory.java +++ b/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/WebViewFactory.java @@ -6,6 +6,7 @@ import android.content.Context; import android.view.View; +import androidx.annotation.Nullable; import io.flutter.plugin.common.BinaryMessenger; import io.flutter.plugin.common.StandardMessageCodec; import io.flutter.plugin.platform.PlatformView; @@ -14,9 +15,9 @@ public final class WebViewFactory extends PlatformViewFactory { private final BinaryMessenger messenger; - private final View containerView; + private @Nullable final View containerView; - WebViewFactory(BinaryMessenger messenger, View containerView) { + WebViewFactory(BinaryMessenger messenger, @Nullable View containerView) { super(StandardMessageCodec.INSTANCE); this.messenger = messenger; this.containerView = containerView; diff --git a/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/WebViewFlutterPlugin.java b/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/WebViewFlutterPlugin.java index 17177541222c..43214b4c45b8 100644 --- a/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/WebViewFlutterPlugin.java +++ b/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/WebViewFlutterPlugin.java @@ -4,11 +4,36 @@ package io.flutter.plugins.webviewflutter; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import io.flutter.embedding.engine.plugins.FlutterPlugin; +import io.flutter.plugin.common.BinaryMessenger; import io.flutter.plugin.common.PluginRegistry.Registrar; -/** WebViewFlutterPlugin */ -public class WebViewFlutterPlugin { - /** Plugin registration. */ +/** + * Java platform implementation of the webview_flutter plugin. + * + *

Instantiate this in an add to app scenario to gracefully handle activity and context changes. + * + *

Call registerWith to use the stable {@code io.flutter.plugin.common} package instead. + */ +public class WebViewFlutterPlugin implements FlutterPlugin { + private @Nullable FlutterCookieManager flutterCookieManager; + + /** + * Initialize this within the {@code #configureFlutterEngine} of a Flutter activity or fragment. + * + *

See {@code io.flutter.plugins.webviewflutterexample.MainActivity} for an example. + */ + public WebViewFlutterPlugin() {} + + /** + * Registers a plugin implementation that uses the stable {@code io.flutter.plugin.common} + * package. + * + *

Calling this automatically initializes the plugin. However plugins initialized this way + * won't react to changes in activity or context, unlike {@link CameraPlugin}. + */ public static void registerWith(Registrar registrar) { registrar .platformViewRegistry() @@ -17,4 +42,26 @@ public static void registerWith(Registrar registrar) { new WebViewFactory(registrar.messenger(), registrar.view())); FlutterCookieManager.registerWith(registrar.messenger()); } + + @Override + public void onAttachedToEngine(@NonNull FlutterPluginBinding binding) { + BinaryMessenger messenger = binding.getFlutterEngine().getDartExecutor(); + binding + .getFlutterEngine() + .getPlatformViewsController() + .getRegistry() + .registerViewFactory( + "plugins.flutter.io/webview", new WebViewFactory(messenger, /*containerView=*/ null)); + flutterCookieManager = new FlutterCookieManager(messenger); + } + + @Override + public void onDetachedFromEngine(@NonNull FlutterPluginBinding binding) { + if (flutterCookieManager == null) { + return; + } + + flutterCookieManager.dispose(); + flutterCookieManager = null; + } } diff --git a/packages/webview_flutter/example/android/app/build.gradle b/packages/webview_flutter/example/android/app/build.gradle index 79a69ac3e4d7..74483569a12b 100644 --- a/packages/webview_flutter/example/android/app/build.gradle +++ b/packages/webview_flutter/example/android/app/build.gradle @@ -55,6 +55,8 @@ flutter { } dependencies { + implementation 'androidx.lifecycle:lifecycle-runtime:2.1.0' + implementation 'androidx.lifecycle:lifecycle-common-java8:2.1.0' testImplementation 'junit:junit:4.12' androidTestImplementation 'androidx.test:runner:1.1.1' androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1' diff --git a/packages/webview_flutter/example/android/app/src/main/AndroidManifest.xml b/packages/webview_flutter/example/android/app/src/main/AndroidManifest.xml index 8fcbcd3908ba..fd570acc8959 100644 --- a/packages/webview_flutter/example/android/app/src/main/AndroidManifest.xml +++ b/packages/webview_flutter/example/android/app/src/main/AndroidManifest.xml @@ -1,39 +1,48 @@ + package="io.flutter.plugins.webviewflutterexample"> - - + + + + + + + + + + + + + - - - - - - - - - - - + + diff --git a/packages/webview_flutter/example/android/app/src/main/java/io/flutter/plugins/webviewflutterexample/EmbeddingV1Activity.java b/packages/webview_flutter/example/android/app/src/main/java/io/flutter/plugins/webviewflutterexample/EmbeddingV1Activity.java new file mode 100644 index 000000000000..1d2b58824494 --- /dev/null +++ b/packages/webview_flutter/example/android/app/src/main/java/io/flutter/plugins/webviewflutterexample/EmbeddingV1Activity.java @@ -0,0 +1,17 @@ +// Copyright 2018 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.webviewflutterexample; + +import android.os.Bundle; +import io.flutter.app.FlutterActivity; +import io.flutter.plugins.GeneratedPluginRegistrant; + +public class EmbeddingV1Activity extends FlutterActivity { + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + GeneratedPluginRegistrant.registerWith(this); + } +} diff --git a/packages/webview_flutter/example/android/app/src/main/java/io/flutter/plugins/webviewflutterexample/MainActivity.java b/packages/webview_flutter/example/android/app/src/main/java/io/flutter/plugins/webviewflutterexample/MainActivity.java index f935d0030483..66f20a343d7c 100644 --- a/packages/webview_flutter/example/android/app/src/main/java/io/flutter/plugins/webviewflutterexample/MainActivity.java +++ b/packages/webview_flutter/example/android/app/src/main/java/io/flutter/plugins/webviewflutterexample/MainActivity.java @@ -1,17 +1,12 @@ -// Copyright 2018 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.webviewflutterexample; -import android.os.Bundle; -import io.flutter.app.FlutterActivity; -import io.flutter.plugins.GeneratedPluginRegistrant; +import io.flutter.embedding.android.FlutterActivity; +import io.flutter.embedding.engine.FlutterEngine; +import io.flutter.plugins.webviewflutter.WebViewFlutterPlugin; public class MainActivity extends FlutterActivity { @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - GeneratedPluginRegistrant.registerWith(this); + public void configureFlutterEngine(FlutterEngine flutterEngine) { + flutterEngine.getPlugins().add(new WebViewFlutterPlugin()); } } diff --git a/packages/webview_flutter/example/android/gradle.properties b/packages/webview_flutter/example/android/gradle.properties index ad8917e962e5..d1d4d8d65dc4 100644 --- a/packages/webview_flutter/example/android/gradle.properties +++ b/packages/webview_flutter/example/android/gradle.properties @@ -1,2 +1,2 @@ org.gradle.jvmargs=-Xmx1536M -android.useAndroidX=true \ No newline at end of file +android.useAndroidX=trueandroid.enableR8=true diff --git a/packages/webview_flutter/example/android/settings_aar.gradle b/packages/webview_flutter/example/android/settings_aar.gradle new file mode 100644 index 000000000000..e7b4def49cb5 --- /dev/null +++ b/packages/webview_flutter/example/android/settings_aar.gradle @@ -0,0 +1 @@ +include ':app' From 0f24452c1031e52032b7ce4eebb845c938753173 Mon Sep 17 00:00:00 2001 From: Michael Klimushyn Date: Mon, 14 Oct 2019 15:10:30 -0700 Subject: [PATCH 2/4] Last migration fixes - Add the required lifecycle files into the dynamic Gradle inclusion script - Convert the FlutterDriver test to use the e2e package - Update the pubspec and CHANGELOG, including incrementing the minimum SDK version --- packages/webview_flutter/android/build.gradle | 25 +++++++ .../example/android/app/build.gradle | 7 +- .../EmbeddingV1ActivityTest.java | 13 ++++ .../MainActivityTest.java | 11 ++++ .../example/android/gradle.properties | 4 +- packages/webview_flutter/example/pubspec.yaml | 2 + ...{webview.dart => webview_flutter_e2e.dart} | 65 +++++++++---------- ...est.dart => webview_flutter_e2e_test.dart} | 5 +- packages/webview_flutter/pubspec.yaml | 2 +- 9 files changed, 92 insertions(+), 42 deletions(-) create mode 100644 packages/webview_flutter/example/android/app/src/androidTestDebug/java/io/flutter/plugins/webviewflutterexample/EmbeddingV1ActivityTest.java create mode 100644 packages/webview_flutter/example/android/app/src/androidTestDebug/java/io/flutter/plugins/webviewflutterexample/MainActivityTest.java rename packages/webview_flutter/example/test_driver/{webview.dart => webview_flutter_e2e.dart} (92%) rename packages/webview_flutter/example/test_driver/{webview_test.dart => webview_flutter_e2e_test.dart} (72%) diff --git a/packages/webview_flutter/android/build.gradle b/packages/webview_flutter/android/build.gradle index 4fe7629b5f76..0104ede0a418 100644 --- a/packages/webview_flutter/android/build.gradle +++ b/packages/webview_flutter/android/build.gradle @@ -50,3 +50,28 @@ android { implementation 'androidx.webkit:webkit:1.0.0' } } + +// TODO(mklim): Remove this hack once androidx.lifecycle is included on stable. https://github.com/flutter/flutter/issues/42348 +afterEvaluate { + def containsEmbeddingDependencies = false + for (def configuration : configurations.all) { + for (def dependency : configuration.dependencies) { + if (dependency.group == 'io.flutter' && + dependency.name.startsWith('flutter_embedding') && + dependency.isTransitive()) + { + containsEmbeddingDependencies = true + break + } + } + } + if (!containsEmbeddingDependencies) { + android { + dependencies { + def lifecycle_version = "2.1.0" + api "androidx.lifecycle:lifecycle-common-java8:$lifecycle_version" + api "androidx.lifecycle:lifecycle-runtime:$lifecycle_version" + } + } + } +} \ No newline at end of file diff --git a/packages/webview_flutter/example/android/app/build.gradle b/packages/webview_flutter/example/android/app/build.gradle index 74483569a12b..706d501c4060 100644 --- a/packages/webview_flutter/example/android/app/build.gradle +++ b/packages/webview_flutter/example/android/app/build.gradle @@ -55,9 +55,8 @@ flutter { } dependencies { - implementation 'androidx.lifecycle:lifecycle-runtime:2.1.0' - implementation 'androidx.lifecycle:lifecycle-common-java8:2.1.0' testImplementation 'junit:junit:4.12' - androidTestImplementation 'androidx.test:runner:1.1.1' - androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1' + androidTestImplementation 'androidx.test:runner:1.2.0' + androidTestImplementation 'androidx.test:rules:1.2.0' + androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0' } diff --git a/packages/webview_flutter/example/android/app/src/androidTestDebug/java/io/flutter/plugins/webviewflutterexample/EmbeddingV1ActivityTest.java b/packages/webview_flutter/example/android/app/src/androidTestDebug/java/io/flutter/plugins/webviewflutterexample/EmbeddingV1ActivityTest.java new file mode 100644 index 000000000000..fe10c6155e5a --- /dev/null +++ b/packages/webview_flutter/example/android/app/src/androidTestDebug/java/io/flutter/plugins/webviewflutterexample/EmbeddingV1ActivityTest.java @@ -0,0 +1,13 @@ +package io.flutter.plugins.webviewflutterexample; + +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 rule = + new ActivityTestRule<>(EmbeddingV1Activity.class); +} diff --git a/packages/webview_flutter/example/android/app/src/androidTestDebug/java/io/flutter/plugins/webviewflutterexample/MainActivityTest.java b/packages/webview_flutter/example/android/app/src/androidTestDebug/java/io/flutter/plugins/webviewflutterexample/MainActivityTest.java new file mode 100644 index 000000000000..a0bd4fe1a7f5 --- /dev/null +++ b/packages/webview_flutter/example/android/app/src/androidTestDebug/java/io/flutter/plugins/webviewflutterexample/MainActivityTest.java @@ -0,0 +1,11 @@ +package io.flutter.plugins.webviewflutterexample; + +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 MainActivityTest { + @Rule public ActivityTestRule rule = new ActivityTestRule<>(MainActivity.class); +} diff --git a/packages/webview_flutter/example/android/gradle.properties b/packages/webview_flutter/example/android/gradle.properties index d1d4d8d65dc4..a6738207fd15 100644 --- a/packages/webview_flutter/example/android/gradle.properties +++ b/packages/webview_flutter/example/android/gradle.properties @@ -1,2 +1,4 @@ org.gradle.jvmargs=-Xmx1536M -android.useAndroidX=trueandroid.enableR8=true +android.useAndroidX=true +android.enableJetifier=true +android.enableR8=true diff --git a/packages/webview_flutter/example/pubspec.yaml b/packages/webview_flutter/example/pubspec.yaml index 8657cfde0f8b..e5cf098598ae 100644 --- a/packages/webview_flutter/example/pubspec.yaml +++ b/packages/webview_flutter/example/pubspec.yaml @@ -5,6 +5,7 @@ version: 1.0.1 environment: sdk: ">=2.0.0-dev.68.0 <3.0.0" + flutter: ">=1.9.1+hotfix.2 <2.0.0" dependencies: flutter: @@ -17,6 +18,7 @@ dev_dependencies: sdk: flutter flutter_driver: sdk: flutter + e2e: "^0.2.0" flutter: uses-material-design: true diff --git a/packages/webview_flutter/example/test_driver/webview.dart b/packages/webview_flutter/example/test_driver/webview_flutter_e2e.dart similarity index 92% rename from packages/webview_flutter/example/test_driver/webview.dart rename to packages/webview_flutter/example/test_driver/webview_flutter_e2e.dart index e24afd73f557..a5d4d7de66fe 100644 --- a/packages/webview_flutter/example/test_driver/webview.dart +++ b/packages/webview_flutter/example/test_driver/webview_flutter_e2e.dart @@ -9,19 +9,17 @@ import 'dart:typed_data'; import 'package:flutter/foundation.dart'; import 'package:flutter/services.dart'; import 'package:flutter/widgets.dart'; -import 'package:flutter_driver/driver_extension.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:webview_flutter/webview_flutter.dart'; +import 'package:e2e/e2e.dart'; void main() { - final Completer allTestsCompleter = Completer(); - enableFlutterDriverExtension(handler: (_) => allTestsCompleter.future); - tearDownAll(() => allTestsCompleter.complete(null)); + E2EWidgetsFlutterBinding.ensureInitialized(); - test('initalUrl', () async { + testWidgets('initalUrl', (WidgetTester tester) async { final Completer controllerCompleter = Completer(); - await pumpWidget( + await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, child: WebView( @@ -38,10 +36,10 @@ void main() { expect(currentUrl, 'https://flutter.dev/'); }); - test('loadUrl', () async { + testWidgets('loadUrl', (WidgetTester tester) async { final Completer controllerCompleter = Completer(); - await pumpWidget( + await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, child: WebView( @@ -61,11 +59,11 @@ void main() { // enable this once https://github.com/flutter/flutter/issues/31510 // is resolved. - test('loadUrl with headers', () async { + testWidgets('loadUrl with headers', (WidgetTester tester) async { final Completer controllerCompleter = Completer(); final StreamController pageLoads = StreamController(); - await pumpWidget( + await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, child: WebView( @@ -96,12 +94,12 @@ void main() { expect(content.contains('flutter_test_header'), isTrue); }); - test('JavaScriptChannel', () async { + testWidgets('JavaScriptChannel', (WidgetTester tester) async { final Completer controllerCompleter = Completer(); final Completer pageLoaded = Completer(); final List messagesReceived = []; - await pumpWidget( + await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, child: WebView( @@ -137,7 +135,7 @@ void main() { expect(messagesReceived, equals(['hello'])); }); - test('resize webview', () async { + testWidgets('resize webview', (WidgetTester tester) async { final String resizeTest = ''' Resize test @@ -184,7 +182,7 @@ void main() { javascriptMode: JavascriptMode.unrestricted, ); - await pumpWidget( + await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, child: Column( @@ -204,7 +202,7 @@ void main() { expect(resizeCompleter.isCompleted, false); - await pumpWidget( + await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, child: Column( @@ -222,11 +220,11 @@ void main() { await resizeCompleter.future; }); - test('set custom userAgent', () async { + testWidgets('set custom userAgent', (WidgetTester tester) async { final Completer controllerCompleter1 = Completer(); final GlobalKey _globalKey = GlobalKey(); - await pumpWidget( + await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, child: WebView( @@ -244,7 +242,7 @@ void main() { final String customUserAgent1 = await _getUserAgent(controller1); expect(customUserAgent1, 'Custom_User_Agent1'); // rebuild the WebView with a different user agent. - await pumpWidget( + await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, child: WebView( @@ -260,12 +258,13 @@ void main() { expect(customUserAgent2, 'Custom_User_Agent2'); }); - test('use default platform userAgent after webView is rebuilt', () async { + testWidgets('use default platform userAgent after webView is rebuilt', + (WidgetTester tester) async { final Completer controllerCompleter = Completer(); final GlobalKey _globalKey = GlobalKey(); // Build the webView with no user agent to get the default platform user agent. - await pumpWidget( + await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, child: WebView( @@ -281,7 +280,7 @@ void main() { final WebViewController controller = await controllerCompleter.future; final String defaultPlatformUserAgent = await _getUserAgent(controller); // rebuild the WebView with a custom user agent. - await pumpWidget( + await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, child: WebView( @@ -295,7 +294,7 @@ void main() { final String customUserAgent = await _getUserAgent(controller); expect(customUserAgent, 'Custom_User_Agent'); // rebuilds the WebView with no user agent. - await pumpWidget( + await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, child: WebView( @@ -341,12 +340,12 @@ void main() { audioTestBase64 = base64Encode(const Utf8Encoder().convert(audioTest)); }); - test('Auto media playback', () async { + testWidgets('Auto media playback', (WidgetTester tester) async { Completer controllerCompleter = Completer(); Completer pageLoaded = Completer(); - await pumpWidget( + await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, child: WebView( @@ -373,7 +372,7 @@ void main() { pageLoaded = Completer(); // We change the key to re-create a new webview as we change the initialMediaPlaybackPolicy - await pumpWidget( + await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, child: WebView( @@ -399,13 +398,14 @@ void main() { expect(isPaused, _webviewBool(true)); }); - test('Changes to initialMediaPlaybackPolocy are ignored', () async { + testWidgets('Changes to initialMediaPlaybackPolocy are ignored', + (WidgetTester tester) async { final Completer controllerCompleter = Completer(); Completer pageLoaded = Completer(); final GlobalKey key = GlobalKey(); - await pumpWidget( + await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, child: WebView( @@ -430,7 +430,7 @@ void main() { pageLoaded = Completer(); - await pumpWidget( + await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, child: WebView( @@ -458,7 +458,7 @@ void main() { }); }); - test('getTitle', () async { + testWidgets('getTitle', (WidgetTester tester) async { final String getTitleTest = ''' Some title @@ -473,7 +473,7 @@ void main() { final Completer controllerCompleter = Completer(); - await pumpWidget( + await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, child: WebView( @@ -496,11 +496,6 @@ void main() { }); } -Future pumpWidget(Widget widget) { - runApp(widget); - return WidgetsBinding.instance.endOfFrame; -} - // JavaScript booleans evaluate to different string values on Android and iOS. // This utility method returns the string boolean value of the current platform. String _webviewBool(bool value) { diff --git a/packages/webview_flutter/example/test_driver/webview_test.dart b/packages/webview_flutter/example/test_driver/webview_flutter_e2e_test.dart similarity index 72% rename from packages/webview_flutter/example/test_driver/webview_test.dart rename to packages/webview_flutter/example/test_driver/webview_flutter_e2e_test.dart index b0d3305cd652..2e5c27fd402e 100644 --- a/packages/webview_flutter/example/test_driver/webview_test.dart +++ b/packages/webview_flutter/example/test_driver/webview_flutter_e2e_test.dart @@ -3,11 +3,14 @@ // 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 main() async { final FlutterDriver driver = await FlutterDriver.connect(); - await driver.requestData(null, timeout: const Duration(minutes: 1)); + final String result = + await driver.requestData(null, timeout: const Duration(minutes: 1)); driver.close(); + exit(result == 'pass' ? 0 : 1); } diff --git a/packages/webview_flutter/pubspec.yaml b/packages/webview_flutter/pubspec.yaml index 13ccef744009..68f27a2e384d 100644 --- a/packages/webview_flutter/pubspec.yaml +++ b/packages/webview_flutter/pubspec.yaml @@ -6,7 +6,7 @@ homepage: https://github.com/flutter/plugins/tree/master/packages/webview_flutte environment: sdk: ">=2.0.0-dev.68.0 <3.0.0" - flutter: ">=1.5.0 <2.0.0" + flutter: ">=1.6.7 <2.0.0" dependencies: flutter: From f73bfd8ff2f9032a3ee2551504bce9a443510713 Mon Sep 17 00:00:00 2001 From: Michael Klimushyn Date: Mon, 14 Oct 2019 16:43:56 -0700 Subject: [PATCH 3/4] Review feedback --- .../webviewflutter/FlutterCookieManager.java | 4 ---- .../plugins/webviewflutter/InputAwareWebView.java | 2 +- .../webviewflutter/WebViewFlutterPlugin.java | 14 +++++++++----- .../webviewflutterexample/EmbeddingV1Activity.java | 2 +- .../webviewflutterexample/MainActivity.java | 5 +++++ .../example/android/settings_aar.gradle | 1 - packages/webview_flutter/example/pubspec.yaml | 2 +- 7 files changed, 17 insertions(+), 13 deletions(-) delete mode 100644 packages/webview_flutter/example/android/settings_aar.gradle diff --git a/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/FlutterCookieManager.java b/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/FlutterCookieManager.java index f9bbb93b2240..86b4fd412a29 100644 --- a/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/FlutterCookieManager.java +++ b/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/FlutterCookieManager.java @@ -22,10 +22,6 @@ class FlutterCookieManager implements MethodCallHandler { methodChannel.setMethodCallHandler(this); } - static void registerWith(BinaryMessenger messenger) { - new FlutterCookieManager(messenger); - } - @Override public void onMethodCall(MethodCall methodCall, Result result) { switch (methodCall.method) { diff --git a/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/InputAwareWebView.java b/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/InputAwareWebView.java index a8168a99b2b3..e04d566bdc92 100644 --- a/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/InputAwareWebView.java +++ b/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/InputAwareWebView.java @@ -41,7 +41,7 @@ void setContainerView(@Nullable View containerView) { return; } - Log.e(TAG, "The containerView has changed while the proxyAdapterView exists."); + Log.w(TAG, "The containerView has changed while the proxyAdapterView exists."); if (containerView != null) { setInputConnectionTarget(proxyAdapterView); } diff --git a/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/WebViewFlutterPlugin.java b/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/WebViewFlutterPlugin.java index 43214b4c45b8..dcce7962aed5 100644 --- a/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/WebViewFlutterPlugin.java +++ b/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/WebViewFlutterPlugin.java @@ -13,17 +13,21 @@ /** * Java platform implementation of the webview_flutter plugin. * - *

Instantiate this in an add to app scenario to gracefully handle activity and context changes. + *

Register this in an add to app scenario to gracefully handle activity and context changes. * - *

Call registerWith to use the stable {@code io.flutter.plugin.common} package instead. + *

Call {@link #registerWith(Registrar)} to use the stable {@code io.flutter.plugin.common} + * package instead. */ public class WebViewFlutterPlugin implements FlutterPlugin { + private @Nullable FlutterCookieManager flutterCookieManager; /** - * Initialize this within the {@code #configureFlutterEngine} of a Flutter activity or fragment. + * Add an instance of this to {@link io.flutter.embedding.engine.plugins.PluginRegistry} to + * register it. * - *

See {@code io.flutter.plugins.webviewflutterexample.MainActivity} for an example. + *

Registration should eventually be handled automatically by v2 of the + * GeneratedPluginRegistrant. https://github.com/flutter/flutter/issues/42694 */ public WebViewFlutterPlugin() {} @@ -40,7 +44,7 @@ public static void registerWith(Registrar registrar) { .registerViewFactory( "plugins.flutter.io/webview", new WebViewFactory(registrar.messenger(), registrar.view())); - FlutterCookieManager.registerWith(registrar.messenger()); + new FlutterCookieManager(registrar.messenger()); } @Override diff --git a/packages/webview_flutter/example/android/app/src/main/java/io/flutter/plugins/webviewflutterexample/EmbeddingV1Activity.java b/packages/webview_flutter/example/android/app/src/main/java/io/flutter/plugins/webviewflutterexample/EmbeddingV1Activity.java index 1d2b58824494..9b868934cc10 100644 --- a/packages/webview_flutter/example/android/app/src/main/java/io/flutter/plugins/webviewflutterexample/EmbeddingV1Activity.java +++ b/packages/webview_flutter/example/android/app/src/main/java/io/flutter/plugins/webviewflutterexample/EmbeddingV1Activity.java @@ -1,4 +1,4 @@ -// Copyright 2018 The Chromium Authors. All rights reserved. +// Copyright 2019 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. diff --git a/packages/webview_flutter/example/android/app/src/main/java/io/flutter/plugins/webviewflutterexample/MainActivity.java b/packages/webview_flutter/example/android/app/src/main/java/io/flutter/plugins/webviewflutterexample/MainActivity.java index 66f20a343d7c..1596844948e7 100644 --- a/packages/webview_flutter/example/android/app/src/main/java/io/flutter/plugins/webviewflutterexample/MainActivity.java +++ b/packages/webview_flutter/example/android/app/src/main/java/io/flutter/plugins/webviewflutterexample/MainActivity.java @@ -1,3 +1,7 @@ +// Copyright 2018 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.webviewflutterexample; import io.flutter.embedding.android.FlutterActivity; @@ -5,6 +9,7 @@ import io.flutter.plugins.webviewflutter.WebViewFlutterPlugin; public class MainActivity extends FlutterActivity { + // TODO(mklim): Remove this once v2 of GeneratedPluginRegistrant rolls to stable. https://github.com/flutter/flutter/issues/42694 @Override public void configureFlutterEngine(FlutterEngine flutterEngine) { flutterEngine.getPlugins().add(new WebViewFlutterPlugin()); diff --git a/packages/webview_flutter/example/android/settings_aar.gradle b/packages/webview_flutter/example/android/settings_aar.gradle deleted file mode 100644 index e7b4def49cb5..000000000000 --- a/packages/webview_flutter/example/android/settings_aar.gradle +++ /dev/null @@ -1 +0,0 @@ -include ':app' diff --git a/packages/webview_flutter/example/pubspec.yaml b/packages/webview_flutter/example/pubspec.yaml index e5cf098598ae..93ea78b74af0 100644 --- a/packages/webview_flutter/example/pubspec.yaml +++ b/packages/webview_flutter/example/pubspec.yaml @@ -5,7 +5,7 @@ version: 1.0.1 environment: sdk: ">=2.0.0-dev.68.0 <3.0.0" - flutter: ">=1.9.1+hotfix.2 <2.0.0" + flutter: ">=1.9.1+hotfix.4 <2.0.0" dependencies: flutter: From 6ced26685b29f26467738bf551e05bddf719513f Mon Sep 17 00:00:00 2001 From: Michael Klimushyn Date: Mon, 14 Oct 2019 16:51:19 -0700 Subject: [PATCH 4/4] Increment pubspec/changelog --- packages/webview_flutter/CHANGELOG.md | 8 +++++++- packages/webview_flutter/example/pubspec.yaml | 2 +- packages/webview_flutter/pubspec.yaml | 2 +- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/packages/webview_flutter/CHANGELOG.md b/packages/webview_flutter/CHANGELOG.md index 9822ad511f0e..7710f748fca5 100644 --- a/packages/webview_flutter/CHANGELOG.md +++ b/packages/webview_flutter/CHANGELOG.md @@ -1,3 +1,9 @@ +## 0.3.15 + +* Add support for the v2 Android embedding. This shouldn't affect existing + functionality. Plugin authors who use the V2 embedding can now register the + plugin and expect that it correctly responds to app lifecycle changes. + ## 0.3.14+2 * Define clang module for iOS. @@ -13,7 +19,7 @@ ## 0.3.13 * Add an optional `userAgent` property to set a custom User Agent. - + ## 0.3.12+1 * Temporarily revert getTitle (doing this as a patch bump shortly after publishing). diff --git a/packages/webview_flutter/example/pubspec.yaml b/packages/webview_flutter/example/pubspec.yaml index 93ea78b74af0..0e24333cfc0b 100644 --- a/packages/webview_flutter/example/pubspec.yaml +++ b/packages/webview_flutter/example/pubspec.yaml @@ -1,7 +1,7 @@ name: webview_flutter_example description: Demonstrates how to use the webview_flutter plugin. -version: 1.0.1 +version: 1.0.2 environment: sdk: ">=2.0.0-dev.68.0 <3.0.0" diff --git a/packages/webview_flutter/pubspec.yaml b/packages/webview_flutter/pubspec.yaml index 68f27a2e384d..efddfd3b7aec 100644 --- a/packages/webview_flutter/pubspec.yaml +++ b/packages/webview_flutter/pubspec.yaml @@ -1,6 +1,6 @@ name: webview_flutter description: A Flutter plugin that provides a WebView widget on Android and iOS. -version: 0.3.14+2 +version: 0.3.15 author: Flutter Team homepage: https://github.com/flutter/plugins/tree/master/packages/webview_flutter