diff --git a/README.md b/README.md index 88dec08a..91dec464 100644 --- a/README.md +++ b/README.md @@ -5,6 +5,10 @@ Plugin that allow Flutter to communicate with a native WebView. +***Warning:*** +The webview is not integrated in the widget tree, it is a native view on top of the flutter view. +you won't be able to use snackbars, dialogs ... + ## Getting Started For help getting started with Flutter, view our online [documentation](http://flutter.io/). diff --git a/android/src/main/java/com/flutter_webview_plugin/BrowserChromeClient.java b/android/src/main/java/com/flutter_webview_plugin/BrowserChromeClient.java new file mode 100644 index 00000000..5bb5b70d --- /dev/null +++ b/android/src/main/java/com/flutter_webview_plugin/BrowserChromeClient.java @@ -0,0 +1,61 @@ +package com.flutter_webview_plugin; +import com.flutter_webview_plugin.OpenFileChooser; + +import android.app.Activity; +import android.content.Intent; +import android.graphics.Bitmap; +import android.net.Uri; +import android.webkit.ValueCallback; +import android.webkit.WebChromeClient; +import android.webkit.WebView; +import android.webkit.WebViewClient; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * Created by lejard_h on 20/12/2017. + */ + +public class BrowserChromeClient extends WebChromeClient { + + public final static int FILE_CHOOSER_RESULT_CODE = 10000; + OpenFileChooser activity; + + public BrowserChromeClient(Activity activity) { + super(); + this.activity = ((OpenFileChooser)activity); + } + // For Android < 3.0 + public void openFileChooser(ValueCallback valueCallback) { + activity.setUploadMessage(valueCallback); + openImageChooserActivity(); + } + + // For Android >= 3.0 + public void openFileChooser(ValueCallback valueCallback, String acceptType) { + activity.setUploadMessage(valueCallback); + openImageChooserActivity(); + } + + //For Android >= 4.1 + public void openFileChooser(ValueCallback valueCallback, String acceptType, String capture) { + activity.setUploadMessage(valueCallback); + openImageChooserActivity(); + } + + // For Android >= 5.0 + @Override + public boolean onShowFileChooser(WebView webView, ValueCallback filePathCallback, WebChromeClient.FileChooserParams fileChooserParams) { + activity.setUploadMessageAboveL(filePathCallback); + openImageChooserActivity(); + return true; + } + private void openImageChooserActivity() { + Intent i = new Intent(Intent.ACTION_GET_CONTENT); + i.addCategory(Intent.CATEGORY_OPENABLE); + i.setType("image/*"); + ((Activity)activity).startActivityForResult(Intent.createChooser(i, "Image Chooser"), FILE_CHOOSER_RESULT_CODE); + } +} \ No newline at end of file diff --git a/android/src/main/java/com/flutter_webview_plugin/BrowserClient.java b/android/src/main/java/com/flutter_webview_plugin/BrowserClient.java index b17ced82..d8a00e04 100644 --- a/android/src/main/java/com/flutter_webview_plugin/BrowserClient.java +++ b/android/src/main/java/com/flutter_webview_plugin/BrowserClient.java @@ -1,10 +1,12 @@ package com.flutter_webview_plugin; import android.graphics.Bitmap; +import android.webkit.WebResourceRequest; import android.webkit.WebView; import android.webkit.WebViewClient; import java.util.HashMap; +import java.util.List; import java.util.Map; /** @@ -12,8 +14,12 @@ */ public class BrowserClient extends WebViewClient { - public BrowserClient() { + + private final List mInterceptUrls; + + public BrowserClient(List interceptUrls) { super(); + mInterceptUrls = interceptUrls; } @Override @@ -26,13 +32,24 @@ public void onPageStarted(WebView view, String url, Bitmap favicon) { } @Override - public void onPageFinished(WebView view, String url) { - super.onPageFinished(view, url); + public boolean shouldOverrideUrlLoading(WebView view, String url) { Map data = new HashMap<>(); data.put("url", url); FlutterWebviewPlugin.channel.invokeMethod("onUrlChanged", data); + for (String interceptUrl : mInterceptUrls) { + if(url.contains(interceptUrl)){ + return true; + } + } + return false; + } + @Override + public void onPageFinished(WebView view, String url) { + super.onPageFinished(view, url); + Map data = new HashMap<>(); + data.put("url", url); data.put("type", "finishLoad"); FlutterWebviewPlugin.channel.invokeMethod("onState", data); diff --git a/android/src/main/java/com/flutter_webview_plugin/FlutterWebviewPlugin.java b/android/src/main/java/com/flutter_webview_plugin/FlutterWebviewPlugin.java index 1cbac2c7..2cc434a8 100644 --- a/android/src/main/java/com/flutter_webview_plugin/FlutterWebviewPlugin.java +++ b/android/src/main/java/com/flutter_webview_plugin/FlutterWebviewPlugin.java @@ -7,6 +7,7 @@ import android.view.Display; import android.widget.FrameLayout; +import java.util.List; import java.util.Map; import io.flutter.plugin.common.MethodCall; @@ -63,11 +64,16 @@ private void openUrl(MethodCall call, MethodChannel.Result result) { boolean clearCookies = call.argument("clearCookies"); boolean withZoom = call.argument("withZoom"); boolean withLocalStorage = call.argument("withLocalStorage"); + Map additionalHttpHeaders = call.argument("additionalHttpHeaders"); + List interceptUrls = call.argument("interceptUrls"); if (webViewManager == null || webViewManager.closed == true) { - webViewManager = new WebviewManager(activity); + webViewManager = new WebviewManager(activity,interceptUrls); + } + if(webViewManager.webView.getParent() != null){ + webViewManager.webView.loadUrl(url); + return; } - FrameLayout.LayoutParams params = buildLayoutParams(call); activity.addContentView(webViewManager.webView, params); @@ -79,7 +85,8 @@ private void openUrl(MethodCall call, MethodChannel.Result result) { userAgent, url, withZoom, - withLocalStorage + withLocalStorage, + additionalHttpHeaders ); result.success(null); } diff --git a/android/src/main/java/com/flutter_webview_plugin/OpenFileChooser.java b/android/src/main/java/com/flutter_webview_plugin/OpenFileChooser.java new file mode 100644 index 00000000..018cb5e2 --- /dev/null +++ b/android/src/main/java/com/flutter_webview_plugin/OpenFileChooser.java @@ -0,0 +1,11 @@ +package com.flutter_webview_plugin; + +import android.webkit.ValueCallback; + +/** + * Created by lejard_h on 20/12/2017. + */ +public interface OpenFileChooser { + void setUploadMessage(ValueCallback valueCallback); + void setUploadMessageAboveL(ValueCallback valueCallback); +} \ No newline at end of file diff --git a/android/src/main/java/com/flutter_webview_plugin/WebviewManager.java b/android/src/main/java/com/flutter_webview_plugin/WebviewManager.java index 3c645124..13aa04d6 100644 --- a/android/src/main/java/com/flutter_webview_plugin/WebviewManager.java +++ b/android/src/main/java/com/flutter_webview_plugin/WebviewManager.java @@ -7,15 +7,16 @@ import android.view.KeyEvent; import android.view.View; import android.view.ViewGroup; -import android.webkit.CookieManager; -import android.webkit.ValueCallback; -import android.webkit.WebView; -import android.webkit.WebViewClient; +import android.webkit.*; import android.widget.FrameLayout; - +import com.flutter_webview_plugin.BrowserChromeClient; +import com.flutter_webview_plugin.BrowserClient; import io.flutter.plugin.common.MethodCall; import io.flutter.plugin.common.MethodChannel; +import java.util.List; +import java.util.Map; + /** * Created by lejard_h on 20/12/2017. */ @@ -25,9 +26,9 @@ class WebviewManager { boolean closed = false; WebView webView; - WebviewManager(Activity activity) { + WebviewManager(Activity activity, List interceptUrls) { this.webView = new WebView(activity); - WebViewClient webViewClient = new BrowserClient(); + WebViewClient webViewClient = new BrowserClient(interceptUrls); webView.setOnKeyListener(new View.OnKeyListener() { @Override public boolean onKey(View v, int keyCode, KeyEvent event) { @@ -48,6 +49,8 @@ public boolean onKey(View v, int keyCode, KeyEvent event) { }); webView.setWebViewClient(webViewClient); + WebChromeClient webChromeClient= new BrowserChromeClient(activity); + webView.setWebChromeClient(webChromeClient); } private void clearCookies() { @@ -68,12 +71,13 @@ private void clearCache() { webView.clearFormData(); } - void openUrl(boolean withJavascript, boolean clearCache, boolean hidden, boolean clearCookies, String userAgent, String url, boolean withZoom, boolean withLocalStorage) { + void openUrl(boolean withJavascript, boolean clearCache, boolean hidden, boolean clearCookies, String userAgent, String url, boolean withZoom, boolean withLocalStorage, Map additionalHttpHeaders) { webView.getSettings().setJavaScriptEnabled(withJavascript); webView.getSettings().setBuiltInZoomControls(withZoom); webView.getSettings().setSupportZoom(withZoom); webView.getSettings().setDomStorageEnabled(withLocalStorage); - + webView.getSettings().setUseWideViewPort(true); + webView.getSettings().setLoadWithOverviewMode(true); if (clearCache) { clearCache(); } @@ -90,7 +94,7 @@ void openUrl(boolean withJavascript, boolean clearCache, boolean hidden, boolean webView.getSettings().setUserAgentString(userAgent); } - webView.loadUrl(url); + webView.loadUrl(url, additionalHttpHeaders); } void close(MethodCall call, MethodChannel.Result result) { diff --git a/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 00000000..18d98100 --- /dev/null +++ b/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/ios/Classes/FlutterWebviewPlugin.m b/ios/Classes/FlutterWebviewPlugin.m index 0f4649c6..bd7526f6 100644 --- a/ios/Classes/FlutterWebviewPlugin.m +++ b/ios/Classes/FlutterWebviewPlugin.m @@ -6,6 +6,7 @@ @interface FlutterWebviewPlugin() { BOOL _enableAppScheme; BOOL _enableZoom; + NSMutableDictionary *_additionalHttpHeaders; } @end @@ -25,6 +26,7 @@ - (instancetype)initWithViewController:(UIViewController *)viewController { self = [super init]; if (self) { self.viewController = viewController; + _additionalHttpHeaders = [[NSMutableDictionary alloc] init]; } return self; } @@ -57,6 +59,7 @@ - (void)initWebview:(FlutterMethodCall*)call { NSNumber *hidden = call.arguments[@"hidden"]; NSDictionary *rect = call.arguments[@"rect"]; _enableAppScheme = call.arguments[@"enableAppScheme"]; + _additionalHttpHeaders = call.arguments[@"additionalHttpHeaders"]; NSString *userAgent = call.arguments[@"userAgent"]; NSNumber *withZoom = call.arguments[@"withZoom"]; @@ -102,7 +105,10 @@ - (CGRect)parseRect:(NSDictionary *)rect { - (void)navigate:(FlutterMethodCall*)call { if (self.webview != nil) { NSString *url = call.arguments[@"url"]; - NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:url]]; + NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:url]]; + for (NSString *key in _additionalHttpHeaders) { + [request setValue:_additionalHttpHeaders[key] forHTTPHeaderField:key]; + } [self.webview loadRequest:request]; } } @@ -155,7 +161,6 @@ - (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigati id data = @{@"url": navigationAction.request.URL.absoluteString}; [channel invokeMethod:@"onUrlChanged" arguments:data]; } - if (_enableAppScheme || ([webView.URL.scheme isEqualToString:@"http"] || [webView.URL.scheme isEqualToString:@"https"] || diff --git a/lib/src/base.dart b/lib/src/base.dart index 7d93e953..584d39c2 100644 --- a/lib/src/base.dart +++ b/lib/src/base.dart @@ -80,7 +80,10 @@ class FlutterWebviewPlugin { Rect rect, String userAgent, bool withZoom, - bool withLocalStorage}) async { + bool withLocalStorage, + Map additionalHttpHeaders, + List interceptUrls, + }) async { Map args = { "url": url, "withJavascript": withJavascript ?? true, @@ -90,7 +93,9 @@ class FlutterWebviewPlugin { "enableAppScheme": enableAppScheme ?? true, "userAgent": userAgent, "withZoom": withZoom ?? false, - "withLocalStorage": withLocalStorage ?? true + "withLocalStorage": withLocalStorage ?? true, + "additionalHttpHeaders": additionalHttpHeaders ?? {}, + "interceptUrls": interceptUrls ?? [] }; if (rect != null) { args["rect"] = { diff --git a/lib/src/webview_scaffold.dart b/lib/src/webview_scaffold.dart index 2817fff6..349bde4e 100644 --- a/lib/src/webview_scaffold.dart +++ b/lib/src/webview_scaffold.dart @@ -15,10 +15,11 @@ class WebviewScaffold extends StatefulWidget { final String userAgent; final bool primary; final List persistentFooterButtons; + final List interceptUrls; final Widget bottomNavigationBar; final bool withZoom; final bool withLocalStorage; - + final Map additionalHttpHeaders; WebviewScaffold( {Key key, this.appBar, @@ -32,7 +33,10 @@ class WebviewScaffold extends StatefulWidget { this.persistentFooterButtons, this.bottomNavigationBar, this.withZoom, - this.withLocalStorage}) + this.withLocalStorage, + this.additionalHttpHeaders, + this.interceptUrls, + }) : super(key: key); @override @@ -67,7 +71,10 @@ class _WebviewScaffoldState extends State { userAgent: widget.userAgent, rect: _rect, withZoom: widget.withZoom, - withLocalStorage: widget.withLocalStorage); + withLocalStorage: widget.withLocalStorage, + additionalHttpHeaders: widget.additionalHttpHeaders, + interceptUrls: widget.interceptUrls, + ); } else { Rect rect = _buildRect(context); if (_rect != rect) {