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

Commit f31d16a

Browse files
Michael Klimushynmatthew-carroll
Michael Klimushyn
authored andcommitted
[webview_flutter] Re-land support v2 embedding support (#2209)
1 parent 451a689 commit f31d16a

File tree

19 files changed

+295
-103
lines changed

19 files changed

+295
-103
lines changed

packages/webview_flutter/CHANGELOG.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,11 @@
1+
## 0.3.15+3
2+
3+
* Re-land support for the v2 Android embedding. This correctly sets the minimum
4+
SDK to the latest stable and avoid any compile errors. *WARNING:* the V2
5+
embedding itself still requires the current Flutter master channel
6+
(flutter/flutter@1d4d63a) for text input to work properly on all Android
7+
versions.
8+
19
## 0.3.15+2
210

311
* Remove AndroidX warnings.

packages/webview_flutter/android/build.gradle

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,3 +37,28 @@ android {
3737
implementation 'androidx.webkit:webkit:1.0.0'
3838
}
3939
}
40+
41+
// TODO(mklim): Remove this hack once androidx.lifecycle is included on stable. https://github.com/flutter/flutter/issues/42348
42+
afterEvaluate {
43+
def containsEmbeddingDependencies = false
44+
for (def configuration : configurations.all) {
45+
for (def dependency : configuration.dependencies) {
46+
if (dependency.group == 'io.flutter' &&
47+
dependency.name.startsWith('flutter_embedding') &&
48+
dependency.isTransitive())
49+
{
50+
containsEmbeddingDependencies = true
51+
break
52+
}
53+
}
54+
}
55+
if (!containsEmbeddingDependencies) {
56+
android {
57+
dependencies {
58+
def lifecycle_version = "1.1.1"
59+
compileOnly "android.arch.lifecycle:common-java8:$lifecycle_version"
60+
compileOnly "android.arch.lifecycle:runtime:$lifecycle_version"
61+
}
62+
}
63+
}
64+
}

packages/webview_flutter/android/gradle.properties

Lines changed: 0 additions & 1 deletion
This file was deleted.

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: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ public class FlutterWebView implements PlatformView, MethodCallHandler {
3636
BinaryMessenger messenger,
3737
int id,
3838
Map<String, Object> params,
39-
final View containerView) {
39+
View containerView) {
4040

4141
DisplayListenerProxy displayListenerProxy = new DisplayListenerProxy();
4242
DisplayManager displayManager =
@@ -95,6 +95,26 @@ public void onInputConnectionLocked() {
9595
webView.lockInputConnection();
9696
}
9797

98+
// @Override
99+
// This is overriding a method that hasn't rolled into stable Flutter yet. Including the
100+
// annotation would cause compile time failures in versions of Flutter too old to include the new
101+
// method. However leaving it raw like this means that the method will be ignored in old versions
102+
// of Flutter but used as an override anyway wherever it's actually defined.
103+
// TODO(mklim): Add the @Override annotation once stable passes v1.10.9.
104+
public void onFlutterViewAttached(View flutterView) {
105+
webView.setContainerView(flutterView);
106+
}
107+
108+
// @Override
109+
// This is overriding a method that hasn't rolled into stable Flutter yet. Including the
110+
// annotation would cause compile time failures in versions of Flutter too old to include the new
111+
// method. However leaving it raw like this means that the method will be ignored in old versions
112+
// of Flutter but used as an override anyway wherever it's actually defined.
113+
// TODO(mklim): Add the @Override annotation once stable passes v1.10.9.
114+
public void onFlutterViewDetached() {
115+
webView.setContainerView(null);
116+
}
117+
98118
@Override
99119
public void onMethodCall(MethodCall methodCall, Result result) {
100120
switch (methodCall.method) {

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

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@
1111
import android.webkit.WebResourceRequest;
1212
import android.webkit.WebView;
1313
import android.webkit.WebViewClient;
14-
import androidx.annotation.NonNull;
1514
import androidx.webkit.WebViewClientCompat;
1615
import io.flutter.plugin.common.MethodChannel;
1716
import java.util.HashMap;
@@ -124,8 +123,7 @@ public void onUnhandledKeyEvent(WebView view, KeyEvent event) {
124123
private WebViewClientCompat internalCreateWebViewClientCompat() {
125124
return new WebViewClientCompat() {
126125
@Override
127-
public boolean shouldOverrideUrlLoading(
128-
@NonNull WebView view, @NonNull WebResourceRequest request) {
126+
public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) {
129127
return FlutterWebViewClient.this.shouldOverrideUrlLoading(view, request);
130128
}
131129

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

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
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;
@@ -22,16 +23,29 @@
2223
* <p>See also {@link ThreadedInputConnectionProxyAdapterView}.
2324
*/
2425
final class InputAwareWebView extends WebView {
25-
private final View containerView;
26-
26+
private static final String TAG = "InputAwareWebView";
2727
private View threadedInputConnectionProxyView;
2828
private ThreadedInputConnectionProxyAdapterView proxyAdapterView;
29+
private View containerView;
2930

3031
InputAwareWebView(Context context, View containerView) {
3132
super(context);
3233
this.containerView = containerView;
3334
}
3435

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

85105
// We've never seen this before, so we make the assumption that this is WebView's
86106
// ThreadedInputConnectionProxyView. We are making the assumption that the only view that could
@@ -120,6 +140,10 @@ private void resetInputConnection() {
120140
// No need to reset the InputConnection to the default thread if we've never changed it.
121141
return;
122142
}
143+
if (containerView == null) {
144+
Log.e(TAG, "Can't reset the input connection to the container view because there is none.");
145+
return;
146+
}
123147
setInputConnectionTarget(/*targetView=*/ containerView);
124148
}
125149

@@ -132,6 +156,13 @@ private void resetInputConnection() {
132156
* InputConnections should be created on.
133157
*/
134158
private void setInputConnectionTarget(final View targetView) {
159+
if (containerView == null) {
160+
Log.e(
161+
TAG,
162+
"Can't set the input connection target because there is no containerView to use as a handler.");
163+
return;
164+
}
165+
135166
targetView.requestFocus();
136167
containerView.post(
137168
new Runnable() {

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

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

55
package io.flutter.plugins.webviewflutter;
66

7+
import io.flutter.embedding.engine.plugins.FlutterPlugin;
8+
import io.flutter.plugin.common.BinaryMessenger;
79
import io.flutter.plugin.common.PluginRegistry.Registrar;
810

9-
/** WebViewFlutterPlugin */
10-
public class WebViewFlutterPlugin {
11-
/** Plugin registration. */
11+
/**
12+
* Java platform implementation of the webview_flutter plugin.
13+
*
14+
* <p>Register this in an add to app scenario to gracefully handle activity and context changes.
15+
*
16+
* <p>Call {@link #registerWith(Registrar)} to use the stable {@code io.flutter.plugin.common}
17+
* package instead.
18+
*/
19+
public class WebViewFlutterPlugin implements FlutterPlugin {
20+
21+
private FlutterCookieManager flutterCookieManager;
22+
23+
/**
24+
* Add an instance of this to {@link io.flutter.embedding.engine.plugins.PluginRegistry} to
25+
* register it.
26+
*
27+
* <p>THIS PLUGIN CODE PATH DEPENDS ON A NEWER VERSION OF FLUTTER THAN THE ONE DEFINED IN THE
28+
* PUBSPEC.YAML. Text input will fail on some Android devices unless this is used with at least
29+
* flutter/flutter@1d4d63ace1f801a022ea9ec737bf8c15395588b9. Use the V1 embedding with {@link
30+
* #registerWith(Registrar)} to use this plugin with older Flutter versions.
31+
*
32+
* <p>Registration should eventually be handled automatically by v2 of the
33+
* GeneratedPluginRegistrant. https://github.com/flutter/flutter/issues/42694
34+
*/
35+
public WebViewFlutterPlugin() {}
36+
37+
/**
38+
* Registers a plugin implementation that uses the stable {@code io.flutter.plugin.common}
39+
* package.
40+
*
41+
* <p>Calling this automatically initializes the plugin. However plugins initialized this way
42+
* won't react to changes in activity or context, unlike {@link CameraPlugin}.
43+
*/
1244
public static void registerWith(Registrar registrar) {
1345
registrar
1446
.platformViewRegistry()
1547
.registerViewFactory(
1648
"plugins.flutter.io/webview",
1749
new WebViewFactory(registrar.messenger(), registrar.view()));
18-
FlutterCookieManager.registerWith(registrar.messenger());
50+
new FlutterCookieManager(registrar.messenger());
51+
}
52+
53+
@Override
54+
public void onAttachedToEngine(FlutterPluginBinding binding) {
55+
BinaryMessenger messenger = binding.getFlutterEngine().getDartExecutor();
56+
binding
57+
.getFlutterEngine()
58+
.getPlatformViewsController()
59+
.getRegistry()
60+
.registerViewFactory(
61+
"plugins.flutter.io/webview", new WebViewFactory(messenger, /*containerView=*/ null));
62+
flutterCookieManager = new FlutterCookieManager(messenger);
63+
}
64+
65+
@Override
66+
public void onDetachedFromEngine(FlutterPluginBinding binding) {
67+
if (flutterCookieManager == null) {
68+
return;
69+
}
70+
71+
flutterCookieManager.dispose();
72+
flutterCookieManager = null;
1973
}
2074
}

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+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
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 MainActivityTest {
10+
@Rule public ActivityTestRule<MainActivity> rule = new ActivityTestRule<>(MainActivity.class);
11+
}

0 commit comments

Comments
 (0)