From 99ff84c6170d3be32050882dc4a27cfb077062f5 Mon Sep 17 00:00:00 2001 From: pichillilorenzo Date: Thu, 18 Oct 2018 01:38:36 +0200 Subject: [PATCH] v0.4.1 --- .idea/workspace.xml | 374 ++++++++---------- CHANGELOG.md | 7 + README.md | 39 +- .../InAppBrowserFlutterPlugin.java | 44 +++ .../flutter_inappbrowser/WebViewActivity.java | 132 ++++++- .../ChromeCustomTabsActivity.java | 1 + .../src/main/res/layout/activity_web_view.xml | 1 + example/pubspec.yaml | 2 +- flutter_inappbrowser.iml | 1 + .../InAppBrowserWebViewController.swift | 228 +++++++++-- ios/Classes/Options.swift | 19 +- ios/Classes/SwiftFlutterPlugin.swift | 42 +- ios/Storyboards/WebView.storyboard | 2 + lib/flutter_inappbrowser.dart | 33 +- pubspec.yaml | 2 +- 15 files changed, 667 insertions(+), 260 deletions(-) diff --git a/.idea/workspace.xml b/.idea/workspace.xml index cd5f0a39..e5de8703 100644 --- a/.idea/workspace.xml +++ b/.idea/workspace.xml @@ -15,7 +15,21 @@ + + + + + + + + + + + + + + @@ -32,63 +46,11 @@ - - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + @@ -104,11 +66,6 @@ - getPre - client - webView - result.s - result Client LOAD_START_EVENT loadstop @@ -134,6 +91,11 @@ _blank .html close( + isOpened + javascript + header + localhost + /main.dart activity.getPreferences(0) @@ -171,21 +133,22 @@ - @@ -194,18 +157,6 @@ - - - - - - - - - + + + @@ -402,33 +348,34 @@ - + - - - - - - - - - - - - - + + + + + + + + + + + + + + @@ -437,7 +384,6 @@ - @@ -482,45 +428,10 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -606,13 +517,6 @@ - - - - - - - @@ -622,16 +526,6 @@ - - - - - - - - - - @@ -669,13 +563,6 @@ - - - - - - - @@ -709,71 +596,134 @@ - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + - - - - - - - - - - - - - - - - - - + - - + + - - - - - - - - - - - - - - + + + + diff --git a/CHANGELOG.md b/CHANGELOG.md index 52acf4d3..4a29b0c8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,9 @@ +## 0.4.1 + +- added `InAppBrowser.takeScreenshot()` +- added `InAppBrowser.setOptions()` +- added `InAppBrowser.getOptions()` + ## 0.4.0 - removed `target` parameter to `InAppBrowser.open()` method. To open the url on the system browser, use the `openWithSystemBrowser: true` option @@ -7,6 +13,7 @@ - added `InAppBrowser.openWithSystemBrowser` method - added `InAppBrowser.openOnLocalhost` method - added `InAppBrowser.loadFile` method +- added `InAppBrowser.isOpened` method ## 0.3.2 diff --git a/README.md b/README.md index bc39e50a..37a218cb 100644 --- a/README.md +++ b/README.md @@ -9,6 +9,14 @@ A Flutter plugin that allows you to open an in-app browser window. This plugin is inspired by the popular [cordova-plugin-inappbrowser](https://github.com/apache/cordova-plugin-inappbrowser)! +### IMPORTANT Note for iOS +To be able to use this plugin on iOS, you need to create the Flutter App with `flutter create -i swift` (see [flutter/flutter#13422 (comment)](https://github.com/flutter/flutter/issues/13422#issuecomment-392133780)), otherwise, you will get this message: +``` +=== BUILD TARGET flutter_inappbrowser OF PROJECT Pods WITH CONFIGURATION Debug === +The “Swift Language Version” (SWIFT_VERSION) build setting must be set to a supported value for targets which use Swift. Supported values are: 3.0, 4.0, 4.2. This setting can be set in the build settings editor. +``` +that is not true! The Swift version that I have used is already a supported value: 4.0. + ## Getting Started For help getting started with Flutter, view our online @@ -334,7 +342,7 @@ inAppBrowser.loadUrl(String url, {Map headers = const {}}); Loads the given `assetFilePath` with optional `headers` specified as a map from name to value. -To be able to load your local files (assets, js, css, etc.), you need to add them in the `assets` section of the `pubspec.yaml` file, otherwise they cannot be found! +To be able to load your local files (html, js, css, etc.), you need to add them in the `assets` section of the `pubspec.yaml` file, otherwise they cannot be found! Example of a `pubspec.yaml` file: ```yaml @@ -493,6 +501,35 @@ Returns `true` if the callback is removed, otherwise `false`. inAppBrowser.removeJavaScriptHandler(String handlerName, int index); ``` +#### Future\ InAppBrowser.takeScreenshot + +Takes a screenshot (in PNG format) of the view's visible viewport and returns a `Uint8List`. Returns `null` if it wasn't be able to take it. + +**NOTE for iOS**: available from iOS 11.0+. +```dart +inAppBrowser.takeScreenshot(); +``` + +#### Future\ InAppBrowser.setOptions + +Sets the InAppBrowser options with the new `options` and evaluates them. +```dart +inAppBrowser.setOptions(Map options); +``` + +#### Future\\> InAppBrowser.getOptions + +Gets the current InAppBrowser options. Returns `null` if the options are not setted yet. +```dart +inAppBrowser.getOptions(); +``` + +#### bool InAppBrowser.isOpened + +Returns `true` if the `InAppBrowser` instance is opened, otherwise `false`. +```dart +inAppBrowser.isOpened(); +``` ### `ChromeSafariBrowser` class [Chrome Custom Tabs](https://developer.android.com/reference/android/support/customtabs/package-summary) on Android / [SFSafariViewController](https://developer.apple.com/documentation/safariservices/sfsafariviewcontroller) on iOS. diff --git a/android/src/main/java/com/pichillilorenzo/flutter_inappbrowser/InAppBrowserFlutterPlugin.java b/android/src/main/java/com/pichillilorenzo/flutter_inappbrowser/InAppBrowserFlutterPlugin.java index d29842e7..648245e3 100644 --- a/android/src/main/java/com/pichillilorenzo/flutter_inappbrowser/InAppBrowserFlutterPlugin.java +++ b/android/src/main/java/com/pichillilorenzo/flutter_inappbrowser/InAppBrowserFlutterPlugin.java @@ -31,6 +31,7 @@ import android.provider.Browser; import android.net.Uri; import android.os.Build; import android.os.Bundle; +import android.util.Base64; import android.util.JsonReader; import android.util.JsonToken; import android.webkit.MimeTypeMap; @@ -247,6 +248,28 @@ public class InAppBrowserFlutterPlugin implements MethodCallHandler { case "isHidden": result.success(isHidden(uuid)); break; + case "takeScreenshot": + result.success(takeScreenshot(uuid)); + break; + case "setOptions": + { + String optionsType = (String) call.argument("optionsType"); + switch (optionsType){ + case "InAppBrowserOptions": + InAppBrowserOptions inAppBrowserOptions = new InAppBrowserOptions(); + HashMap inAppBroeserOptionsMap = (HashMap) call.argument("options"); + inAppBrowserOptions.parse(inAppBroeserOptionsMap); + setOptions(uuid, inAppBrowserOptions, inAppBroeserOptionsMap); + break; + default: + result.error(LOG_TAG, "Options " + optionsType + " not available.", null); + } + } + result.success(true); + break; + case "getOptions": + result.success(getOptions(uuid)); + break; default: result.notImplemented(); } @@ -580,4 +603,25 @@ public class InAppBrowserFlutterPlugin implements MethodCallHandler { result.success(true); } } + + public byte[] takeScreenshot(String uuid) { + WebViewActivity webViewActivity = webViewActivities.get(uuid); + if (webViewActivity != null) + return webViewActivity.takeScreenshot(); + return null; + } + + public void setOptions(String uuid, InAppBrowserOptions options, HashMap optionsMap) { + WebViewActivity webViewActivity = webViewActivities.get(uuid); + if (webViewActivity != null) + webViewActivity.setOptions(options, optionsMap); + } + + public HashMap getOptions(String uuid) { + WebViewActivity webViewActivity = webViewActivities.get(uuid); + if (webViewActivity != null) + return webViewActivity.getOptions(); + return null; + } + } diff --git a/android/src/main/java/com/pichillilorenzo/flutter_inappbrowser/WebViewActivity.java b/android/src/main/java/com/pichillilorenzo/flutter_inappbrowser/WebViewActivity.java index 80cb5817..c596b550 100644 --- a/android/src/main/java/com/pichillilorenzo/flutter_inappbrowser/WebViewActivity.java +++ b/android/src/main/java/com/pichillilorenzo/flutter_inappbrowser/WebViewActivity.java @@ -1,7 +1,10 @@ package com.pichillilorenzo.flutter_inappbrowser; import android.content.Intent; +import android.graphics.Bitmap; +import android.graphics.Canvas; import android.graphics.Color; +import android.graphics.Picture; import android.graphics.drawable.ColorDrawable; import android.os.Build; import android.support.v7.app.ActionBar; @@ -12,6 +15,7 @@ import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; import android.view.View; +import android.view.ViewGroup; import android.webkit.CookieManager; import android.webkit.ValueCallback; import android.webkit.WebSettings; @@ -19,6 +23,10 @@ import android.webkit.WebView; import android.widget.ProgressBar; import android.widget.SearchView; +import java.io.ByteArrayOutputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStream; import java.util.HashMap; import java.util.Map; @@ -31,6 +39,7 @@ public class WebViewActivity extends AppCompatActivity { String uuid; WebView webView; ActionBar actionBar; + Menu menu; InAppBrowserWebViewClient inAppBrowserWebViewClient; InAppBrowserWebChromeClient inAppBrowserWebChromeClient; SearchView searchView; @@ -182,8 +191,12 @@ public class WebViewActivity extends AppCompatActivity { else settings.setTextZoom(100); - if (options.progressBar) - progressBar = findViewById(R.id.progressBar); + progressBar = findViewById(R.id.progressBar); + + if (!options.progressBar) + progressBar.setMax(0); + else + progressBar.setMax(100); actionBar.setDisplayShowTitleEnabled(!options.hideTitleBar); @@ -199,7 +212,9 @@ public class WebViewActivity extends AppCompatActivity { } @Override - public boolean onCreateOptionsMenu(Menu menu) { + public boolean onCreateOptionsMenu(Menu m) { + menu = m; + MenuInflater inflater = getMenuInflater(); // Inflate menu to add items to action bar if it is present. inflater.inflate(R.menu.menu_main, menu); @@ -366,9 +381,11 @@ public class WebViewActivity extends AppCompatActivity { } private void clearCache() { - webView.clearCache(true); - clearCookies(); - webView.clearFormData(); + if (webView != null) { + webView.clearCache(true); + clearCookies(); + webView.clearFormData(); + } } public void goBackButtonClicked(MenuItem item) { @@ -394,4 +411,107 @@ public class WebViewActivity extends AppCompatActivity { InAppBrowserFlutterPlugin.close(uuid, null); } + public byte[] takeScreenshot() { + if (webView != null) { + Picture picture = webView.capturePicture(); + Bitmap b = Bitmap.createBitmap( webView.getWidth(), + webView.getHeight(), Bitmap.Config.ARGB_8888); + Canvas c = new Canvas(b); + + picture.draw(c); + ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); + b.compress(Bitmap.CompressFormat.PNG, 100, byteArrayOutputStream); + try { + byteArrayOutputStream.close(); + } catch (IOException e) { + e.printStackTrace(); + } + return byteArrayOutputStream.toByteArray(); + } + return null; + } + + public void setOptions(InAppBrowserOptions newOptions, HashMap newOptionsMap) { + + WebSettings settings = webView.getSettings(); + + if (newOptionsMap.get("hidden") != null && options.hidden != newOptions.hidden) { + if (newOptions.hidden) + hide(); + else + show(); + } + + if (newOptionsMap.get("javaScriptEnabled") != null && options.javaScriptEnabled != newOptions.javaScriptEnabled) + settings.setJavaScriptEnabled(newOptions.javaScriptEnabled); + + if (newOptionsMap.get("javaScriptCanOpenWindowsAutomatically") != null && options.javaScriptCanOpenWindowsAutomatically != newOptions.javaScriptCanOpenWindowsAutomatically) + settings.setJavaScriptCanOpenWindowsAutomatically(newOptions.javaScriptCanOpenWindowsAutomatically); + + if (newOptionsMap.get("builtInZoomControls") != null && options.builtInZoomControls != newOptions.builtInZoomControls) + settings.setBuiltInZoomControls(newOptions.builtInZoomControls); + + if (newOptionsMap.get("safeBrowsingEnabled") != null && options.safeBrowsingEnabled != newOptions.safeBrowsingEnabled && Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) + settings.setSafeBrowsingEnabled(newOptions.safeBrowsingEnabled); + + if (newOptionsMap.get("mediaPlaybackRequiresUserGesture") != null && options.mediaPlaybackRequiresUserGesture != newOptions.mediaPlaybackRequiresUserGesture) + settings.setMediaPlaybackRequiresUserGesture(newOptions.mediaPlaybackRequiresUserGesture); + + if (newOptionsMap.get("databaseEnabled") != null && options.databaseEnabled != newOptions.databaseEnabled) + settings.setDatabaseEnabled(newOptions.databaseEnabled); + + if (newOptionsMap.get("domStorageEnabled") != null && options.domStorageEnabled != newOptions.domStorageEnabled) + settings.setDomStorageEnabled(newOptions.domStorageEnabled); + + if (newOptionsMap.get("userAgent") != null && options.userAgent != newOptions.userAgent && !newOptions.userAgent.isEmpty()) + settings.setUserAgentString(newOptions.userAgent); + + if (newOptionsMap.get("clearCache") != null && newOptions.clearCache) + clearCache(); + else if (newOptionsMap.get("clearSessionCache") != null && newOptions.clearSessionCache) + CookieManager.getInstance().removeSessionCookie(); + + if (newOptionsMap.get("useWideViewPort") != null && options.useWideViewPort != newOptions.useWideViewPort) + settings.setUseWideViewPort(newOptions.useWideViewPort); + + if (newOptionsMap.get("supportZoom") != null && options.supportZoom != newOptions.supportZoom) + settings.setSupportZoom(newOptions.supportZoom); + + if (newOptionsMap.get("progressBar") != null && options.progressBar != newOptions.progressBar && progressBar != null) { + if (newOptions.progressBar) + progressBar.setMax(0); + else + progressBar.setMax(100); + } + + if (newOptionsMap.get("hideTitleBar") != null && options.hideTitleBar != newOptions.hideTitleBar) + actionBar.setDisplayShowTitleEnabled(!newOptions.hideTitleBar); + + if (newOptionsMap.get("toolbarTop") != null && options.toolbarTop != newOptions.toolbarTop) { + if (!newOptions.toolbarTop) + actionBar.hide(); + else + actionBar.show(); + } + + if (newOptionsMap.get("toolbarTopBackgroundColor") != null && options.toolbarTopBackgroundColor != newOptions.toolbarTopBackgroundColor && !newOptions.toolbarTopBackgroundColor.isEmpty()) + actionBar.setBackgroundDrawable(new ColorDrawable(Color.parseColor(newOptions.toolbarTopBackgroundColor))); + + if (newOptionsMap.get("toolbarTopFixedTitle") != null && options.toolbarTopFixedTitle != newOptions.toolbarTopFixedTitle && !newOptions.toolbarTopFixedTitle.isEmpty()) + actionBar.setTitle(newOptions.toolbarTopFixedTitle); + + if (newOptionsMap.get("hideUrlBar") != null && options.hideUrlBar != newOptions.hideUrlBar) { + if (newOptions.hideUrlBar) + menu.findItem(R.id.menu_search).setVisible(false); + else + menu.findItem(R.id.menu_search).setVisible(true); + } + + options = newOptions; + } + + public HashMap getOptions() { + return (options != null) ? options.getHashMap() : null; + } + } diff --git a/android/src/main/java/com/pichillilorenzo/flutter_inappbrowser/chrome_custom_tabs/ChromeCustomTabsActivity.java b/android/src/main/java/com/pichillilorenzo/flutter_inappbrowser/chrome_custom_tabs/ChromeCustomTabsActivity.java index d516b925..36d64f4e 100644 --- a/android/src/main/java/com/pichillilorenzo/flutter_inappbrowser/chrome_custom_tabs/ChromeCustomTabsActivity.java +++ b/android/src/main/java/com/pichillilorenzo/flutter_inappbrowser/chrome_custom_tabs/ChromeCustomTabsActivity.java @@ -91,4 +91,5 @@ public class ChromeCustomTabsActivity extends Activity { InAppBrowserFlutterPlugin.channel.invokeMethod("onChromeSafariBrowserClosed", obj); } } + } diff --git a/android/src/main/res/layout/activity_web_view.xml b/android/src/main/res/layout/activity_web_view.xml index 0d328547..8b19f543 100644 --- a/android/src/main/res/layout/activity_web_view.xml +++ b/android/src/main/res/layout/activity_web_view.xml @@ -2,6 +2,7 @@ + diff --git a/ios/Classes/InAppBrowserWebViewController.swift b/ios/Classes/InAppBrowserWebViewController.swift index 0ea3e33e..7222697b 100644 --- a/ios/Classes/InAppBrowserWebViewController.swift +++ b/ios/Classes/InAppBrowserWebViewController.swift @@ -150,6 +150,11 @@ class InAppBrowserWebViewController: UIViewController, WKUIDelegate, WKNavigatio @IBOutlet var toolbarBottom: UIToolbar! @IBOutlet var urlField: UITextField! + @IBOutlet var toolbarTop_BottomToWebViewTopConstraint: NSLayoutConstraint! + @IBOutlet var toolbarBottom_TopToWebViewBottomConstraint: NSLayoutConstraint! + @IBOutlet var webView_BottomFullScreenConstraint: NSLayoutConstraint! + @IBOutlet var webView_TopFullScreenConstraint: NSLayoutConstraint! + weak var navigationDelegate: SwiftFlutterPlugin? var currentURL: URL? var tmpWindow: UIWindow? @@ -159,20 +164,24 @@ class InAppBrowserWebViewController: UIViewController, WKUIDelegate, WKNavigatio var uuid: String = "" var WKNavigationMap: [String: [String: Any]] = [:] var startPageTime = 0 + var viewPrepared = false required init(coder aDecoder: NSCoder) { super.init(coder: aDecoder)! } override func viewWillAppear(_ animated: Bool) { - prepareWebView() + if !viewPrepared { + prepareConstraints() + prepareWebView() + } + viewPrepared = true super.viewWillAppear(animated) } + override func viewDidLoad() { super.viewDidLoad() - - //MyURLProtocol.wkWebViewDelegateMap[uuid] = self webView.uiDelegate = self webView.navigationDelegate = self @@ -214,6 +223,11 @@ class InAppBrowserWebViewController: UIViewController, WKUIDelegate, WKNavigatio super.viewWillDisappear(animated) } + func prepareConstraints () { + webView_BottomFullScreenConstraint = NSLayoutConstraint(item: self.webView, attribute: NSLayoutAttribute.bottom, relatedBy: NSLayoutRelation.equal, toItem: self.view, attribute: NSLayoutAttribute.bottom, multiplier: 1, constant: 0) + webView_TopFullScreenConstraint = NSLayoutConstraint(item: self.webView, attribute: NSLayoutAttribute.top, relatedBy: NSLayoutRelation.equal, toItem: self.view, attribute: NSLayoutAttribute.top, multiplier: 1, constant: 0) + } + func prepareWebView() { //UIApplication.shared.statusBarStyle = preferredStatusBarStyle @@ -231,13 +245,9 @@ class InAppBrowserWebViewController: UIViewController, WKUIDelegate, WKNavigatio } } else { - self.toolbarTop.removeFromSuperview() - self.webView.bounds.size.height += self.toolbarTop.bounds.height - - if #available(iOS 9.0, *) { - let topConstraint = webView.topAnchor.constraint(equalTo: self.view.topAnchor, constant: CGFloat(getStatusBarOffset())) - NSLayoutConstraint.activate([topConstraint]) - } + self.toolbarTop.isHidden = true + self.toolbarTop_BottomToWebViewTopConstraint.isActive = false + self.webView_TopFullScreenConstraint.isActive = true } if (browserOptions?.toolbarBottom)! { @@ -247,13 +257,9 @@ class InAppBrowserWebViewController: UIViewController, WKUIDelegate, WKNavigatio self.toolbarBottom.isTranslucent = (browserOptions?.toolbarBottomTranslucent)! } else { - self.toolbarBottom.removeFromSuperview() - self.webView.bounds.size.height += self.toolbarBottom.bounds.height - - if #available(iOS 9.0, *) { - let bottomConstraint = webView.bottomAnchor.constraint(equalTo: self.view.bottomAnchor) - NSLayoutConstraint.activate([bottomConstraint]) - } + self.toolbarBottom.isHidden = true + self.toolbarBottom_TopToWebViewBottomConstraint.isActive = false + self.webView_BottomFullScreenConstraint.isActive = true } if browserOptions?.closeButtonCaption != "" { @@ -303,19 +309,12 @@ class InAppBrowserWebViewController: UIViewController, WKUIDelegate, WKNavigatio self.webView.configuration.userContentController.addUserScript(javaScriptBridgeJSScript) self.webView.configuration.userContentController.add(self, name: "callHandler") - if (browserOptions?.useOnLoadResource)! { - let resourceObserverJSScript = WKUserScript(source: resourceObserverJS, injectionTime: .atDocumentStart, forMainFrameOnly: false) - self.webView.configuration.userContentController.addUserScript(resourceObserverJSScript) - self.webView.configuration.userContentController.add(self, name: "resourceLoaded") - } + let resourceObserverJSScript = WKUserScript(source: resourceObserverJS, injectionTime: .atDocumentStart, forMainFrameOnly: false) + self.webView.configuration.userContentController.addUserScript(resourceObserverJSScript) + self.webView.configuration.userContentController.add(self, name: "resourceLoaded") if #available(iOS 10.0, *) { - if (browserOptions?.mediaPlaybackRequiresUserGesture)! { - self.webView.configuration.mediaTypesRequiringUserActionForPlayback = .all - } - else { - self.webView.configuration.mediaTypesRequiringUserActionForPlayback = [] - } + self.webView.configuration.mediaTypesRequiringUserActionForPlayback = ((browserOptions?.mediaPlaybackRequiresUserGesture)!) ? .all : [] } else { // Fallback on earlier versions self.webView.configuration.mediaPlaybackRequiresUserAction = (browserOptions?.mediaPlaybackRequiresUserGesture)! @@ -330,21 +329,20 @@ class InAppBrowserWebViewController: UIViewController, WKUIDelegate, WKNavigatio self.webView.allowsBackForwardNavigationGestures = (browserOptions?.allowsBackForwardNavigationGestures)! if #available(iOS 9.0, *) { self.webView.allowsLinkPreview = (browserOptions?.allowsLinkPreview)! - } else { - // Fallback on earlier versions } + if #available(iOS 10.0, *) { self.webView.configuration.ignoresViewportScaleLimits = (browserOptions?.ignoresViewportScaleLimits)! - } else { - // Fallback on earlier versions } + self.webView.configuration.allowsInlineMediaPlayback = (browserOptions?.allowsInlineMediaPlayback)! + if #available(iOS 9.0, *) { self.webView.configuration.allowsPictureInPictureMediaPlayback = (browserOptions?.allowsPictureInPictureMediaPlayback)! - } else { - // Fallback on earlier versions } + self.webView.configuration.preferences.javaScriptCanOpenWindowsAutomatically = (browserOptions?.javaScriptCanOpenWindowsAutomatically)! + self.webView.configuration.preferences.javaScriptEnabled = (browserOptions?.javaScriptEnabled)! if ((browserOptions?.userAgent)! != "") { @@ -680,7 +678,7 @@ class InAppBrowserWebViewController: UIViewController, WKUIDelegate, WKNavigatio navigationDelegate?.onConsoleMessage(uuid: self.uuid, sourceURL: "", lineNumber: 1, message: message.body as! String, messageLevel: messageLevel) } } - else if message.name == "resourceLoaded" { + else if message.name == "resourceLoaded" && (browserOptions?.useOnLoadResource)! { if let resource = convertToDictionary(text: message.body as! String) { let url = URL(string: resource["name"] as! String)! if !UIApplication.shared.canOpenURL(url) { @@ -715,4 +713,164 @@ class InAppBrowserWebViewController: UIViewController, WKUIDelegate, WKNavigatio } } } + + func takeScreenshot (completionHandler: @escaping (_ screenshot: Data?) -> Void) { + if #available(iOS 11.0, *) { + self.webView.takeSnapshot(with: nil, completionHandler: {(image, error) -> Void in + var imageData: Data? = nil + if let screenshot = image { + imageData = UIImagePNGRepresentation(screenshot)! + } + completionHandler(imageData) + }) + } else { + completionHandler(nil) + } + } + + func setOptions(newOptions: InAppBrowserOptions, newOptionsMap: [String: Any]) { + + if newOptionsMap["hidden"] != nil && browserOptions?.hidden != newOptions.hidden { + if newOptions.hidden { + self.navigationDelegate?.hide(uuid: self.uuid) + } + else { + self.navigationDelegate?.show(uuid: self.uuid) + } + } + + if newOptionsMap["hideUrlBar"] != nil && browserOptions?.hideUrlBar != newOptions.hideUrlBar { + self.urlField.isHidden = newOptions.hideUrlBar + self.urlField.isEnabled = !newOptions.hideUrlBar + } + + if newOptionsMap["toolbarTop"] != nil && browserOptions?.toolbarTop != newOptions.toolbarTop { + self.webView_TopFullScreenConstraint.isActive = !newOptions.toolbarTop + self.toolbarTop.isHidden = !newOptions.toolbarTop + self.toolbarTop_BottomToWebViewTopConstraint.isActive = newOptions.toolbarTop + } + + if newOptionsMap["toolbarTopBackgroundColor"] != nil && browserOptions?.toolbarTopBackgroundColor != newOptions.toolbarTopBackgroundColor && newOptions.toolbarTopBackgroundColor != "" { + self.toolbarTop.backgroundColor = color(fromHexString: newOptions.toolbarTopBackgroundColor) + } + + if newOptionsMap["toolbarBottom"] != nil && browserOptions?.toolbarBottom != newOptions.toolbarBottom { + self.webView_BottomFullScreenConstraint.isActive = !newOptions.toolbarBottom + self.toolbarBottom.isHidden = !newOptions.toolbarBottom + self.toolbarBottom_TopToWebViewBottomConstraint.isActive = newOptions.toolbarBottom + } + + if newOptionsMap["toolbarBottomBackgroundColor"] != nil && browserOptions?.toolbarBottomBackgroundColor != newOptions.toolbarBottomBackgroundColor && newOptions.toolbarBottomBackgroundColor != "" { + self.toolbarBottom.backgroundColor = color(fromHexString: newOptions.toolbarBottomBackgroundColor) + } + + if newOptionsMap["toolbarBottomTranslucent"] != nil && browserOptions?.toolbarBottomTranslucent != newOptions.toolbarBottomTranslucent { + self.toolbarBottom.isTranslucent = newOptions.toolbarBottomTranslucent + } + + if newOptionsMap["closeButtonCaption"] != nil && browserOptions?.closeButtonCaption != newOptions.closeButtonCaption && newOptions.closeButtonCaption != "" { + closeButton.setTitle(newOptions.closeButtonCaption, for: .normal) + } + + if newOptionsMap["closeButtonColor"] != nil && browserOptions?.closeButtonColor != newOptions.closeButtonColor && newOptions.closeButtonColor != "" { + closeButton.tintColor = color(fromHexString: newOptions.closeButtonColor) + } + + if newOptionsMap["presentationStyle"] != nil && browserOptions?.presentationStyle != newOptions.presentationStyle { + self.modalPresentationStyle = UIModalPresentationStyle(rawValue: newOptions.presentationStyle)! + } + + if newOptionsMap["transitionStyle"] != nil && browserOptions?.transitionStyle != newOptions.transitionStyle { + self.modalTransitionStyle = UIModalTransitionStyle(rawValue: newOptions.transitionStyle)! + } + + if newOptionsMap["disallowOverScroll"] != nil && browserOptions?.disallowOverScroll != newOptions.disallowOverScroll { + if self.webView.responds(to: #selector(getter: self.webView.scrollView)) { + self.webView.scrollView.bounces = !newOptions.disallowOverScroll + } + else { + for subview: UIView in self.webView.subviews { + if subview is UIScrollView { + (subview as! UIScrollView).bounces = !newOptions.disallowOverScroll + } + } + } + } + + if newOptionsMap["enableViewportScale"] != nil && browserOptions?.enableViewportScale != newOptions.enableViewportScale && newOptions.enableViewportScale { + let jscript = "var meta = document.createElement('meta'); meta.setAttribute('name', 'viewport'); meta.setAttribute('content', 'width=device-width'); document.getElementsByTagName('head')[0].appendChild(meta);" + self.webView.evaluateJavaScript(jscript, completionHandler: nil) + } + + if newOptionsMap["mediaPlaybackRequiresUserGesture"] != nil && browserOptions?.mediaPlaybackRequiresUserGesture != newOptions.mediaPlaybackRequiresUserGesture { + if #available(iOS 10.0, *) { + self.webView.configuration.mediaTypesRequiringUserActionForPlayback = (newOptions.mediaPlaybackRequiresUserGesture) ? .all : [] + } else { + // Fallback on earlier versions + self.webView.configuration.mediaPlaybackRequiresUserAction = newOptions.mediaPlaybackRequiresUserGesture + } + } + + if newOptionsMap["allowsInlineMediaPlayback"] != nil && browserOptions?.allowsInlineMediaPlayback != newOptions.allowsInlineMediaPlayback { + self.webView.configuration.allowsInlineMediaPlayback = newOptions.allowsInlineMediaPlayback + } + +// if newOptionsMap["keyboardDisplayRequiresUserAction"] != nil && browserOptions?.keyboardDisplayRequiresUserAction != newOptions.keyboardDisplayRequiresUserAction { +// self.webView.keyboardDisplayRequiresUserAction = newOptions.keyboardDisplayRequiresUserAction +// } + + if newOptionsMap["suppressesIncrementalRendering"] != nil && browserOptions?.suppressesIncrementalRendering != newOptions.suppressesIncrementalRendering { + self.webView.configuration.suppressesIncrementalRendering = newOptions.suppressesIncrementalRendering + } + + if newOptionsMap["allowsBackForwardNavigationGestures"] != nil && browserOptions?.allowsBackForwardNavigationGestures != newOptions.allowsBackForwardNavigationGestures { + self.webView.allowsBackForwardNavigationGestures = newOptions.allowsBackForwardNavigationGestures + } + + if newOptionsMap["allowsLinkPreview"] != nil && browserOptions?.allowsLinkPreview != newOptions.allowsLinkPreview { + if #available(iOS 9.0, *) { + self.webView.allowsLinkPreview = newOptions.allowsLinkPreview + } + } + + if newOptionsMap["ignoresViewportScaleLimits"] != nil && browserOptions?.ignoresViewportScaleLimits != newOptions.ignoresViewportScaleLimits { + if #available(iOS 10.0, *) { + self.webView.configuration.ignoresViewportScaleLimits = newOptions.ignoresViewportScaleLimits + } + } + + if newOptionsMap["allowsInlineMediaPlayback"] != nil && browserOptions?.allowsInlineMediaPlayback != newOptions.allowsInlineMediaPlayback { + self.webView.configuration.allowsInlineMediaPlayback = newOptions.allowsInlineMediaPlayback + } + + if newOptionsMap["allowsPictureInPictureMediaPlayback"] != nil && browserOptions?.allowsPictureInPictureMediaPlayback != newOptions.allowsPictureInPictureMediaPlayback { + if #available(iOS 9.0, *) { + self.webView.configuration.allowsPictureInPictureMediaPlayback = newOptions.allowsPictureInPictureMediaPlayback + } + } + + if newOptionsMap["javaScriptCanOpenWindowsAutomatically"] != nil && browserOptions?.javaScriptCanOpenWindowsAutomatically != newOptions.javaScriptCanOpenWindowsAutomatically { + self.webView.configuration.preferences.javaScriptCanOpenWindowsAutomatically = newOptions.javaScriptCanOpenWindowsAutomatically + } + + if newOptionsMap["javaScriptEnabled"] != nil && browserOptions?.javaScriptEnabled != newOptions.javaScriptEnabled { + self.webView.configuration.preferences.javaScriptEnabled = newOptions.javaScriptEnabled + } + + if newOptionsMap["userAgent"] != nil && browserOptions?.userAgent != newOptions.userAgent && (newOptions.userAgent != "") { + if #available(iOS 9.0, *) { + self.webView.customUserAgent = newOptions.userAgent + } + } + + if newOptionsMap["clearCache"] != nil && newOptions.clearCache { + clearCache() + } + + self.browserOptions = newOptions + } + + func getOptions() -> [String: Any]? { + return (self.browserOptions != nil) ? self.browserOptions?.getHashMap() : nil + } } diff --git a/ios/Classes/Options.swift b/ios/Classes/Options.swift index 50332320..ddde934e 100644 --- a/ios/Classes/Options.swift +++ b/ios/Classes/Options.swift @@ -14,7 +14,7 @@ public class Options: NSObject { super.init() } - public func parse(options: [String: Any]) -> Options { + func parse(options: [String: Any]) -> Options { for (key, value) in options { if self.responds(to: Selector(key)) { self.setValue(value, forKey: key) @@ -22,5 +22,20 @@ public class Options: NSObject { } return self } + + func getHashMap() -> [String: Any] { + var options: [String: Any] = [:] + var counts = UInt32(); + let properties = class_copyPropertyList(object_getClass(self), &counts); + for i in 0.. Void in + result(screenshot) + }) + } + else { + result(nil) + } + break + case "setOptions": + let optionsType = arguments!["optionsType"] as! String; + switch (optionsType){ + case "InAppBrowserOptions": + let inAppBrowserOptions = InAppBrowserOptions(); + let inAppBroeserOptionsMap = arguments!["options"] as! [String: Any]; + inAppBrowserOptions.parse(options: inAppBroeserOptionsMap); + self.setOptions(uuid: uuid, options: inAppBrowserOptions, optionsMap: inAppBroeserOptionsMap); + break; + default: + result(FlutterError(code: "InAppBrowserFlutterPlugin", message: "Options " + optionsType + " not available.", details: nil)) + } + result(true) + break + case "getOptions": + result(self.getOptions(uuid: uuid)) + break default: result(FlutterMethodNotImplemented) break @@ -594,4 +621,17 @@ public class SwiftFlutterPlugin: NSObject, FlutterPlugin { } } + func setOptions(uuid: String, options: InAppBrowserOptions, optionsMap: [String: Any]) { + if let webViewController = self.webViewControllers[uuid] { + webViewController!.setOptions(newOptions: options, newOptionsMap: optionsMap) + } + } + + func getOptions(uuid: String) -> [String: Any]? { + if let webViewController = self.webViewControllers[uuid] { + return webViewController!.getOptions() + } + return nil + } + } diff --git a/ios/Storyboards/WebView.storyboard b/ios/Storyboards/WebView.storyboard index a11702ef..bfc191a0 100644 --- a/ios/Storyboards/WebView.storyboard +++ b/ios/Storyboards/WebView.storyboard @@ -92,7 +92,9 @@ + + diff --git a/lib/flutter_inappbrowser.dart b/lib/flutter_inappbrowser.dart index 44759e0c..2c51eb98 100644 --- a/lib/flutter_inappbrowser.dart +++ b/lib/flutter_inappbrowser.dart @@ -579,7 +579,38 @@ class InAppBrowser { } - ///Returns `true` if the browser is opened, otherwise `false`. + ///Takes a screenshot (in PNG format) of the WebView's visible viewport and returns a `Uint8List`. Returns `null` if it wasn't be able to take it. + /// + ///**NOTE for iOS**: available from iOS 11.0+. + Future takeScreenshot() async { + this._throwIsNotOpened(); + Map args = {}; + args.putIfAbsent('uuid', () => uuid); + return await _ChannelManager.channel.invokeMethod('takeScreenshot', args); + } + + ///Sets the [InAppBrowser] options with the new [options] and evaluates them. + Future setOptions(Map options) async { + this._throwIsNotOpened(); + Map args = {}; + args.putIfAbsent('uuid', () => uuid); + args.putIfAbsent('options', () => options); + args.putIfAbsent('optionsType', () => "InAppBrowserOptions"); + await _ChannelManager.channel.invokeMethod('setOptions', args); + } + + ///Gets the current [InAppBrowser] options. Returns `null` if the options are not setted yet. + Future> getOptions() async { + this._throwIsNotOpened(); + Map args = {}; + args.putIfAbsent('uuid', () => uuid); + args.putIfAbsent('optionsType', () => "InAppBrowserOptions"); + Map options = await _ChannelManager.channel.invokeMethod('getOptions', args); + options = options.cast(); + return options; + } + + ///Returns `true` if the [InAppBrowser] instance is opened, otherwise `false`. bool isOpened() { return this._isOpened; } diff --git a/pubspec.yaml b/pubspec.yaml index 2d24d6b3..70fae052 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: flutter_inappbrowser description: A Flutter plugin that allows you to open an in-app browser window. (inspired by the popular cordova-plugin-inappbrowser). -version: 0.4.0 +version: 0.4.1 author: Lorenzo Pichilli homepage: https://github.com/pichillilorenzo/flutter_inappbrowser