Skip to content
This repository was archived by the owner on Feb 22, 2023. It is now read-only.

Commit 573c9ff

Browse files
author
Michael Klimushyn
authored
[flutter_webview] Migrate to the new embedding (#2169)
This depends on a new bugfix on the engine for text input to work with the new embedding (flutter/engine#13015).
1 parent 0992077 commit 573c9ff

File tree

19 files changed

+284
-104
lines changed

19 files changed

+284
-104
lines changed

packages/webview_flutter/CHANGELOG.md

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
1+
## 0.3.15
2+
3+
* Add support for the v2 Android embedding. This shouldn't affect existing
4+
functionality. Plugin authors who use the V2 embedding can now register the
5+
plugin and expect that it correctly responds to app lifecycle changes.
6+
17
## 0.3.14+2
28

39
* Define clang module for iOS.
@@ -13,7 +19,7 @@
1319
## 0.3.13
1420

1521
* Add an optional `userAgent` property to set a custom User Agent.
16-
22+
1723
## 0.3.12+1
1824

1925
* Temporarily revert getTitle (doing this as a patch bump shortly after publishing).

packages/webview_flutter/android/build.gradle

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,3 +50,28 @@ android {
5050
implementation 'androidx.webkit:webkit:1.0.0'
5151
}
5252
}
53+
54+
// TODO(mklim): Remove this hack once androidx.lifecycle is included on stable. https://github.com/flutter/flutter/issues/42348
55+
afterEvaluate {
56+
def containsEmbeddingDependencies = false
57+
for (def configuration : configurations.all) {
58+
for (def dependency : configuration.dependencies) {
59+
if (dependency.group == 'io.flutter' &&
60+
dependency.name.startsWith('flutter_embedding') &&
61+
dependency.isTransitive())
62+
{
63+
containsEmbeddingDependencies = true
64+
break
65+
}
66+
}
67+
}
68+
if (!containsEmbeddingDependencies) {
69+
android {
70+
dependencies {
71+
def lifecycle_version = "2.1.0"
72+
api "androidx.lifecycle:lifecycle-common-java8:$lifecycle_version"
73+
api "androidx.lifecycle:lifecycle-runtime:$lifecycle_version"
74+
}
75+
}
76+
}
77+
}
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,3 @@
11
org.gradle.jvmargs=-Xmx1536M
2+
android.enableJetifier=true
3+
android.useAndroidX=true

packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/FlutterCookieManager.java

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -15,17 +15,11 @@
1515
import io.flutter.plugin.common.MethodChannel.Result;
1616

1717
class FlutterCookieManager implements MethodCallHandler {
18+
private final MethodChannel methodChannel;
1819

19-
private FlutterCookieManager() {
20-
// Do not instantiate.
21-
// This class should only be used in context of a BinaryMessenger.
22-
// Use FlutterCookieManager#registerWith instead.
23-
}
24-
25-
static void registerWith(BinaryMessenger messenger) {
26-
MethodChannel methodChannel = new MethodChannel(messenger, "plugins.flutter.io/cookie_manager");
27-
FlutterCookieManager cookieManager = new FlutterCookieManager();
28-
methodChannel.setMethodCallHandler(cookieManager);
20+
FlutterCookieManager(BinaryMessenger messenger) {
21+
methodChannel = new MethodChannel(messenger, "plugins.flutter.io/cookie_manager");
22+
methodChannel.setMethodCallHandler(this);
2923
}
3024

3125
@Override
@@ -39,6 +33,10 @@ public void onMethodCall(MethodCall methodCall, Result result) {
3933
}
4034
}
4135

36+
void dispose() {
37+
methodChannel.setMethodCallHandler(null);
38+
}
39+
4240
private static void clearCookies(final Result result) {
4341
CookieManager cookieManager = CookieManager.getInstance();
4442
final boolean hasCookies = cookieManager.hasCookies();

packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/FlutterWebView.java

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212
import android.view.View;
1313
import android.webkit.WebStorage;
1414
import android.webkit.WebViewClient;
15+
import androidx.annotation.NonNull;
16+
import androidx.annotation.Nullable;
1517
import io.flutter.plugin.common.BinaryMessenger;
1618
import io.flutter.plugin.common.MethodCall;
1719
import io.flutter.plugin.common.MethodChannel;
@@ -36,7 +38,7 @@ public class FlutterWebView implements PlatformView, MethodCallHandler {
3638
BinaryMessenger messenger,
3739
int id,
3840
Map<String, Object> params,
39-
final View containerView) {
41+
@Nullable View containerView) {
4042

4143
DisplayListenerProxy displayListenerProxy = new DisplayListenerProxy();
4244
DisplayManager displayManager =
@@ -95,6 +97,16 @@ public void onInputConnectionLocked() {
9597
webView.lockInputConnection();
9698
}
9799

100+
@Override
101+
public void onFlutterViewAttached(@NonNull View flutterView) {
102+
webView.setContainerView(flutterView);
103+
}
104+
105+
@Override
106+
public void onFlutterViewDetached() {
107+
webView.setContainerView(null);
108+
}
109+
98110
@Override
99111
public void onMethodCall(MethodCall methodCall, Result result) {
100112
switch (methodCall.method) {

packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/InputAwareWebView.java

Lines changed: 35 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,11 @@
77
import static android.content.Context.INPUT_METHOD_SERVICE;
88

99
import android.content.Context;
10+
import android.util.Log;
1011
import android.view.View;
1112
import android.view.inputmethod.InputMethodManager;
1213
import android.webkit.WebView;
14+
import androidx.annotation.Nullable;
1315

1416
/**
1517
* A WebView subclass that mirrors the same implementation hacks that the system WebView does in
@@ -22,16 +24,29 @@
2224
* <p>See also {@link ThreadedInputConnectionProxyAdapterView}.
2325
*/
2426
final class InputAwareWebView extends WebView {
25-
private final View containerView;
26-
27+
private static final String TAG = "InputAwareWebView";
2728
private View threadedInputConnectionProxyView;
2829
private ThreadedInputConnectionProxyAdapterView proxyAdapterView;
30+
private @Nullable View containerView;
2931

30-
InputAwareWebView(Context context, View containerView) {
32+
InputAwareWebView(Context context, @Nullable View containerView) {
3133
super(context);
3234
this.containerView = containerView;
3335
}
3436

37+
void setContainerView(@Nullable View containerView) {
38+
this.containerView = containerView;
39+
40+
if (proxyAdapterView == null) {
41+
return;
42+
}
43+
44+
Log.w(TAG, "The containerView has changed while the proxyAdapterView exists.");
45+
if (containerView != null) {
46+
setInputConnectionTarget(proxyAdapterView);
47+
}
48+
}
49+
3550
/**
3651
* Set our proxy adapter view to use its cached input connection instead of creating new ones.
3752
*
@@ -81,6 +96,12 @@ public boolean checkInputConnectionProxy(final View view) {
8196
// This isn't a new ThreadedInputConnectionProxyView. Ignore it.
8297
return super.checkInputConnectionProxy(view);
8398
}
99+
if (containerView == null) {
100+
Log.e(
101+
TAG,
102+
"Can't create a proxy view because there's no container view. Text input may not work.");
103+
return super.checkInputConnectionProxy(view);
104+
}
84105

85106
// We've never seen this before, so we make the assumption that this is WebView's
86107
// ThreadedInputConnectionProxyView. We are making the assumption that the only view that could
@@ -120,6 +141,10 @@ private void resetInputConnection() {
120141
// No need to reset the InputConnection to the default thread if we've never changed it.
121142
return;
122143
}
144+
if (containerView == null) {
145+
Log.e(TAG, "Can't reset the input connection to the container view because there is none.");
146+
return;
147+
}
123148
setInputConnectionTarget(/*targetView=*/ containerView);
124149
}
125150

@@ -132,6 +157,13 @@ private void resetInputConnection() {
132157
* InputConnections should be created on.
133158
*/
134159
private void setInputConnectionTarget(final View targetView) {
160+
if (containerView == null) {
161+
Log.e(
162+
TAG,
163+
"Can't set the input connection target because there is no containerView to use as a handler.");
164+
return;
165+
}
166+
135167
targetView.requestFocus();
136168
containerView.post(
137169
new Runnable() {

packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/WebViewFactory.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
import android.content.Context;
88
import android.view.View;
9+
import androidx.annotation.Nullable;
910
import io.flutter.plugin.common.BinaryMessenger;
1011
import io.flutter.plugin.common.StandardMessageCodec;
1112
import io.flutter.plugin.platform.PlatformView;
@@ -14,9 +15,9 @@
1415

1516
public final class WebViewFactory extends PlatformViewFactory {
1617
private final BinaryMessenger messenger;
17-
private final View containerView;
18+
private @Nullable final View containerView;
1819

19-
WebViewFactory(BinaryMessenger messenger, View containerView) {
20+
WebViewFactory(BinaryMessenger messenger, @Nullable View containerView) {
2021
super(StandardMessageCodec.INSTANCE);
2122
this.messenger = messenger;
2223
this.containerView = containerView;

packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/WebViewFlutterPlugin.java

Lines changed: 55 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,17 +4,68 @@
44

55
package io.flutter.plugins.webviewflutter;
66

7+
import androidx.annotation.NonNull;
8+
import androidx.annotation.Nullable;
9+
import io.flutter.embedding.engine.plugins.FlutterPlugin;
10+
import io.flutter.plugin.common.BinaryMessenger;
711
import io.flutter.plugin.common.PluginRegistry.Registrar;
812

9-
/** WebViewFlutterPlugin */
10-
public class WebViewFlutterPlugin {
11-
/** Plugin registration. */
13+
/**
14+
* Java platform implementation of the webview_flutter plugin.
15+
*
16+
* <p>Register this in an add to app scenario to gracefully handle activity and context changes.
17+
*
18+
* <p>Call {@link #registerWith(Registrar)} to use the stable {@code io.flutter.plugin.common}
19+
* package instead.
20+
*/
21+
public class WebViewFlutterPlugin implements FlutterPlugin {
22+
23+
private @Nullable FlutterCookieManager flutterCookieManager;
24+
25+
/**
26+
* Add an instance of this to {@link io.flutter.embedding.engine.plugins.PluginRegistry} to
27+
* register it.
28+
*
29+
* <p>Registration should eventually be handled automatically by v2 of the
30+
* GeneratedPluginRegistrant. https://github.com/flutter/flutter/issues/42694
31+
*/
32+
public WebViewFlutterPlugin() {}
33+
34+
/**
35+
* Registers a plugin implementation that uses the stable {@code io.flutter.plugin.common}
36+
* package.
37+
*
38+
* <p>Calling this automatically initializes the plugin. However plugins initialized this way
39+
* won't react to changes in activity or context, unlike {@link CameraPlugin}.
40+
*/
1241
public static void registerWith(Registrar registrar) {
1342
registrar
1443
.platformViewRegistry()
1544
.registerViewFactory(
1645
"plugins.flutter.io/webview",
1746
new WebViewFactory(registrar.messenger(), registrar.view()));
18-
FlutterCookieManager.registerWith(registrar.messenger());
47+
new FlutterCookieManager(registrar.messenger());
48+
}
49+
50+
@Override
51+
public void onAttachedToEngine(@NonNull FlutterPluginBinding binding) {
52+
BinaryMessenger messenger = binding.getFlutterEngine().getDartExecutor();
53+
binding
54+
.getFlutterEngine()
55+
.getPlatformViewsController()
56+
.getRegistry()
57+
.registerViewFactory(
58+
"plugins.flutter.io/webview", new WebViewFactory(messenger, /*containerView=*/ null));
59+
flutterCookieManager = new FlutterCookieManager(messenger);
60+
}
61+
62+
@Override
63+
public void onDetachedFromEngine(@NonNull FlutterPluginBinding binding) {
64+
if (flutterCookieManager == null) {
65+
return;
66+
}
67+
68+
flutterCookieManager.dispose();
69+
flutterCookieManager = null;
1970
}
2071
}

packages/webview_flutter/example/android/app/build.gradle

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ flutter {
5656

5757
dependencies {
5858
testImplementation 'junit:junit:4.12'
59-
androidTestImplementation 'androidx.test:runner:1.1.1'
60-
androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1'
59+
androidTestImplementation 'androidx.test:runner:1.2.0'
60+
androidTestImplementation 'androidx.test:rules:1.2.0'
61+
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
6162
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package io.flutter.plugins.webviewflutterexample;
2+
3+
import androidx.test.rule.ActivityTestRule;
4+
import dev.flutter.plugins.e2e.FlutterRunner;
5+
import org.junit.Rule;
6+
import org.junit.runner.RunWith;
7+
8+
@RunWith(FlutterRunner.class)
9+
public class EmbeddingV1ActivityTest {
10+
@Rule
11+
public ActivityTestRule<EmbeddingV1Activity> rule =
12+
new ActivityTestRule<>(EmbeddingV1Activity.class);
13+
}

0 commit comments

Comments
 (0)