Skip to content
This repository was archived by the owner on Feb 22, 2023. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions packages/webview_flutter/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,11 @@
## 0.3.15+2

* Re-land support for the v2 Android embedding. This correctly sets the minimum
SDK to the latest stable and avoid any compile errors. *WARNING:* the V2
embedding itself still requires the current Flutter master channel
(flutter/flutter@1d4d63a) for text input to work properly on all Android
versions.

## 0.3.15+1

* Revert the prior embedding support add since it requires an API that hasn't
Expand Down
25 changes: 25 additions & 0 deletions packages/webview_flutter/android/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -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"
}
}
}
}
2 changes: 2 additions & 0 deletions packages/webview_flutter/android/gradle.properties
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
org.gradle.jvmargs=-Xmx1536M
android.enableJetifier=true
android.useAndroidX=true
Original file line number Diff line number Diff line change
Expand Up @@ -15,17 +15,11 @@
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.
}

static void registerWith(BinaryMessenger messenger) {
MethodChannel methodChannel = new MethodChannel(messenger, "plugins.flutter.io/cookie_manager");
FlutterCookieManager cookieManager = new FlutterCookieManager();
methodChannel.setMethodCallHandler(cookieManager);
FlutterCookieManager(BinaryMessenger messenger) {
methodChannel = new MethodChannel(messenger, "plugins.flutter.io/cookie_manager");
methodChannel.setMethodCallHandler(this);
}

@Override
Expand All @@ -39,6 +33,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();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -36,7 +38,7 @@ public class FlutterWebView implements PlatformView, MethodCallHandler {
BinaryMessenger messenger,
int id,
Map<String, Object> params,
final View containerView) {
@Nullable View containerView) {

DisplayListenerProxy displayListenerProxy = new DisplayListenerProxy();
DisplayManager displayManager =
Expand Down Expand Up @@ -95,6 +97,26 @@ public void onInputConnectionLocked() {
webView.lockInputConnection();
}

// @Override
// This is overriding a method that hasn't rolled into stable Flutter yet. Including the
// annotation would cause compile time failures in versions of Flutter too old to include the new
// method. However leaving it raw like this means that the method will be ignored in old versions
// of Flutter but used as an override anyway wherever it's actually defined.
// TODO(mklim): Add the @Override annotation once stable passes v1.10.9.
public void onFlutterViewAttached(@NonNull View flutterView) {
webView.setContainerView(flutterView);
}

// @Override
// This is overriding a method that hasn't rolled into stable Flutter yet. Including the
// annotation would cause compile time failures in versions of Flutter too old to include the new
// method. However leaving it raw like this means that the method will be ignored in old versions
// of Flutter but used as an override anyway wherever it's actually defined.
// TODO(mklim): Add the @Override annotation once stable passes v1.10.9.
public void onFlutterViewDetached() {
webView.setContainerView(null);
}

@Override
public void onMethodCall(MethodCall methodCall, Result result) {
switch (methodCall.method) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -22,16 +24,29 @@
* <p>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.w(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.
*
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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);
}

Expand All @@ -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() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,73 @@

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.
*
* <p>Register this in an add to app scenario to gracefully handle activity and context changes.
*
* <p>Call {@link #registerWith(Registrar)} to use the stable {@code io.flutter.plugin.common}
* package instead.
*/
public class WebViewFlutterPlugin implements FlutterPlugin {

private @Nullable FlutterCookieManager flutterCookieManager;

/**
* Add an instance of this to {@link io.flutter.embedding.engine.plugins.PluginRegistry} to
* register it.
*
* <p>THIS PLUGIN CODE PATH DEPENDS ON A NEWER VERSION OF FLUTTER THAN THE ONE DEFINED IN THE
* PUBSPEC.YAML. Text input will fail on some Android devices unless this is used with at least
* flutter/flutter@1d4d63ace1f801a022ea9ec737bf8c15395588b9. Use the V1 embedding with {@link
* #registerWith(Registrar)} to use this plugin with older Flutter versions.
*
* <p>Registration should eventually be handled automatically by v2 of the
* GeneratedPluginRegistrant. https://github.com/flutter/flutter/issues/42694
*/
public WebViewFlutterPlugin() {}

/**
* Registers a plugin implementation that uses the stable {@code io.flutter.plugin.common}
* package.
*
* <p>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()
.registerViewFactory(
"plugins.flutter.io/webview",
new WebViewFactory(registrar.messenger(), registrar.view()));
FlutterCookieManager.registerWith(registrar.messenger());
new FlutterCookieManager(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));

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: extra =?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's a subjective style decision. I tend to manually write in parameter hints in the style of /*parameterName=*/ to make function calls easier to reason about even when the code is being read as plain text.

// Extreme hypothetical confusing example.
return new Fraction(/*numerator=*/2, /*denominator=*/3);

Most IDEs will automatically do this for you, but for reading Github or something like Vim I think it's helpful.

flutterCookieManager = new FlutterCookieManager(messenger);
}

@Override
public void onDetachedFromEngine(@NonNull FlutterPluginBinding binding) {
if (flutterCookieManager == null) {
return;
}

flutterCookieManager.dispose();
flutterCookieManager = null;
}
}
5 changes: 3 additions & 2 deletions packages/webview_flutter/example/android/app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ flutter {

dependencies {
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'
}
Original file line number Diff line number Diff line change
@@ -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<EmbeddingV1Activity> rule =
new ActivityTestRule<>(EmbeddingV1Activity.class);
}
Original file line number Diff line number Diff line change
@@ -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<MainActivity> rule = new ActivityTestRule<>(MainActivity.class);
}
Loading