[webview_flutter] Create WebView client depending on version and hasDelegate (#1360)


diff --git a/packages/webview_flutter/CHANGELOG.md b/packages/webview_flutter/CHANGELOG.md
index 813ab33..81801b8 100644
--- a/packages/webview_flutter/CHANGELOG.md
+++ b/packages/webview_flutter/CHANGELOG.md
@@ -1,3 +1,7 @@
+## 0.3.5+2
+
+* Fix crash from `NavigationDelegate` on later versions of Android.
+
 ## 0.3.5+1
 
 * Fix a bug where updates to onPageFinished were ignored.
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 070ba74..9db7d5e 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
@@ -10,6 +10,7 @@
 import android.view.View;
 import android.webkit.WebStorage;
 import android.webkit.WebView;
+import android.webkit.WebViewClient;
 import io.flutter.plugin.common.BinaryMessenger;
 import io.flutter.plugin.common.MethodCall;
 import io.flutter.plugin.common.MethodChannel;
@@ -41,8 +42,6 @@
       registerJavaScriptChannelNames((List<String>) params.get(JS_CHANNEL_NAMES_FIELD));
     }
 
-    webView.setWebViewClient(flutterWebViewClient);
-
     if (params.containsKey("initialUrl")) {
       String url = (String) params.get("initialUrl");
       webView.loadUrl(url);
@@ -186,7 +185,12 @@
           updateJsMode((Integer) settings.get(key));
           break;
         case "hasNavigationDelegate":
-          flutterWebViewClient.setHasNavigationDelegate((boolean) settings.get(key));
+          final boolean hasNavigationDelegate = (boolean) settings.get(key);
+
+          final WebViewClient webViewClient =
+              flutterWebViewClient.createWebViewClient(hasNavigationDelegate);
+
+          webView.setWebViewClient(webViewClient);
           break;
         default:
           throw new IllegalArgumentException("Unknown WebView setting: " + key);
diff --git a/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/FlutterWebViewClient.java b/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/FlutterWebViewClient.java
index fef2258..e7b10ce 100644
--- a/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/FlutterWebViewClient.java
+++ b/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/FlutterWebViewClient.java
@@ -9,6 +9,8 @@
 import android.util.Log;
 import android.webkit.WebResourceRequest;
 import android.webkit.WebView;
+import android.webkit.WebViewClient;
+import androidx.annotation.NonNull;
 import androidx.webkit.WebViewClientCompat;
 import io.flutter.plugin.common.MethodChannel;
 import java.util.HashMap;
@@ -18,7 +20,7 @@
 // shouldOverrideUrlLoading(WebView view, WebResourceRequest request)
 // invoked by the webview on older Android devices, without it pages that use iframes will
 // be broken when a navigationDelegate is set on Android version earlier than N.
-class FlutterWebViewClient extends WebViewClientCompat {
+class FlutterWebViewClient {
   private static final String TAG = "FlutterWebViewClient";
   private final MethodChannel methodChannel;
   private boolean hasNavigationDelegate;
@@ -27,13 +29,8 @@
     this.methodChannel = methodChannel;
   }
 
-  void setHasNavigationDelegate(boolean hasNavigationDelegate) {
-    this.hasNavigationDelegate = hasNavigationDelegate;
-  }
-
   @TargetApi(Build.VERSION_CODES.LOLLIPOP)
-  @Override
-  public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) {
+  private boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) {
     if (!hasNavigationDelegate) {
       return false;
     }
@@ -53,8 +50,7 @@
     return request.isForMainFrame();
   }
 
-  @Override
-  public boolean shouldOverrideUrlLoading(WebView view, String url) {
+  private boolean shouldOverrideUrlLoading(WebView view, String url) {
     if (!hasNavigationDelegate) {
       return false;
     }
@@ -70,8 +66,7 @@
     return true;
   }
 
-  @Override
-  public void onPageFinished(WebView view, String url) {
+  private void onPageFinished(WebView view, String url) {
     Map<String, Object> args = new HashMap<>();
     args.put("url", url);
     methodChannel.invokeMethod("onPageFinished", args);
@@ -90,6 +85,54 @@
     }
   }
 
+  // This method attempts to avoid using WebViewClientCompat due to bug
+  // https://bugs.chromium.org/p/chromium/issues/detail?id=925887. Also, see
+  // https://github.com/flutter/flutter/issues/29446.
+  WebViewClient createWebViewClient(boolean hasNavigationDelegate) {
+    this.hasNavigationDelegate = hasNavigationDelegate;
+
+    if (!hasNavigationDelegate || android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
+      return internalCreateWebViewClient();
+    }
+
+    return internalCreateWebViewClientCompat();
+  }
+
+  private WebViewClient internalCreateWebViewClient() {
+    return new WebViewClient() {
+      @TargetApi(Build.VERSION_CODES.N)
+      @Override
+      public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) {
+        return FlutterWebViewClient.this.shouldOverrideUrlLoading(view, request);
+      }
+
+      @Override
+      public void onPageFinished(WebView view, String url) {
+        FlutterWebViewClient.this.onPageFinished(view, url);
+      }
+    };
+  }
+
+  private WebViewClientCompat internalCreateWebViewClientCompat() {
+    return new WebViewClientCompat() {
+      @Override
+      public boolean shouldOverrideUrlLoading(
+          @NonNull WebView view, @NonNull WebResourceRequest request) {
+        return FlutterWebViewClient.this.shouldOverrideUrlLoading(view, request);
+      }
+
+      @Override
+      public boolean shouldOverrideUrlLoading(WebView view, String url) {
+        return FlutterWebViewClient.this.shouldOverrideUrlLoading(view, url);
+      }
+
+      @Override
+      public void onPageFinished(WebView view, String url) {
+        FlutterWebViewClient.this.onPageFinished(view, url);
+      }
+    };
+  }
+
   private static class OnNavigationRequestResult implements MethodChannel.Result {
     private final String url;
     private final Map<String, String> headers;
diff --git a/packages/webview_flutter/pubspec.yaml b/packages/webview_flutter/pubspec.yaml
index e21da55..bf707a8 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.5+1
+version: 0.3.5+2
 author: Flutter Team <flutter-dev@googlegroups.com>
 homepage: https://github.com/flutter/plugins/tree/master/packages/webview_flutter