Explorar o código

Merge pull request #1 from fluttercommunity/master

pull
islxyqwe %!s(int64=6) %!d(string=hai) anos
pai
achega
92baafb936

+ 18 - 0
CHANGELOG.md

@@ -1,3 +1,21 @@
+# 0.3.3
+
+- BREAKING CHANGE - AndroidX support
+
+# 0.3.2
+
+- enable Javascript in iOS, support abort loading specific URLs
+- add resizeToAvoidBottomInset to WebviewScaffold; #301
+
+# 0.3.1
+
+- Add support for geolocation Android
+- fix No269: Can't load target="_blank" links on iOS
+- fix: reloadUrl will not return Future
+- Fix height of keyboard
+- Fix Hide/Show WebView
+- hotfix widget back to initialChild after webview is tapped on Android
+
 # 0.3.0
 
 - Fixes rect capture issue. Ensures WebView remains in the correct place on screen even when keyboard appears.

+ 15 - 3
android/build.gradle

@@ -1,6 +1,18 @@
 group 'com.flutter_webview_plugin'
 version '1.0-SNAPSHOT'
 
+def ANDROIDX_WARNING = "flutterPluginsAndroidXWarning";
+gradle.buildFinished { buildResult ->
+    if (buildResult.failure && !rootProject.ext.has(ANDROIDX_WARNING)) {
+        println '         *********************************************************'
+        println 'WARNING: This version of flutter_webview_plugin will break your Android build if it or its dependencies aren\'t compatible with AndroidX.'
+        println '         See https://goo.gl/CP92wY for more information on the problem and how to fix it.'
+        println '         This warning prints for all Android build failures. The real root cause of the error may be unrelated.'
+        println '         *********************************************************'
+        rootProject.ext.set(ANDROIDX_WARNING, true);
+    }
+}
+
 buildscript {
     repositories {
         google()
@@ -8,7 +20,7 @@ buildscript {
     }
 
     dependencies {
-        classpath 'com.android.tools.build:gradle:3.2.1'
+        classpath 'com.android.tools.build:gradle:3.3.2'
     }
 }
 
@@ -21,10 +33,10 @@ allprojects {
 apply plugin: 'com.android.library'
 
 android {
-    compileSdkVersion 27
+    compileSdkVersion 28
 
     defaultConfig {
-        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
+        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
         // NOTE(jeffmikels): When targetSdkVersion or minSdkVersion is not set or < 4, gradle adds 
         // additional scary permissions such as WRITE_EXTERNAL_STORAGE and READ_PHONE_STATE.
         minSdkVersion 16

+ 68 - 0
android/src/main/java/com/flutter_webview_plugin/BrowserClient.java

@@ -1,6 +1,8 @@
 package com.flutter_webview_plugin;
 
+import android.annotation.TargetApi;
 import android.graphics.Bitmap;
+import android.os.Build;
 import android.webkit.WebResourceRequest;
 import android.webkit.WebResourceResponse;
 import android.webkit.WebView;
@@ -8,14 +10,33 @@ import android.webkit.WebViewClient;
 
 import java.util.HashMap;
 import java.util.Map;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
 
 /**
  * Created by lejard_h on 20/12/2017.
  */
 
 public class BrowserClient extends WebViewClient {
+    private Pattern invalidUrlPattern = null;
+
     public BrowserClient() {
+        this(null);
+    }
+
+    public BrowserClient(String invalidUrlRegex) {
         super();
+        if (invalidUrlRegex != null) {
+            invalidUrlPattern = Pattern.compile(invalidUrlRegex);
+        }
+    }
+
+    public void updateInvalidUrlRegex(String invalidUrlRegex) {
+        if (invalidUrlRegex != null) {
+            invalidUrlPattern = Pattern.compile(invalidUrlRegex);
+        } else {
+            invalidUrlPattern = null;
+        }
     }
 
     @Override
@@ -40,6 +61,35 @@ public class BrowserClient extends WebViewClient {
 
     }
 
+    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
+    @Override
+    public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) {
+        // returning true causes the current WebView to abort loading the URL,
+        // while returning false causes the WebView to continue loading the URL as usual.
+        String url = request.getUrl().toString();
+        boolean isInvalid = checkInvalidUrl(url);
+        Map<String, Object> data = new HashMap<>();
+        data.put("url", url);
+        data.put("type", isInvalid ? "abortLoad" : "shouldStart");
+
+        FlutterWebviewPlugin.channel.invokeMethod("onState", data);
+        return isInvalid;
+    }
+
+    @Override
+    public boolean shouldOverrideUrlLoading(WebView view, String url) {
+        // returning true causes the current WebView to abort loading the URL,
+        // while returning false causes the WebView to continue loading the URL as usual.
+        boolean isInvalid = checkInvalidUrl(url);
+        Map<String, Object> data = new HashMap<>();
+        data.put("url", url);
+        data.put("type", isInvalid ? "abortLoad" : "shouldStart");
+
+        FlutterWebviewPlugin.channel.invokeMethod("onState", data);
+        return isInvalid;
+    }
+
+    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
     @Override
     public void onReceivedHttpError(WebView view, WebResourceRequest request, WebResourceResponse errorResponse) {
         super.onReceivedHttpError(view, request, errorResponse);
@@ -48,4 +98,22 @@ public class BrowserClient extends WebViewClient {
         data.put("code", Integer.toString(errorResponse.getStatusCode()));
         FlutterWebviewPlugin.channel.invokeMethod("onHttpError", data);
     }
+
+    @Override
+    public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) {
+        super.onReceivedError(view, errorCode, description, failingUrl);
+        Map<String, Object> data = new HashMap<>();
+        data.put("url", failingUrl);
+        data.put("code", errorCode);
+        FlutterWebviewPlugin.channel.invokeMethod("onHttpError", data);
+    }
+
+    private boolean checkInvalidUrl(String url) {
+        if (invalidUrlPattern == null) {
+            return false;
+        } else {
+            Matcher matcher = invalidUrlPattern.matcher(url);
+            return matcher.lookingAt();
+        }
+    }
 }

+ 4 - 0
android/src/main/java/com/flutter_webview_plugin/FlutterWebviewPlugin.java

@@ -99,6 +99,8 @@ public class FlutterWebviewPlugin implements MethodCallHandler, PluginRegistry.A
         Map<String, String> headers = call.argument("headers");
         boolean scrollBar = call.argument("scrollBar");
         boolean allowFileURLs = call.argument("allowFileURLs");
+        boolean useWideViewPort = call.argument("useWideViewPort");
+        String invalidUrlRegex = call.argument("invalidUrlRegex");
         boolean geolocationEnabled = call.argument("geolocationEnabled");
 
         if (webViewManager == null || webViewManager.closed == true) {
@@ -122,6 +124,8 @@ public class FlutterWebviewPlugin implements MethodCallHandler, PluginRegistry.A
                 supportMultipleWindows,
                 appCacheEnabled,
                 allowFileURLs,
+                useWideViewPort,
+                invalidUrlRegex,
                 geolocationEnabled
         );
         result.success(null);

+ 8 - 3
android/src/main/java/com/flutter_webview_plugin/WebviewManager.java

@@ -2,7 +2,6 @@ package com.flutter_webview_plugin;
 
 import android.content.Intent;
 import android.net.Uri;
-import android.util.Log;
 import android.annotation.TargetApi;
 import android.app.Activity;
 import android.content.Context;
@@ -16,7 +15,6 @@ import android.webkit.ValueCallback;
 import android.webkit.WebChromeClient;
 import android.webkit.WebSettings;
 import android.webkit.WebView;
-import android.webkit.WebViewClient;
 import android.widget.FrameLayout;
 import android.provider.MediaStore;
 import android.support.v4.content.FileProvider;
@@ -90,6 +88,7 @@ class WebviewManager {
     boolean closed = false;
     WebView webView;
     Activity activity;
+    BrowserClient webViewClient;
     ResultHandler resultHandler;
     Context context;
 
@@ -98,7 +97,7 @@ class WebviewManager {
         this.activity = activity;
         this.context = context;
         this.resultHandler = new ResultHandler();
-        WebViewClient webViewClient = new BrowserClient();
+        webViewClient = new BrowserClient();
         webView.setOnKeyListener(new View.OnKeyListener() {
             @Override
             public boolean onKey(View v, int keyCode, KeyEvent event) {
@@ -308,6 +307,8 @@ class WebviewManager {
             boolean supportMultipleWindows,
             boolean appCacheEnabled,
             boolean allowFileURLs,
+            boolean useWideViewPort,
+            String invalidUrlRegex,
             boolean geolocationEnabled
     ) {
         webView.getSettings().setJavaScriptEnabled(withJavascript);
@@ -323,6 +324,10 @@ class WebviewManager {
         webView.getSettings().setAllowFileAccessFromFileURLs(allowFileURLs);
         webView.getSettings().setAllowUniversalAccessFromFileURLs(allowFileURLs);
 
+        webView.getSettings().setUseWideViewPort(useWideViewPort);
+
+        webViewClient.updateInvalidUrlRegex(invalidUrlRegex);
+
         if (geolocationEnabled) {
             webView.getSettings().setGeolocationEnabled(true);
             webView.setWebChromeClient(new WebChromeClient() {

+ 5 - 4
example/android/app/build.gradle

@@ -15,7 +15,7 @@ apply plugin: 'com.android.application'
 apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
 
 android {
-    compileSdkVersion 27
+    compileSdkVersion 28
 
     lintOptions {
         disable 'InvalidPackage'
@@ -25,6 +25,7 @@ android {
         testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
         // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
         applicationId "com.yourcompany.flutter_webview_plugin_example"
+        minSdkVersion 16
     }
 
     buildTypes {
@@ -41,7 +42,7 @@ flutter {
 }
 
 dependencies {
-    androidTestImplementation 'com.android.support:support-annotations:27.0.0'
-    androidTestImplementation 'com.android.support.test:runner:1.0.2'
-    androidTestImplementation 'com.android.support.test:rules:1.0.2'
+    androidTestImplementation 'androidx.annotation:annotation:1.0.2'
+    androidTestImplementation 'androidx.test:runner:1.1.1'
+    androidTestImplementation 'androidx.test:rules:1.1.1'
 }

+ 2 - 2
example/android/build.gradle

@@ -8,7 +8,7 @@ buildscript {
     }
 
     dependencies {
-        classpath 'com.android.tools.build:gradle:3.2.1'
+        classpath 'com.android.tools.build:gradle:3.3.2'
     }
 }
 
@@ -33,5 +33,5 @@ task clean(type: Delete) {
 }
 
 task wrapper(type: Wrapper) {
-    gradleVersion = '2.14.1'
+    gradleVersion = '4.10.2'
 }

+ 2 - 0
example/android/gradle.properties

@@ -1 +1,3 @@
 org.gradle.jvmargs=-Xmx1536M
+android.useAndroidX=true
+android.enableJetifier=false

+ 1 - 0
example/lib/main.dart

@@ -201,6 +201,7 @@ class _MyHomePageState extends State<MyHomePage> {
                   selectedUrl,
                   rect: Rect.fromLTWH(0.0, 0.0, MediaQuery.of(context).size.width, 300.0),
                   userAgent: kAndroidUserAgent,
+                  invalidUrlRegex: r'^(https).+(twitter)', // prevent redirecting to twitter when user click on its icon in flutter website
                 );
               },
               child: const Text('Open Webview (rect)'),

+ 39 - 4
ios/Classes/FlutterWebviewPlugin.m

@@ -6,6 +6,7 @@ static NSString *const CHANNEL_NAME = @"flutter_webview_plugin";
 @interface FlutterWebviewPlugin() <WKNavigationDelegate, UIScrollViewDelegate, WKUIDelegate> {
     BOOL _enableAppScheme;
     BOOL _enableZoom;
+    NSString* _invalidUrlRegex;
 }
 @end
 
@@ -83,6 +84,8 @@ static NSString *const CHANNEL_NAME = @"flutter_webview_plugin";
     NSString *userAgent = call.arguments[@"userAgent"];
     NSNumber *withZoom = call.arguments[@"withZoom"];
     NSNumber *scrollBar = call.arguments[@"scrollBar"];
+    NSNumber *withJavascript = call.arguments[@"withJavascript"];
+    _invalidUrlRegex = call.arguments[@"invalidUrlRegex"];
 
     if (clearCache != (id)[NSNull null] && [clearCache boolValue]) {
         [[NSURLCache sharedURLCache] removeAllCachedResponses];
@@ -112,9 +115,18 @@ static NSString *const CHANNEL_NAME = @"flutter_webview_plugin";
     self.webview.scrollView.showsHorizontalScrollIndicator = [scrollBar boolValue];
     self.webview.scrollView.showsVerticalScrollIndicator = [scrollBar boolValue];
 
+    WKPreferences* preferences = [[self.webview configuration] preferences];
+    if ([withJavascript boolValue]) {
+        [preferences setJavaScriptEnabled:YES];
+    } else {
+        [preferences setJavaScriptEnabled:NO];
+    }
+
     _enableZoom = [withZoom boolValue];
 
-    [self.viewController.view addSubview:self.webview];
+    UIViewController* presentedViewController = self.viewController.presentedViewController;
+    UIViewController* currentViewController = presentedViewController != nil ? presentedViewController : self.viewController;
+    [currentViewController.view addSubview:self.webview];
 
     [self navigate:call];
 }
@@ -235,18 +247,37 @@ static NSString *const CHANNEL_NAME = @"flutter_webview_plugin";
         }];
 }
 
+- (bool)checkInvalidUrl:(NSURL*)url {
+  NSString* urlString = url != nil ? [url absoluteString] : nil;
+  if (_invalidUrlRegex != [NSNull null] && urlString != nil) {
+    NSError* error = NULL;
+    NSRegularExpression* regex =
+        [NSRegularExpression regularExpressionWithPattern:_invalidUrlRegex
+                                                  options:NSRegularExpressionCaseInsensitive
+                                                    error:&error];
+    NSTextCheckingResult* match = [regex firstMatchInString:urlString
+                                                    options:0
+                                                      range:NSMakeRange(0, [urlString length])];
+    return match != nil;
+  } else {
+    return false;
+  }
+}
+
 #pragma mark -- WkWebView Delegate
 - (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction
     decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler {
 
+    BOOL isInvalid = [self checkInvalidUrl: navigationAction.request.URL];
+
     id data = @{@"url": navigationAction.request.URL.absoluteString,
-                @"type": @"shouldStart",
+                @"type": isInvalid ? @"abortLoad" : @"shouldStart",
                 @"navigationType": [NSNumber numberWithInt:navigationAction.navigationType]};
     [channel invokeMethod:@"onState" arguments:data];
 
     if (navigationAction.navigationType == WKNavigationTypeBackForward) {
         [channel invokeMethod:@"onBackPressed" arguments:nil];
-    } else {
+    } else if (!isInvalid) {
         id data = @{@"url": navigationAction.request.URL.absoluteString};
         [channel invokeMethod:@"onUrlChanged" arguments:data];
     }
@@ -255,7 +286,11 @@ static NSString *const CHANNEL_NAME = @"flutter_webview_plugin";
         ([webView.URL.scheme isEqualToString:@"http"] ||
          [webView.URL.scheme isEqualToString:@"https"] ||
          [webView.URL.scheme isEqualToString:@"about"])) {
-        decisionHandler(WKNavigationActionPolicyAllow);
+         if (isInvalid) {
+            decisionHandler(WKNavigationActionPolicyCancel);
+         } else {
+            decisionHandler(WKNavigationActionPolicyAllow);
+         }
     } else {
         decisionHandler(WKNavigationActionPolicyCancel);
     }

+ 12 - 2
lib/src/base.dart

@@ -7,7 +7,7 @@ import 'package:flutter/services.dart';
 const _kChannel = 'flutter_webview_plugin';
 
 // TODO: more general state for iOS/android
-enum WebViewState { shouldStart, startLoad, finishLoad }
+enum WebViewState { shouldStart, startLoad, finishLoad, abortLoad }
 
 // TODO: use an id by webview to be able to manage multiple webview
 
@@ -79,7 +79,6 @@ class FlutterWebviewPlugin {
   /// Start the Webview with [url]
   /// - [headers] specify additional HTTP headers
   /// - [withJavascript] enable Javascript or not for the Webview
-  ///     iOS WebView: Not implemented yet
   /// - [clearCache] clear the cache of the Webview
   /// - [clearCookies] clear all cookies of the Webview
   /// - [hidden] not show
@@ -94,6 +93,10 @@ class FlutterWebviewPlugin {
   /// - [withLocalUrl]: allow url as a local path
   ///     Allow local files on iOs > 9.0
   /// - [scrollBar]: enable or disable scrollbar
+  /// - [supportMultipleWindows] enable multiple windows support in Android
+  /// - [invalidUrlRegex] is the regular expression of URLs that web view shouldn't load.
+  /// For example, when webview is redirected to a specific URL, you want to intercept
+  /// this process by stopping loading this URL and replacing webview by another screen.
   Future<Null> launch(String url, {
     Map<String, String> headers,
     bool withJavascript,
@@ -110,6 +113,8 @@ class FlutterWebviewPlugin {
     bool supportMultipleWindows,
     bool appCacheEnabled,
     bool allowFileURLs,
+    bool useWideViewPort,
+    String invalidUrlRegex,
     bool geolocationEnabled,
   }) async {
     final args = <String, dynamic>{
@@ -127,6 +132,8 @@ class FlutterWebviewPlugin {
       'supportMultipleWindows': supportMultipleWindows ?? false,
       'appCacheEnabled': appCacheEnabled ?? false,
       'allowFileURLs': allowFileURLs ?? false,
+      'useWideViewPort': useWideViewPort ?? false,
+      'invalidUrlRegex': invalidUrlRegex,
       'geolocationEnabled': geolocationEnabled ?? false,
     };
 
@@ -235,6 +242,9 @@ class WebViewStateChanged {
       case 'finishLoad':
         t = WebViewState.finishLoad;
         break;
+      case 'abortLoad':
+        t = WebViewState.abortLoad;
+        break;
     }
     return WebViewStateChanged(t, map['url'], map['navigationType']);
   }

+ 6 - 1
lib/src/webview_scaffold.dart

@@ -30,6 +30,8 @@ class WebviewScaffold extends StatefulWidget {
     this.hidden = false,
     this.initialChild,
     this.allowFileURLs,
+    this.resizeToAvoidBottomInset = false,
+    this.invalidUrlRegex,
     this.geolocationEnabled
   }) : super(key: key);
 
@@ -53,6 +55,8 @@ class WebviewScaffold extends StatefulWidget {
   final bool hidden;
   final Widget initialChild;
   final bool allowFileURLs;
+  final bool resizeToAvoidBottomInset;
+  final String invalidUrlRegex;
   final bool geolocationEnabled;
 
   @override
@@ -103,7 +107,7 @@ class _WebviewScaffoldState extends State<WebviewScaffold> {
   Widget build(BuildContext context) {
     return Scaffold(
       appBar: widget.appBar,
-      resizeToAvoidBottomInset: false,
+      resizeToAvoidBottomInset: widget.resizeToAvoidBottomInset,
       persistentFooterButtons: widget.persistentFooterButtons,
       bottomNavigationBar: widget.bottomNavigationBar,
       body: _WebviewPlaceholder(
@@ -127,6 +131,7 @@ class _WebviewScaffoldState extends State<WebviewScaffold> {
               supportMultipleWindows: widget.supportMultipleWindows,
               appCacheEnabled: widget.appCacheEnabled,
               allowFileURLs: widget.allowFileURLs,
+              invalidUrlRegex: widget.invalidUrlRegex,
               geolocationEnabled: widget.geolocationEnabled
             );
           } else {

+ 1 - 1
pubspec.yaml

@@ -7,7 +7,7 @@ authors:
 - Simon Lightfoot <simon@devangels.london>
 - Rafal Wachol <gorudonu@gmail.com>
 homepage: https://github.com/dart-flitter/flutter_webview_plugin
-version: 0.3.1
+version: 0.3.3
 maintainer: Simon Lightfoot (@slightfoot)
 
 environment: