Commit 6a7963e4 authored by Lorenzo Pichilli's avatar Lorenzo Pichilli

fixed android zoom, added new ios webview options, added

>  onLongPressHitTestResult event, updated test cases, fixed Promise polyfill, fixed android options
parent 7d9f9f56
File mode changed from 100644 to 100755
File mode changed from 100644 to 100755
File mode changed from 100644 to 100755
File mode changed from 100644 to 100755
File mode changed from 100644 to 100755
File mode changed from 100644 to 100755
File mode changed from 100644 to 100755
File mode changed from 100644 to 100755
File mode changed from 100644 to 100755
File mode changed from 100644 to 100755
File mode changed from 100644 to 100755
File mode changed from 100644 to 100755
File mode changed from 100644 to 100755
File mode changed from 100644 to 100755
File mode changed from 100644 to 100755
File mode changed from 100644 to 100755
File mode changed from 100644 to 100755
File mode changed from 100644 to 100755
File mode changed from 100644 to 100755
File mode changed from 100644 to 100755
File mode changed from 100644 to 100755
File mode changed from 100644 to 100755
File mode changed from 100644 to 100755
File mode changed from 100644 to 100755
File mode changed from 100644 to 100755
File mode changed from 100644 to 100755
## 3.0.0 ## 3.0.0
- Added `Promise` javascript [polyfill](https://github.com/taylorhakes/promise-polyfill/blob/master/src/index.js) for webviews that doesn't support it for `window.flutter_inappwebview.callHandler` - Added `Promise` javascript [polyfill](https://github.com/tildeio/rsvp.js) for webviews that doesn't support it for `window.flutter_inappwebview.callHandler`
- Added `getDefaultUserAgent` static method to `InAppWebViewController` - Added `getDefaultUserAgent` static method to `InAppWebViewController`
- Added `onUpdateVisitedHistory`, `onPrint` event - Added `onUpdateVisitedHistory`, `onPrint`, `onLongPressHitTestResult` event
- Added `onGeolocationPermissionsHidePrompt` event for Android - Added `androidOnGeolocationPermissionsHidePrompt` event for Android webview
- Added `iosOnWebContentProcessDidTerminate`, `iosOnDidCommit`, `iosOnDidReceiveServerRedirectForProvisionalNavigation` events for iOS webview
- Added `supportMultipleWindows` webview option for Android - Added `supportMultipleWindows` webview option for Android
- Added `regexToCancelSubFramesLoading` webview option for Android to cancel subframe requests on `shouldOverrideUrlLoading` event based on a Regular Expression - Added `regexToCancelSubFramesLoading` webview option for Android to cancel subframe requests on `shouldOverrideUrlLoading` event based on a Regular Expression
- Added `getContentHeight`, `zoomBy`, `printCurrentPage`, `getScale` methods - Added `getContentHeight`, `zoomBy`, `printCurrentPage`, `getScale` methods
- Added `getOriginalUrl` webview method for Android - Added `getOriginalUrl` webview method for Android
- Added `reloadFromOrigin` webview method for iOS - Added `reloadFromOrigin`, `hasOnlySecureContent` webview methods for iOS
- Added `automaticallyAdjustsScrollIndicatorInsets` webview options for iOS - Added `automaticallyAdjustsScrollIndicatorInsets`, `accessibilityIgnoresInvertColors`, `decelerationRate`, `alwaysBounceVertical`, `alwaysBounceHorizontal`, `scrollsToTop`, `isPagingEnabled`, `maximumZoomScale`, `minimumZoomScale` webview options for iOS
- Added `WebStorageManager` class which manages the web storage used by WebView instances - Added `WebStorageManager` class which manages the web storage used by WebView instances
- Added `packageName` [#229](https://github.com/pichillilorenzo/flutter_inappwebview/issues/229) and `keepAliveEnabled` ChromeCustomTab options for Android - Added `packageName` [#229](https://github.com/pichillilorenzo/flutter_inappwebview/issues/229) and `keepAliveEnabled` ChromeCustomTab options for Android
- Updated for Flutter 1.12 new Java Embedding API (Android) - Updated for Flutter 1.12 new Java Embedding API (Android)
...@@ -17,10 +18,16 @@ ...@@ -17,10 +18,16 @@
- Updated default value for `domStorageEnabled` and `databaseEnabled` options to `true` for Android - Updated default value for `domStorageEnabled` and `databaseEnabled` options to `true` for Android
- Merge "Fixes null error when calling getOptions for InAppBrowser class" [#214](https://github.com/pichillilorenzo/flutter_inappwebview/pull/214) (thanks to [panndoraBoo](https://github.com/panndoraBoo)) - Merge "Fixes null error when calling getOptions for InAppBrowser class" [#214](https://github.com/pichillilorenzo/flutter_inappwebview/pull/214) (thanks to [panndoraBoo](https://github.com/panndoraBoo))
- Merge "Fixes crash onConsoleMessage iOS forced unwrapping" [#228](https://github.com/pichillilorenzo/flutter_inappwebview/pull/228) (thanks to [tokonu](https://github.com/tokonu)) - Merge "Fixes crash onConsoleMessage iOS forced unwrapping" [#228](https://github.com/pichillilorenzo/flutter_inappwebview/pull/228) (thanks to [tokonu](https://github.com/tokonu))
- Merge "Fix HTTPCookie.secure" [#311](https://github.com/pichillilorenzo/flutter_inappwebview/pull/311) (thanks to [xtyxtyx](https://github.com/xtyxtyx))
- Merge "Fix config options for Android release builds" [#295](https://github.com/pichillilorenzo/flutter_inappwebview/pull/295) (thanks to [wwwdata](https://github.com/wwwdata))
- Merge "fix scrollbar on iOS always show if not disable scroll" [#256](https://github.com/pichillilorenzo/flutter_inappwebview/pull/256) (thanks to [phamnhuvu-dev](https://github.com/phamnhuvu-dev))
- Merge "Fix crash on nil/invalid URL (iOS)" [#262](https://github.com/pichillilorenzo/flutter_inappwebview/pull/262) (thanks to [AlexVincent525](https://github.com/AlexVincent525))
- Merge "Fix crash when `prompt` was called on Android Q." [#262](https://github.com/pichillilorenzo/flutter_inappwebview/pull/263) (thanks to [AlexVincent525](https://github.com/AlexVincent525))
- Fix for Android and iOS `InAppBrowser` for some controller methods not exposed. - Fix for Android and iOS `InAppBrowser` for some controller methods not exposed.
- Fixed "App Crashes after clicking on dropdown (Using inappwebview)" [#182](https://github.com/pichillilorenzo/flutter_inappwebview/issues/182) - Fixed "App Crashes after clicking on dropdown (Using inappwebview)" [#182](https://github.com/pichillilorenzo/flutter_inappwebview/issues/182)
- Fixed "webview can not be released when in ios" [#225](https://github.com/pichillilorenzo/flutter_inappwebview/issues/225). Now the iOS WebView is released from memory when it is disposed from Flutter. - Fixed "webview can not be released when in ios" [#225](https://github.com/pichillilorenzo/flutter_inappwebview/issues/225). Now the iOS WebView is released from memory when it is disposed from Flutter.
- Fixed "Setting of presentationStyle not working on iOS" [#213](https://github.com/pichillilorenzo/flutter_inappwebview/issues/213) - Fixed "Setting of presentationStyle not working on iOS" [#213](https://github.com/pichillilorenzo/flutter_inappwebview/issues/213)
- Fixed "Android zoom issues" [#270](https://github.com/pichillilorenzo/flutter_inappwebview/issues/270)
### BREAKING CHANGES ### BREAKING CHANGES
......
File mode changed from 100644 to 100755
...@@ -11,8 +11,8 @@ A Flutter plugin that allows you to add an inline webview or open an in-app brow ...@@ -11,8 +11,8 @@ A Flutter plugin that allows you to add an inline webview or open an in-app brow
### Requirements ### Requirements
- Dart sdk: ">=2.0.0-dev.68.0 <3.0.0" - Dart sdk: ">=2.7.0 <3.0.0"
- Flutter: ">=1.9.1+hotfix.5 <2.0.0" - Flutter: ">=1.12.13+hotfix.5"
- Android: `minSdkVersion 17` and add support for `androidx` (see [AndroidX Migration](https://flutter.dev/docs/development/androidx-migration) to migrate an existing app) - Android: `minSdkVersion 17` and add support for `androidx` (see [AndroidX Migration](https://flutter.dev/docs/development/androidx-migration) to migrate an existing app)
- iOS: `--ios-language swift`, Xcode version `>= 11` - iOS: `--ios-language swift`, Xcode version `>= 11`
...@@ -448,6 +448,15 @@ Instead, on the `onLoadStop` WebView event, you can use `callHandler` directly: ...@@ -448,6 +448,15 @@ Instead, on the `onLoadStop` WebView event, you can use `callHandler` directly:
* `selectionGranularity`: The level of granularity with which the user can interactively select content in the web view. * `selectionGranularity`: The level of granularity with which the user can interactively select content in the web view.
* `dataDetectorTypes`: Specifying a dataDetectoryTypes value adds interactivity to web content that matches the value. * `dataDetectorTypes`: Specifying a dataDetectoryTypes value adds interactivity to web content that matches the value.
* `sharedCookiesEnabled`: Set `true` if shared cookies from `HTTPCookieStorage.shared` should used for every load request in the WebView. * `sharedCookiesEnabled`: Set `true` if shared cookies from `HTTPCookieStorage.shared` should used for every load request in the WebView.
* `automaticallyAdjustsScrollIndicatorInsets`: Configures whether the scroll indicator insets are automatically adjusted by the system. The default value is `false`.
* `accessibilityIgnoresInvertColors`: A Boolean value indicating whether the view ignores an accessibility request to invert its colors. The default value is `false`.
* `decelerationRate`: A `IOSUIScrollViewDecelerationRate` value that determines the rate of deceleration after the user lifts their finger. The default value is `IOSUIScrollViewDecelerationRate.NORMAL`.
* `alwaysBounceVertical`: A Boolean value that determines whether bouncing always occurs when vertical scrolling reaches the end of the content. The default value is `false`.
* `alwaysBounceHorizontal`: A Boolean value that determines whether bouncing always occurs when horizontal scrolling reaches the end of the content view. The default value is `false`.
* `scrollsToTop`: A Boolean value that controls whether the scroll-to-top gesture is enabled. The default value is `true`.
* `isPagingEnabled`: A Boolean value that determines whether paging is enabled for the scroll view. The default value is `false`.
* `maximumZoomScale`: A floating-point value that specifies the maximum scale factor that can be applied to the scroll view's content. The default value is `1.0`.
* `minimumZoomScale`: A floating-point value that specifies the minimum scale factor that can be applied to the scroll view's content. The default value is `1.0`.
#### `InAppWebView` Events #### `InAppWebView` Events
...@@ -479,10 +488,14 @@ Event names that starts with `android` or `ios` are events platform-specific. ...@@ -479,10 +488,14 @@ Event names that starts with `android` or `ios` are events platform-specific.
* `onAjaxProgress`: Event fired as an `XMLHttpRequest` progress (to use this event, the `useShouldInterceptAjaxRequest` option must be `true`). * `onAjaxProgress`: Event fired as an `XMLHttpRequest` progress (to use this event, the `useShouldInterceptAjaxRequest` option must be `true`).
* `shouldInterceptFetchRequest`: Event fired when a request is sent to a server through [Fetch API](https://developer.mozilla.org/it/docs/Web/API/Fetch_API) (to use this event, the `useShouldInterceptFetchRequest` option must be `true`). * `shouldInterceptFetchRequest`: Event fired when a request is sent to a server through [Fetch API](https://developer.mozilla.org/it/docs/Web/API/Fetch_API) (to use this event, the `useShouldInterceptFetchRequest` option must be `true`).
* `onPrint`: Event fired when `window.print()` is called from JavaScript side. * `onPrint`: Event fired when `window.print()` is called from JavaScript side.
* `onLongPressHitTestResult`: Event fired when an HTML element of the webview has been clicked and held.
* `androidOnSafeBrowsingHit`: Event fired when the webview notifies that a loading URL has been flagged by Safe Browsing (available only on Android). * `androidOnSafeBrowsingHit`: Event fired when the webview notifies that a loading URL has been flagged by Safe Browsing (available only on Android).
* `androidOnPermissionRequest`: Event fired when the webview is requesting permission to access the specified resources and the permission currently isn't granted or denied (available only on Android). * `androidOnPermissionRequest`: Event fired when the webview is requesting permission to access the specified resources and the permission currently isn't granted or denied (available only on Android).
* `androidOnGeolocationPermissionsShowPrompt`: Event that notifies the host application that web content from the specified origin is attempting to use the Geolocation API, but no permission state is currently set for that origin (available only on Android). * `androidOnGeolocationPermissionsShowPrompt`: Event that notifies the host application that web content from the specified origin is attempting to use the Geolocation API, but no permission state is currently set for that origin (available only on Android).
* `androidOnGeolocationPermissionsHidePrompt`: Notify the host application that a request for Geolocation permissions, made with a previous call to `androidOnGeolocationPermissionsShowPrompt` has been canceled. (available only on Android). * `androidOnGeolocationPermissionsHidePrompt`: Notify the host application that a request for Geolocation permissions, made with a previous call to `androidOnGeolocationPermissionsShowPrompt` has been canceled. (available only on Android).
* `iosOnWebContentProcessDidTerminate`: Invoked when the web view's web content process is terminated (available only on iOS).
* `iosOnDidCommit`: Called when the web view begins to receive web content (available only on iOS).
* `iosOnDidReceiveServerRedirectForProvisionalNavigation`: Called when a web view receives a server redirect (available only on iOS).
### `InAppBrowser` class ### `InAppBrowser` class
...@@ -553,7 +566,12 @@ class MyInAppBrowser extends InAppBrowser { ...@@ -553,7 +566,12 @@ class MyInAppBrowser extends InAppBrowser {
} }
} }
void main() => runApp(new MyApp()); void main() {
WidgetsFlutterBinding.ensureInitialized();
runApp(
new MyApp(),
);
}
class MyApp extends StatefulWidget { class MyApp extends StatefulWidget {
final MyInAppBrowser browser = new MyInAppBrowser(); final MyInAppBrowser browser = new MyInAppBrowser();
...@@ -707,7 +725,12 @@ class MyChromeSafariBrowser extends ChromeSafariBrowser { ...@@ -707,7 +725,12 @@ class MyChromeSafariBrowser extends ChromeSafariBrowser {
} }
} }
void main() => runApp(new MyApp()); void main() {
WidgetsFlutterBinding.ensureInitialized();
runApp(
new MyApp(),
);
}
class MyApp extends StatefulWidget { class MyApp extends StatefulWidget {
final ChromeSafariBrowser browser = new MyChromeSafariBrowser(new MyInAppBrowser()); final ChromeSafariBrowser browser = new MyChromeSafariBrowser(new MyInAppBrowser());
......
File mode changed from 100644 to 100755
File mode changed from 100644 to 100755
File mode changed from 100644 to 100755
File mode changed from 100644 to 100755
File mode changed from 100644 to 100755
File mode changed from 100644 to 100755
File mode changed from 100644 to 100755
File mode changed from 100644 to 100755
File mode changed from 100644 to 100755
File mode changed from 100644 to 100755
File mode changed from 100644 to 100755
File mode changed from 100644 to 100755
File mode changed from 100644 to 100755
File mode changed from 100644 to 100755
File mode changed from 100644 to 100755
File mode changed from 100644 to 100755
...@@ -2,7 +2,10 @@ package com.pichillilorenzo.flutter_inappwebview.ChromeCustomTabs; ...@@ -2,7 +2,10 @@ package com.pichillilorenzo.flutter_inappwebview.ChromeCustomTabs;
import com.pichillilorenzo.flutter_inappwebview.Options; import com.pichillilorenzo.flutter_inappwebview.Options;
public class ChromeCustomTabsOptions extends Options { import java.util.HashMap;
import java.util.Map;
public class ChromeCustomTabsOptions implements Options {
final static String LOG_TAG = "ChromeCustomTabsOptions"; final static String LOG_TAG = "ChromeCustomTabsOptions";
...@@ -14,4 +17,53 @@ public class ChromeCustomTabsOptions extends Options { ...@@ -14,4 +17,53 @@ public class ChromeCustomTabsOptions extends Options {
public String packageName; public String packageName;
public Boolean keepAliveEnabled = false; public Boolean keepAliveEnabled = false;
@Override
public ChromeCustomTabsOptions parse(HashMap<String, Object> options) {
for (Map.Entry<String, Object> pair : options.entrySet()) {
String key = pair.getKey();
Object value = pair.getValue();
if (value == null) {
continue;
}
switch (key) {
case "addDefaultShareMenuItem":
addDefaultShareMenuItem = (boolean) value;
break;
case "showTitle":
showTitle = (boolean) value;
break;
case "toolbarBackgroundColor":
toolbarBackgroundColor = (String) value;
break;
case "enableUrlBarHiding":
enableUrlBarHiding = (boolean) value;
break;
case "instantAppsEnabled":
instantAppsEnabled = (boolean) value;
break;
case "packageName":
packageName = (String) value;
break;
case "keepAliveEnabled":
keepAliveEnabled = (boolean) value;
break;
}
}
return this;
}
@Override
public HashMap<String, Object> getHashMap() {
HashMap<String, Object> options = new HashMap<>();
options.put("addDefaultShareMenuItem", addDefaultShareMenuItem);
options.put("showTitle", showTitle);
options.put("toolbarBackgroundColor", toolbarBackgroundColor);
options.put("enableUrlBarHiding", enableUrlBarHiding);
options.put("instantAppsEnabled", instantAppsEnabled);
options.put("packageName", packageName);
options.put("keepAliveEnabled", keepAliveEnabled);
return options;
}
} }
package com.pichillilorenzo.flutter_inappwebview; package com.pichillilorenzo.flutter_inappwebview;
public class InAppBrowserOptions extends Options { import java.util.HashMap;
import java.util.Map;
public class InAppBrowserOptions implements Options {
public static final String LOG_TAG = "InAppBrowserOptions"; public static final String LOG_TAG = "InAppBrowserOptions";
...@@ -13,4 +16,58 @@ public class InAppBrowserOptions extends Options { ...@@ -13,4 +16,58 @@ public class InAppBrowserOptions extends Options {
public Boolean hideTitleBar = false; public Boolean hideTitleBar = false;
public Boolean closeOnCannotGoBack = true; public Boolean closeOnCannotGoBack = true;
public Boolean progressBar = true; public Boolean progressBar = true;
@Override
public InAppBrowserOptions parse(HashMap<String, Object> options) {
for (Map.Entry<String, Object> pair : options.entrySet()) {
String key = pair.getKey();
Object value = pair.getValue();
if (value == null) {
continue;
}
switch (key) {
case "hidden":
hidden = (boolean) value;
break;
case "toolbarTop":
toolbarTop = (boolean) value;
break;
case "toolbarTopBackgroundColor":
toolbarTopBackgroundColor = (String) value;
break;
case "toolbarTopFixedTitle":
toolbarTopFixedTitle = (String) value;
break;
case "hideUrlBar":
hideUrlBar = (boolean) value;
break;
case "hideTitleBar":
hideTitleBar = (boolean) value;
break;
case "closeOnCannotGoBack":
closeOnCannotGoBack = (boolean) value;
break;
case "progressBar":
progressBar = (boolean) value;
break;
}
}
return this;
}
@Override
public HashMap<String, Object> getHashMap() {
HashMap<String, Object> options = new HashMap<>();
options.put("hidden", hidden);
options.put("toolbarTop", toolbarTop);
options.put("toolbarTopBackgroundColor", toolbarTopBackgroundColor);
options.put("toolbarTopFixedTitle", toolbarTopFixedTitle);
options.put("hideUrlBar", hideUrlBar);
options.put("hideTitleBar", hideTitleBar);
options.put("closeOnCannotGoBack", closeOnCannotGoBack);
options.put("progressBar", progressBar);
return options;
}
} }
...@@ -4,14 +4,20 @@ import android.content.Context; ...@@ -4,14 +4,20 @@ import android.content.Context;
import android.graphics.Bitmap; import android.graphics.Bitmap;
import android.graphics.Canvas; import android.graphics.Canvas;
import android.graphics.Color; import android.graphics.Color;
import android.graphics.Point;
import android.graphics.Rect;
import android.os.Build; import android.os.Build;
import android.print.PrintAttributes; import android.print.PrintAttributes;
import android.print.PrintDocumentAdapter; import android.print.PrintDocumentAdapter;
import android.print.PrintManager; import android.print.PrintManager;
import android.util.AttributeSet; import android.util.AttributeSet;
import android.util.Log; import android.util.Log;
import android.view.ContextMenu;
import android.view.Menu;
import android.view.MenuItem;
import android.view.MotionEvent; import android.view.MotionEvent;
import android.view.View; import android.view.View;
import android.view.ViewParent;
import android.webkit.CookieManager; import android.webkit.CookieManager;
import android.webkit.DownloadListener; import android.webkit.DownloadListener;
import android.webkit.ValueCallback; import android.webkit.ValueCallback;
...@@ -21,6 +27,10 @@ import android.webkit.WebSettings; ...@@ -21,6 +27,10 @@ import android.webkit.WebSettings;
import android.webkit.WebStorage; import android.webkit.WebStorage;
import androidx.annotation.RequiresApi; import androidx.annotation.RequiresApi;
import androidx.appcompat.widget.PopupMenu;
import android.view.ActionMode;
import android.webkit.WebView;
import com.pichillilorenzo.flutter_inappwebview.ContentBlocker.ContentBlocker; import com.pichillilorenzo.flutter_inappwebview.ContentBlocker.ContentBlocker;
import com.pichillilorenzo.flutter_inappwebview.ContentBlocker.ContentBlockerAction; import com.pichillilorenzo.flutter_inappwebview.ContentBlocker.ContentBlockerAction;
...@@ -28,7 +38,6 @@ import com.pichillilorenzo.flutter_inappwebview.ContentBlocker.ContentBlockerHan ...@@ -28,7 +38,6 @@ import com.pichillilorenzo.flutter_inappwebview.ContentBlocker.ContentBlockerHan
import com.pichillilorenzo.flutter_inappwebview.ContentBlocker.ContentBlockerTrigger; import com.pichillilorenzo.flutter_inappwebview.ContentBlocker.ContentBlockerTrigger;
import com.pichillilorenzo.flutter_inappwebview.FlutterWebView; import com.pichillilorenzo.flutter_inappwebview.FlutterWebView;
import com.pichillilorenzo.flutter_inappwebview.InAppBrowserActivity; import com.pichillilorenzo.flutter_inappwebview.InAppBrowserActivity;
import com.pichillilorenzo.flutter_inappwebview.InAppWebViewFlutterPlugin;
import com.pichillilorenzo.flutter_inappwebview.JavaScriptBridgeInterface; import com.pichillilorenzo.flutter_inappwebview.JavaScriptBridgeInterface;
import com.pichillilorenzo.flutter_inappwebview.Shared; import com.pichillilorenzo.flutter_inappwebview.Shared;
import com.pichillilorenzo.flutter_inappwebview.Util; import com.pichillilorenzo.flutter_inappwebview.Util;
...@@ -115,7 +124,7 @@ final public class InAppWebView extends InputAwareWebView { ...@@ -115,7 +124,7 @@ final public class InAppWebView extends InputAwareWebView {
static final String interceptAjaxRequestsJS = "(function(ajax) {" + static final String interceptAjaxRequestsJS = "(function(ajax) {" +
" var send = ajax.prototype.send;" + " var send = ajax.prototype.send;" +
" var openUrl = ajax.prototype.openUrl;" + " var open = ajax.prototype.open;" +
" var setRequestHeader = ajax.prototype.setRequestHeader;" + " var setRequestHeader = ajax.prototype.setRequestHeader;" +
" ajax.prototype._flutter_inappwebview_url = null;" + " ajax.prototype._flutter_inappwebview_url = null;" +
" ajax.prototype._flutter_inappwebview_method = null;" + " ajax.prototype._flutter_inappwebview_method = null;" +
...@@ -148,7 +157,7 @@ final public class InAppWebView extends InputAwareWebView { ...@@ -148,7 +157,7 @@ final public class InAppWebView extends InputAwareWebView {
" }" + " }" +
" callback(null);" + " callback(null);" +
" };" + " };" +
" ajax.prototype.openUrl = function(method, url, isAsync, user, password) {" + " ajax.prototype.open = function(method, url, isAsync, user, password) {" +
" isAsync = (isAsync != null) ? isAsync : true;" + " isAsync = (isAsync != null) ? isAsync : true;" +
" this._flutter_inappwebview_url = url;" + " this._flutter_inappwebview_url = url;" +
" this._flutter_inappwebview_method = method;" + " this._flutter_inappwebview_method = method;" +
...@@ -156,7 +165,7 @@ final public class InAppWebView extends InputAwareWebView { ...@@ -156,7 +165,7 @@ final public class InAppWebView extends InputAwareWebView {
" this._flutter_inappwebview_user = user;" + " this._flutter_inappwebview_user = user;" +
" this._flutter_inappwebview_password = password;" + " this._flutter_inappwebview_password = password;" +
" this._flutter_inappwebview_request_headers = {};" + " this._flutter_inappwebview_request_headers = {};" +
" openUrl.call(this, method, url, isAsync, user, password);" + " open.call(this, method, url, isAsync, user, password);" +
" };" + " };" +
" ajax.prototype.setRequestHeader = function(header, value) {" + " ajax.prototype.setRequestHeader = function(header, value) {" +
" this._flutter_inappwebview_request_headers[header] = value;" + " this._flutter_inappwebview_request_headers[header] = value;" +
...@@ -311,7 +320,7 @@ final public class InAppWebView extends InputAwareWebView { ...@@ -311,7 +320,7 @@ final public class InAppWebView extends InputAwareWebView {
" };" + " };" +
" if ((self._flutter_inappwebview_method != result.method && result.method != null) || (self._flutter_inappwebview_url != result.url && result.url != null)) {" + " if ((self._flutter_inappwebview_method != result.method && result.method != null) || (self._flutter_inappwebview_url != result.url && result.url != null)) {" +
" self.abort();" + " self.abort();" +
" self.openUrl(result.method, result.url, result.isAsync, result.user, result.password);" + " self.open(result.method, result.url, result.isAsync, result.user, result.password);" +
" return;" + " return;" +
" }" + " }" +
" }" + " }" +
...@@ -527,6 +536,7 @@ final public class InAppWebView extends InputAwareWebView { ...@@ -527,6 +536,7 @@ final public class InAppWebView extends InputAwareWebView {
this.channel = (this.inAppBrowserActivity != null) ? this.inAppBrowserActivity.channel : this.flutterWebView.channel; this.channel = (this.inAppBrowserActivity != null) ? this.inAppBrowserActivity.channel : this.flutterWebView.channel;
this.id = id; this.id = id;
this.options = options; this.options = options;
//Shared.activity.registerForContextMenu(this);
} }
@Override @Override
...@@ -643,16 +653,9 @@ final public class InAppWebView extends InputAwareWebView { ...@@ -643,16 +653,9 @@ final public class InAppWebView extends InputAwareWebView {
settings.setSansSerifFontFamily(options.sansSerifFontFamily); settings.setSansSerifFontFamily(options.sansSerifFontFamily);
settings.setSerifFontFamily(options.serifFontFamily); settings.setSerifFontFamily(options.serifFontFamily);
settings.setStandardFontFamily(options.standardFontFamily); settings.setStandardFontFamily(options.standardFontFamily);
if (options.preferredContentMode != null) { if (options.preferredContentMode != null &&
switch (fromValue(options.preferredContentMode)) { options.preferredContentMode == PreferredContentModeOptionType.DESKTOP.toValue()) {
case DESKTOP: setDesktopMode(true);
setDesktopMode(true);
break;
case MOBILE:
case RECOMMENDED:
setDesktopMode(false);
break;
}
} }
settings.setSaveFormData(options.saveFormData); settings.setSaveFormData(options.saveFormData);
if (options.incognito) if (options.incognito)
...@@ -688,18 +691,13 @@ final public class InAppWebView extends InputAwareWebView { ...@@ -688,18 +691,13 @@ final public class InAppWebView extends InputAwareWebView {
setVerticalScrollBarEnabled(!options.disableVerticalScroll); setVerticalScrollBarEnabled(!options.disableVerticalScroll);
setHorizontalScrollBarEnabled(!options.disableHorizontalScroll); setHorizontalScrollBarEnabled(!options.disableHorizontalScroll);
setOnTouchListener(new View.OnTouchListener() { setOnTouchListener(new View.OnTouchListener() {
float m_downX; float m_downX;
float m_downY; float m_downY;
@Override @Override
public boolean onTouch(View v, MotionEvent event) { public boolean onTouch(View v, MotionEvent event) {
if (event.getPointerCount() > 1) {
//Multi touch detected
return true;
}
if (options.disableHorizontalScroll && options.disableVerticalScroll) { if (options.disableHorizontalScroll && options.disableVerticalScroll) {
return (event.getAction() == MotionEvent.ACTION_MOVE); return (event.getAction() == MotionEvent.ACTION_MOVE);
} }
...@@ -729,6 +727,31 @@ final public class InAppWebView extends InputAwareWebView { ...@@ -729,6 +727,31 @@ final public class InAppWebView extends InputAwareWebView {
return false; return false;
} }
}); });
setOnLongClickListener(new OnLongClickListener() {
@Override
public boolean onLongClick(View v) {
HitTestResult hitTestResult = getHitTestResult();
Map<String, Object> hitTestResultMap = new HashMap<>();
hitTestResultMap.put("type", hitTestResult.getType());
hitTestResultMap.put("extra", hitTestResult.getExtra());
Map<String, Object> obj = new HashMap<>();
if (inAppBrowserActivity != null)
obj.put("uuid", inAppBrowserActivity.uuid);
obj.put("hitTestResult", hitTestResultMap);
channel.invokeMethod("onLongPressHitTestResult", obj);
return false;
}
});
}
private Point lastTouch;
@Override
public boolean onTouchEvent(MotionEvent ev) {
lastTouch = new Point((int) ev.getX(), (int) ev.getY()) ;
return super.onTouchEvent(ev);
} }
public void setIncognito(boolean enabled) { public void setIncognito(boolean enabled) {
...@@ -1334,7 +1357,69 @@ final public class InAppWebView extends InputAwareWebView { ...@@ -1334,7 +1357,69 @@ final public class InAppWebView extends InputAwareWebView {
public Float getUpdatedScale() { public Float getUpdatedScale() {
return scale; return scale;
} }
/*
@Override
public void onCreateContextMenu(ContextMenu menu) {
Log.d(LOG_TAG, getHitTestResult().getType() + "");
String extra = getHitTestResult().getExtra();
//if (getHitTestResult().getType() == 7 || getHitTestResult().getType() == 5)
if (extra != null)
Log.d(LOG_TAG, extra);
Log.d(LOG_TAG, "\n\nonCreateContextMenu\n\n");
for(int i = 0; i < menu.size(); i++) {
Log.d(LOG_TAG, menu.getItem(i).toString());
}
}
private Integer mActionMode;
private CustomActionModeCallback mActionModeCallback;
@Override
public ActionMode startActionMode(ActionMode.Callback callback, int mode) {
Log.d(LOG_TAG, "startActionMode");
ViewParent parent = getParent();
if (parent == null || mActionMode != null) {
return null;
}
mActionModeCallback = new CustomActionModeCallback();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
mActionMode = ActionMode.TYPE_FLOATING;
//return Shared.activity.getWindow().getDecorView().startActionMode(mActionModeCallback, mActionMode);
return parent.startActionModeForChild(this, mActionModeCallback, mActionMode);
} else {
return parent.startActionModeForChild(this, mActionModeCallback);
}
}
private class CustomActionModeCallback implements ActionMode.Callback {
@Override
public boolean onCreateActionMode(ActionMode mode, Menu menu) {
menu.add("ciao");
return true;
}
@Override
public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
return false;
}
@Override
public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
mode.finish();
return true;
}
// Called when the user exits the action mode
@Override
public void onDestroyActionMode(ActionMode mode) {
clearFocus();
}
}
*/
@Override @Override
public void dispose() { public void dispose() {
super.dispose(); super.dispose();
......
...@@ -4,16 +4,18 @@ import android.os.Build; ...@@ -4,16 +4,18 @@ import android.os.Build;
import android.util.Log; import android.util.Log;
import android.webkit.WebSettings; import android.webkit.WebSettings;
import com.pichillilorenzo.flutter_inappwebview.ChromeCustomTabs.ChromeCustomTabsOptions;
import com.pichillilorenzo.flutter_inappwebview.Options; import com.pichillilorenzo.flutter_inappwebview.Options;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import static android.webkit.WebSettings.LayoutAlgorithm.NORMAL; import static android.webkit.WebSettings.LayoutAlgorithm.NORMAL;
public class InAppWebViewOptions extends Options { public class InAppWebViewOptions implements Options {
public static final String LOG_TAG = "InAppWebViewOptions"; public static final String LOG_TAG = "InAppWebViewOptions";
...@@ -85,47 +87,316 @@ public class InAppWebViewOptions extends Options { ...@@ -85,47 +87,316 @@ public class InAppWebViewOptions extends Options {
public String regexToCancelSubFramesLoading; public String regexToCancelSubFramesLoading;
@Override @Override
public Object onParse(Map.Entry<String, Object> pair) { public InAppWebViewOptions parse(HashMap<String, Object> options) {
if (pair.getKey().equals("layoutAlgorithm")) { for (Map.Entry<String, Object> pair : options.entrySet()) {
String value = (String) pair.getValue(); String key = pair.getKey();
if (value != null) { Object value = pair.getValue();
switch (value) { if (value == null) {
case "NORMAL": continue;
pair.setValue(NORMAL); }
return pair;
case "TEXT_AUTOSIZING": switch (key) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { case "useShouldOverrideUrlLoading":
return pair.setValue(WebSettings.LayoutAlgorithm.TEXT_AUTOSIZING); useShouldOverrideUrlLoading = (Boolean) value;
} else { break;
pair.setValue(NORMAL); case "useOnLoadResource":
} useOnLoadResource = (Boolean) value;
return pair; break;
} case "useOnDownloadStart":
useOnDownloadStart = (Boolean) value;
break;
case "clearCache":
clearCache = (Boolean) value;
break;
case "userAgent":
userAgent = (String) value;
break;
case "applicationNameForUserAgent":
applicationNameForUserAgent = (String) value;
break;
case "javaScriptEnabled":
javaScriptEnabled = (Boolean) value;
break;
case "debuggingEnabled":
debuggingEnabled = (Boolean) value;
break;
case "javaScriptCanOpenWindowsAutomatically":
javaScriptCanOpenWindowsAutomatically = (Boolean) value;
break;
case "mediaPlaybackRequiresUserGesture":
mediaPlaybackRequiresUserGesture = (Boolean) value;
break;
case "minimumFontSize":
minimumFontSize = (Integer) value;
break;
case "verticalScrollBarEnabled":
verticalScrollBarEnabled = (Boolean) value;
break;
case "horizontalScrollBarEnabled":
horizontalScrollBarEnabled = (Boolean) value;
break;
case "resourceCustomSchemes":
resourceCustomSchemes = (List<String>) value;
break;
case "contentBlockers":
contentBlockers = (List<Map<String, Map<String, Object>>>) value;
break;
case "preferredContentMode":
preferredContentMode = (Integer) value;
break;
case "useShouldInterceptAjaxRequest":
useShouldInterceptAjaxRequest = (Boolean) value;
break;
case "useShouldInterceptFetchRequest":
useShouldInterceptFetchRequest = (Boolean) value;
break;
case "incognito":
incognito = (Boolean) value;
break;
case "cacheEnabled":
cacheEnabled = (Boolean) value;
break;
case "transparentBackground":
transparentBackground = (Boolean) value;
break;
case "disableVerticalScroll":
disableVerticalScroll = (Boolean) value;
break;
case "disableHorizontalScroll":
disableHorizontalScroll = (Boolean) value;
break;
case "textZoom":
textZoom = (Integer) value;
break;
case "clearSessionCache":
clearSessionCache = (Boolean) value;
break;
case "builtInZoomControls":
builtInZoomControls = (Boolean) value;
break;
case "displayZoomControls":
displayZoomControls = (Boolean) value;
break;
case "supportZoom":
supportZoom = (Boolean) value;
break;
case "databaseEnabled":
databaseEnabled = (Boolean) value;
break;
case "domStorageEnabled":
domStorageEnabled = (Boolean) value;
break;
case "useWideViewPort":
useWideViewPort = (Boolean) value;
break;
case "safeBrowsingEnabled":
safeBrowsingEnabled = (Boolean) value;
break;
case "mixedContentMode":
mixedContentMode = (Integer) value;
break;
case "allowContentAccess":
allowContentAccess = (Boolean) value;
break;
case "allowFileAccess":
allowFileAccess = (Boolean) value;
break;
case "allowFileAccessFromFileURLs":
allowFileAccessFromFileURLs = (Boolean) value;
break;
case "allowUniversalAccessFromFileURLs":
allowUniversalAccessFromFileURLs = (Boolean) value;
break;
case "appCachePath":
appCachePath = (String) value;
break;
case "blockNetworkImage":
blockNetworkImage = (Boolean) value;
break;
case "blockNetworkLoads":
blockNetworkLoads = (Boolean) value;
break;
case "cacheMode":
cacheMode = (Integer) value;
break;
case "cursiveFontFamily":
cursiveFontFamily = (String) value;
break;
case "defaultFixedFontSize":
defaultFixedFontSize = (Integer) value;
break;
case "defaultFontSize":
defaultFontSize = (Integer) value;
break;
case "defaultTextEncodingName":
defaultTextEncodingName = (String) value;
break;
case "disabledActionModeMenuItems":
disabledActionModeMenuItems = (Integer) value;
break;
case "fantasyFontFamily":
fantasyFontFamily = (String) value;
break;
case "fixedFontFamily":
fixedFontFamily = (String) value;
break;
case "forceDark":
forceDark = (Integer) value;
break;
case "geolocationEnabled":
geolocationEnabled = (Boolean) value;
break;
case "layoutAlgorithm":
setLayoutAlgorithm((String) value);
break;
case "loadWithOverviewMode":
loadWithOverviewMode = (Boolean) value;
break;
case "loadsImagesAutomatically":
loadsImagesAutomatically = (Boolean) value;
break;
case "minimumLogicalFontSize":
minimumLogicalFontSize = (Integer) value;
break;
case "initialScale":
initialScale = (Integer) value;
break;
case "needInitialFocus":
needInitialFocus = (Boolean) value;
break;
case "offscreenPreRaster":
offscreenPreRaster = (Boolean) value;
break;
case "sansSerifFontFamily":
sansSerifFontFamily = (String) value;
break;
case "serifFontFamily":
serifFontFamily = (String) value;
break;
case "standardFontFamily":
standardFontFamily = (String) value;
break;
case "saveFormData":
saveFormData = (Boolean) value;
break;
case "thirdPartyCookiesEnabled":
thirdPartyCookiesEnabled = (Boolean) value;
break;
case "hardwareAcceleration":
hardwareAcceleration = (Boolean) value;
break;
case "supportMultipleWindows":
supportMultipleWindows = (Boolean) value;
break;
case "regexToCancelSubFramesLoading":
regexToCancelSubFramesLoading = (String) value;
break;
} }
} }
return super.onParse(pair);
return this;
} }
@Override @Override
public Object onGetHashMap(Field field) { public HashMap<String, Object> getHashMap() {
if (field.getName().equals("layoutAlgorithm")) { HashMap<String, Object> options = new HashMap<>();
try { options.put("useShouldOverrideUrlLoading", useShouldOverrideUrlLoading);
WebSettings.LayoutAlgorithm value = (WebSettings.LayoutAlgorithm) field.get(this); options.put("useOnLoadResource", useOnLoadResource);
if (value != null) { options.put("useOnDownloadStart", useOnDownloadStart);
switch (value) { options.put("clearCache", clearCache);
case NORMAL: options.put("userAgent", userAgent);
return "NORMAL"; options.put("applicationNameForUserAgent", applicationNameForUserAgent);
default: options.put("javaScriptEnabled", javaScriptEnabled);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT && value.equals(WebSettings.LayoutAlgorithm.TEXT_AUTOSIZING)) { options.put("debuggingEnabled", debuggingEnabled);
return "TEXT_AUTOSIZING"; options.put("javaScriptCanOpenWindowsAutomatically", javaScriptCanOpenWindowsAutomatically);
} options.put("mediaPlaybackRequiresUserGesture", mediaPlaybackRequiresUserGesture);
return "NORMAL"; options.put("minimumFontSize", minimumFontSize);
options.put("verticalScrollBarEnabled", verticalScrollBarEnabled);
options.put("horizontalScrollBarEnabled", horizontalScrollBarEnabled);
options.put("resourceCustomSchemes", resourceCustomSchemes);
options.put("contentBlockers", contentBlockers);
options.put("preferredContentMode", preferredContentMode);
options.put("useShouldInterceptAjaxRequest", useShouldInterceptAjaxRequest);
options.put("useShouldInterceptFetchRequest", useShouldInterceptFetchRequest);
options.put("incognito", incognito);
options.put("cacheEnabled", cacheEnabled);
options.put("transparentBackground", transparentBackground);
options.put("disableVerticalScroll", disableVerticalScroll);
options.put("disableHorizontalScroll", disableHorizontalScroll);
options.put("textZoom", textZoom);
options.put("clearSessionCache", clearSessionCache);
options.put("builtInZoomControls", builtInZoomControls);
options.put("displayZoomControls", displayZoomControls);
options.put("supportZoom", supportZoom);
options.put("databaseEnabled", databaseEnabled);
options.put("domStorageEnabled", domStorageEnabled);
options.put("useWideViewPort", useWideViewPort);
options.put("safeBrowsingEnabled", safeBrowsingEnabled);
options.put("mixedContentMode", mixedContentMode);
options.put("allowContentAccess", allowContentAccess);
options.put("allowFileAccess", allowFileAccess);
options.put("allowFileAccessFromFileURLs", allowFileAccessFromFileURLs);
options.put("allowUniversalAccessFromFileURLs", allowUniversalAccessFromFileURLs);
options.put("appCachePath", appCachePath);
options.put("blockNetworkImage", blockNetworkImage);
options.put("blockNetworkLoads", blockNetworkLoads);
options.put("cacheMode", cacheMode);
options.put("cursiveFontFamily", cursiveFontFamily);
options.put("defaultFixedFontSize", defaultFixedFontSize);
options.put("defaultFontSize", defaultFontSize);
options.put("defaultTextEncodingName", defaultTextEncodingName);
options.put("disabledActionModeMenuItems", disabledActionModeMenuItems);
options.put("fantasyFontFamily", fantasyFontFamily);
options.put("fixedFontFamily", fixedFontFamily);
options.put("forceDark", forceDark);
options.put("geolocationEnabled", geolocationEnabled);
options.put("layoutAlgorithm", getLayoutAlgorithm());
options.put("loadWithOverviewMode", loadWithOverviewMode);
options.put("loadsImagesAutomatically", loadsImagesAutomatically);
options.put("minimumLogicalFontSize", minimumLogicalFontSize);
options.put("initialScale", initialScale);
options.put("needInitialFocus", needInitialFocus);
options.put("offscreenPreRaster", offscreenPreRaster);
options.put("sansSerifFontFamily", sansSerifFontFamily);
options.put("serifFontFamily", serifFontFamily);
options.put("standardFontFamily", standardFontFamily);
options.put("saveFormData", saveFormData);
options.put("thirdPartyCookiesEnabled", thirdPartyCookiesEnabled);
options.put("hardwareAcceleration", hardwareAcceleration);
options.put("supportMultipleWindows", supportMultipleWindows);
options.put("regexToCancelSubFramesLoading", regexToCancelSubFramesLoading);
return options;
}
private void setLayoutAlgorithm(String value) {
if (value != null) {
switch (value) {
case "NORMAL":
layoutAlgorithm = NORMAL;
case "TEXT_AUTOSIZING":
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
layoutAlgorithm = WebSettings.LayoutAlgorithm.TEXT_AUTOSIZING;
} else {
layoutAlgorithm = NORMAL;
}
break;
}
}
}
private String getLayoutAlgorithm() {
if (layoutAlgorithm != null) {
switch (layoutAlgorithm) {
case NORMAL:
return "NORMAL";
case TEXT_AUTOSIZING:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
return "TEXT_AUTOSIZING";
} else {
return "NORMAL";
} }
}
} catch (IllegalAccessException e) {
Log.d(LOG_TAG, e.getMessage());
} }
} }
return super.onGetHashMap(field); return null;
} }
} }
...@@ -3,11 +3,14 @@ package com.pichillilorenzo.flutter_inappwebview.InAppWebView; ...@@ -3,11 +3,14 @@ package com.pichillilorenzo.flutter_inappwebview.InAppWebView;
import static android.content.Context.INPUT_METHOD_SERVICE; import static android.content.Context.INPUT_METHOD_SERVICE;
import android.content.Context; import android.content.Context;
import android.graphics.Rect;
import android.os.Build;
import android.util.AttributeSet; import android.util.AttributeSet;
import android.util.Log; import android.util.Log;
import android.view.View; import android.view.View;
import android.view.inputmethod.InputMethodManager; import android.view.inputmethod.InputMethodManager;
import android.webkit.WebView; import android.webkit.WebView;
import android.widget.ListPopupWindow;
/** /**
* A WebView subclass that mirrors the same implementation hacks that the system WebView does in * A WebView subclass that mirrors the same implementation hacks that the system WebView does in
...@@ -16,182 +19,227 @@ import android.webkit.WebView; ...@@ -16,182 +19,227 @@ import android.webkit.WebView;
* https://github.com/flutter/plugins/blob/master/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/InputAwareWebView.java * https://github.com/flutter/plugins/blob/master/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/InputAwareWebView.java
*/ */
public class InputAwareWebView extends WebView { public class InputAwareWebView extends WebView {
private static final String LOG_TAG = "InputAwareWebView"; private static final String LOG_TAG = "InputAwareWebView";
public View containerView; public View containerView;
private View threadedInputConnectionProxyView; private View threadedInputConnectionProxyView;
private ThreadedInputConnectionProxyAdapterView proxyAdapterView; private ThreadedInputConnectionProxyAdapterView proxyAdapterView;
public InputAwareWebView(Context context, View containerView) { public InputAwareWebView(Context context, View containerView) {
super(context); super(context);
this.containerView = containerView; this.containerView = containerView;
}
public InputAwareWebView(Context context, AttributeSet attrs) {
super(context, attrs);
this.containerView = null;
}
public InputAwareWebView(Context context) {
super(context);
this.containerView = null;
}
public InputAwareWebView(Context context, AttributeSet attrs, int defaultStyle) {
super(context, attrs, defaultStyle);
this.containerView = null;
}
public void setContainerView(View containerView) {
this.containerView = containerView;
if (proxyAdapterView == null) {
return;
} }
public InputAwareWebView(Context context, AttributeSet attrs) { Log.w(LOG_TAG, "The containerView has changed while the proxyAdapterView exists.");
super(context, attrs); if (containerView != null) {
this.containerView = null; setInputConnectionTarget(proxyAdapterView);
} }
}
public InputAwareWebView(Context context) {
super(context); /**
this.containerView = null; * Set our proxy adapter view to use its cached input connection instead of creating new ones.
} *
* <p>This is used to avoid losing our input connection when the virtual display is resized.
public InputAwareWebView(Context context, AttributeSet attrs, int defaultStyle) { */
super(context, attrs, defaultStyle); public void lockInputConnection() {
this.containerView = null; if (proxyAdapterView == null) {
return;
} }
public void setContainerView(View containerView) { proxyAdapterView.setLocked(true);
this.containerView = containerView; }
if (proxyAdapterView == null) {
return;
}
Log.w(LOG_TAG, "The containerView has changed while the proxyAdapterView exists."); /** Sets the proxy adapter view back to its default behavior. */
if (containerView != null) { public void unlockInputConnection() {
setInputConnectionTarget(proxyAdapterView); if (proxyAdapterView == null) {
} return;
} }
/** proxyAdapterView.setLocked(false);
* Set our proxy adapter view to use its cached input connection instead of creating new ones. }
*
* <p>This is used to avoid losing our input connection when the virtual display is resized. /** Restore the original InputConnection, if needed. */
*/ void dispose() {
public void lockInputConnection() { resetInputConnection();
if (proxyAdapterView == null) { }
return;
} /**
* Creates an InputConnection from the IME thread when needed.
proxyAdapterView.setLocked(true); *
* <p>We only need to create a {@link ThreadedInputConnectionProxyAdapterView} and create an
* InputConnectionProxy on the IME thread when WebView is doing the same thing. So we rely on the
* system calling this method for WebView's proxy view in order to know when we need to create our
* own.
*
* <p>This method would normally be called for any View that used the InputMethodManager. We rely
* on flutter/engine filtering the calls we receive down to the ones in our hierarchy and the
* system WebView in order to know whether or not the system WebView expects an InputConnection on
* the IME thread.
*/
@Override
public boolean checkInputConnectionProxy(final View view) {
// Check to see if the view param is WebView's ThreadedInputConnectionProxyView.
View previousProxy = threadedInputConnectionProxyView;
threadedInputConnectionProxyView = view;
if (previousProxy == view) {
// This isn't a new ThreadedInputConnectionProxyView. Ignore it.
return super.checkInputConnectionProxy(view);
} }
if (containerView == null) {
/** Sets the proxy adapter view back to its default behavior. */ Log.e(
public void unlockInputConnection() { LOG_TAG,
if (proxyAdapterView == null) { "Can't create a proxy view because there's no container view. Text input may not work.");
return; return super.checkInputConnectionProxy(view);
}
proxyAdapterView.setLocked(false);
} }
/** Restore the original InputConnection, if needed. */ // We've never seen this before, so we make the assumption that this is WebView's
void dispose() { // ThreadedInputConnectionProxyView. We are making the assumption that the only view that could
resetInputConnection(); // possibly be interacting with the IMM here is WebView's ThreadedInputConnectionProxyView.
proxyAdapterView =
new ThreadedInputConnectionProxyAdapterView(
/*containerView=*/ containerView,
/*targetView=*/ view,
/*imeHandler=*/ view.getHandler());
setInputConnectionTarget(/*targetView=*/ proxyAdapterView);
return super.checkInputConnectionProxy(view);
}
/**
* Ensure that input creation happens back on {@link #containerView}'s thread once this view no
* longer has focus.
*
* <p>The logic in {@link #checkInputConnectionProxy} forces input creation to happen on Webview's
* thread for all connections. We undo it here so users will be able to go back to typing in
* Flutter UIs as expected.
*/
@Override
public void clearFocus() {
super.clearFocus();
resetInputConnection();
}
/**
* Ensure that input creation happens back on {@link #containerView}.
*
* <p>The logic in {@link #checkInputConnectionProxy} forces input creation to happen on Webview's
* thread for all connections. We undo it here so users will be able to go back to typing in
* Flutter UIs as expected.
*/
private void resetInputConnection() {
if (proxyAdapterView == null) {
// No need to reset the InputConnection to the default thread if we've never changed it.
return;
} }
if (containerView == null) {
/** Log.e(LOG_TAG, "Can't reset the input connection to the container view because there is none.");
* Creates an InputConnection from the IME thread when needed. return;
*
* <p>We only need to create a {@link ThreadedInputConnectionProxyAdapterView} and create an
* InputConnectionProxy on the IME thread when WebView is doing the same thing. So we rely on the
* system calling this method for WebView's proxy view in order to know when we need to create our
* own.
*
* <p>This method would normally be called for any View that used the InputMethodManager. We rely
* on flutter/engine filtering the calls we receive down to the ones in our hierarchy and the
* system WebView in order to know whether or not the system WebView expects an InputConnection on
* the IME thread.
*/
@Override
public boolean checkInputConnectionProxy(final View view) {
// Check to see if the view param is WebView's ThreadedInputConnectionProxyView.
View previousProxy = threadedInputConnectionProxyView;
threadedInputConnectionProxyView = view;
if (previousProxy == view) {
// This isn't a new ThreadedInputConnectionProxyView. Ignore it.
return super.checkInputConnectionProxy(view);
}
if (containerView == null) {
Log.e(
LOG_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
// possibly be interacting with the IMM here is WebView's ThreadedInputConnectionProxyView.
proxyAdapterView =
new ThreadedInputConnectionProxyAdapterView(
/*containerView=*/ containerView,
/*targetView=*/ view,
/*imeHandler=*/ view.getHandler());
setInputConnectionTarget(/*targetView=*/ proxyAdapterView);
return super.checkInputConnectionProxy(view);
} }
setInputConnectionTarget(/*targetView=*/ containerView);
/** }
* Ensure that input creation happens back on {@link #containerView}'s thread once this view no
* longer has focus. /**
* * This is the crucial trick that gets the InputConnection creation to happen on the correct
* <p>The logic in {@link #checkInputConnectionProxy} forces input creation to happen on Webview's * thread pre Android N.
* thread for all connections. We undo it here so users will be able to go back to typing in * https://cs.chromium.org/chromium/src/content/public/android/java/src/org/chromium/content/browser/input/ThreadedInputConnectionFactory.java?l=169&rcl=f0698ee3e4483fad5b0c34159276f71cfaf81f3a
* Flutter UIs as expected. *
*/ * <p>{@code targetView} should have a {@link View#getHandler} method with the thread that future
@Override * InputConnections should be created on.
public void clearFocus() { */
super.clearFocus(); private void setInputConnectionTarget(final View targetView) {
resetInputConnection(); if (containerView == null) {
Log.e(
LOG_TAG,
"Can't set the input connection target because there is no containerView to use as a handler.");
return;
} }
/** targetView.requestFocus();
* Ensure that input creation happens back on {@link #containerView}. containerView.post(
* new Runnable() {
* <p>The logic in {@link #checkInputConnectionProxy} forces input creation to happen on Webview's @Override
* thread for all connections. We undo it here so users will be able to go back to typing in public void run() {
* Flutter UIs as expected. InputMethodManager imm =
*/ (InputMethodManager) getContext().getSystemService(INPUT_METHOD_SERVICE);
private void resetInputConnection() { // This is a hack to make InputMethodManager believe that the target view now has focus.
if (proxyAdapterView == null) { // As a result, InputMethodManager will think that targetView is focused, and will call
// No need to reset the InputConnection to the default thread if we've never changed it. // getHandler() of the view when creating input connection.
return;
} // Step 1: Set targetView as InputMethodManager#mNextServedView. This does not affect
if (containerView == null) { // the real window focus.
Log.e(LOG_TAG, "Can't reset the input connection to the container view because there is none."); targetView.onWindowFocusChanged(true);
return;
// Step 2: Have InputMethodManager focus in on targetView. As a result, IMM will call
// onCreateInputConnection() on targetView on the same thread as
// targetView.getHandler(). It will also call subsequent InputConnection methods on this
// thread. This is the IME thread in cases where targetView is our proxyAdapterView.
// TODO (ALexVincent525): Currently only prompt has been tested, still needs more test cases.
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
imm.isActive(containerView);
}
} }
setInputConnectionTarget(/*targetView=*/ containerView); });
}
@Override
protected void onFocusChanged(boolean focused, int direction, Rect previouslyFocusedRect) {
// This works around a crash when old (<67.0.3367.0) Chromium versions are used.
// Prior to Chromium 67.0.3367 the following sequence happens when a select drop down is shown
// on tablets:
//
// - WebView is calling ListPopupWindow#show
// - buildDropDown is invoked, which sets mDropDownList to a DropDownListView.
// - showAsDropDown is invoked - resulting in mDropDownList being added to the window and is
// also synchronously performing the following sequence:
// - WebView's focus change listener is loosing focus (as mDropDownList got it)
// - WebView is hiding all popups (as it lost focus)
// - WebView's SelectPopupDropDown#hide is invoked.
// - DropDownPopupWindow#dismiss is invoked setting mDropDownList to null.
// - mDropDownList#setSelection is invoked and is throwing a NullPointerException (as we just set mDropDownList to null).
//
// To workaround this, we drop the problematic focus lost call.
// See more details on: https://github.com/flutter/flutter/issues/54164
//
// We don't do this after Android P as it shipped with a new enough WebView version, and it's
// better to not do this on all future Android versions in case DropDownListView's code changes.
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.P
&& isCalledFromListPopupWindowShow()
&& !focused) {
return;
} }
super.onFocusChanged(focused, direction, previouslyFocusedRect);
/** }
* This is the crucial trick that gets the InputConnection creation to happen on the correct
* thread pre Android N. private boolean isCalledFromListPopupWindowShow() {
* https://cs.chromium.org/chromium/src/content/public/android/java/src/org/chromium/content/browser/input/ThreadedInputConnectionFactory.java?l=169&rcl=f0698ee3e4483fad5b0c34159276f71cfaf81f3a StackTraceElement[] stackTraceElements = Thread.currentThread().getStackTrace();
* for (int i = 0; i < stackTraceElements.length; i++) {
* <p>{@code targetView} should have a {@link View#getHandler} method with the thread that future if (stackTraceElements[i].getClassName().equals(ListPopupWindow.class.getCanonicalName())
* InputConnections should be created on. && stackTraceElements[i].getMethodName().equals("show")) {
*/ return true;
private void setInputConnectionTarget(final View targetView) { }
if (containerView == null) {
Log.e(
LOG_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() {
@Override
public void run() {
InputMethodManager imm =
(InputMethodManager) getContext().getSystemService(INPUT_METHOD_SERVICE);
// This is a hack to make InputMethodManager believe that the target view now has focus.
// As a result, InputMethodManager will think that targetView is focused, and will call
// getHandler() of the view when creating input connection.
// Step 1: Set targetView as InputMethodManager#mNextServedView. This does not affect
// the real window focus.
targetView.onWindowFocusChanged(true);
// Step 2: Have InputMethodManager focus in on targetView. As a result, IMM will call
// onCreateInputConnection() on targetView on the same thread as
// targetView.getHandler(). It will also call subsequent InputConnection methods on this
// thread. This is the IME thread in cases where targetView is our proxyAdapterView.
imm.isActive(containerView);
}
});
} }
return false;
}
} }
\ No newline at end of file
...@@ -21,219 +21,9 @@ public class JavaScriptBridgeInterface { ...@@ -21,219 +21,9 @@ public class JavaScriptBridgeInterface {
private InAppBrowserActivity inAppBrowserActivity; private InAppBrowserActivity inAppBrowserActivity;
public MethodChannel channel; public MethodChannel channel;
// https://github.com/taylorhakes/promise-polyfill/blob/master/src/index.js // https://github.com/tildeio/rsvp.js
public static final String promisePolyfillJS = "if (window.Promise == null) {" + public static final String promisePolyfillJS = "if (window.Promise == null) {" +
" var setTimeoutFunc = setTimeout;" + " !function(t,e){\"object\"==typeof exports&&\"undefined\"!=typeof module?e(exports):\"function\"==typeof define&&define.amd?define([\"exports\"],e):e(t.RSVP={})}(this,function(t){\"use strict\";function e(t){var e=t._promiseCallbacks;return e||(e=t._promiseCallbacks={}),e}var r={mixin:function(t){return t.on=this.on,t.off=this.off,t.trigger=this.trigger,t._promiseCallbacks=void 0,t},on:function(t,r){if(\"function\"!=typeof r)throw new TypeError(\"Callback must be a function\");var n=e(this),o=n[t];o||(o=n[t]=[]),-1===o.indexOf(r)&&o.push(r)},off:function(t,r){var n=e(this);if(r){var o=n[t],i=o.indexOf(r);-1!==i&&o.splice(i,1)}else n[t]=[]},trigger:function(t,r,n){var o=e(this)[t];if(o)for(var i=0;i<o.length;i++)(0,o[i])(r,n)}},n={instrument:!1};function o(t,e){if(2!==arguments.length)return n[t];n[t]=e}r.mixin(n);var i=[];function s(t,e,r){1===i.push({name:t,payload:{key:e._guidKey,id:e._id,eventName:t,detail:e._result,childId:r&&r._id,label:e._label,timeStamp:Date.now(),error:n[\"instrument-with-stack\"]?new Error(e._label):null}})&&setTimeout(function(){for(var t=0;t<i.length;t++){var e=i[t],r=e.payload;r.guid=r.key+r.id,r.childGuid=r.key+r.childId,r.error&&(r.stack=r.error.stack),n.trigger(e.name,e.payload)}i.length=0},50)}function u(t,e){if(t&&\"object\"==typeof t&&t.constructor===this)return t;var r=new this(c,e);return m(r,t),r}function c(){}var a=void 0,f=1,l=2,h={error:null};function p(t){try{return t.then}catch(t){return h.error=t,h}}var y=void 0;function _(){try{var t=y;return y=null,t.apply(this,arguments)}catch(t){return h.error=t,h}}function v(t){return y=t,_}function d(t,e,r){if(e.constructor===t.constructor&&r===A&&t.constructor.resolve===u)!function(t,e){e._state===f?b(t,e._result):e._state===l?(e._onError=null,g(t,e._result)):j(e,void 0,function(r){e===r?b(t,r):m(t,r)},function(e){return g(t,e)})}(t,e);else if(r===h){var o=h.error;h.error=null,g(t,o)}else\"function\"==typeof r?function(t,e,r){n.async(function(t){var n=!1,o=v(r).call(e,function(r){n||(n=!0,e===r?b(t,r):m(t,r))},function(e){n||(n=!0,g(t,e))},\"Settle: \"+(t._label||\" unknown promise\"));if(!n&&o===h){n=!0;var i=h.error;h.error=null,g(t,i)}},t)}(t,e,r):b(t,e)}function m(t,e){var r,n;t===e?b(t,e):(n=typeof(r=e),null===r||\"object\"!==n&&\"function\"!==n?b(t,e):d(t,e,p(e)))}function w(t){t._onError&&t._onError(t._result),O(t)}function b(t,e){t._state===a&&(t._result=e,t._state=f,0===t._subscribers.length?n.instrument&&s(\"fulfilled\",t):n.async(O,t))}function g(t,e){t._state===a&&(t._state=l,t._result=e,n.async(w,t))}function j(t,e,r,o){var i=t._subscribers,s=i.length;t._onError=null,i[s]=e,i[s+f]=r,i[s+l]=o,0===s&&t._state&&n.async(O,t)}function O(t){var e=t._subscribers,r=t._state;if(n.instrument&&s(r===f?\"fulfilled\":\"rejected\",t),0!==e.length){for(var o=void 0,i=void 0,u=t._result,c=0;c<e.length;c+=3)o=e[c],i=e[c+r],o?E(r,o,i,u):i(u);t._subscribers.length=0}}function E(t,e,r,n){var o=\"function\"==typeof r,i=void 0;if(i=o?v(r)(n):n,e._state!==a);else if(i===e)g(e,new TypeError(\"A promises callback cannot return that same promise.\"));else if(i===h){var s=h.error;h.error=null,g(e,s)}else o?m(e,i):t===f?b(e,i):t===l&&g(e,i)}function A(t,e,r){var o=this._state;if(o===f&&!t||o===l&&!e)return n.instrument&&s(\"chained\",this,this),this;this._onError=null;var i=new this.constructor(c,r),u=this._result;if(n.instrument&&s(\"chained\",this,i),o===a)j(this,i,t,e);else{var h=o===f?t:e;n.async(function(){return E(o,i,h,u)})}return i}var T=function(){function t(t,e,r,n){this._instanceConstructor=t,this.promise=new t(c,n),this._abortOnReject=r,this._isUsingOwnPromise=t===k,this._isUsingOwnResolve=t.resolve===u,this._init.apply(this,arguments)}return t.prototype._init=function(t,e){var r=e.length||0;this.length=r,this._remaining=r,this._result=new Array(r),this._enumerate(e)},t.prototype._enumerate=function(t){for(var e=this.length,r=this.promise,n=0;r._state===a&&n<e;n++)this._eachEntry(t[n],n,!0);this._checkFullfillment()},t.prototype._checkFullfillment=function(){if(0===this._remaining){var t=this._result;b(this.promise,t),this._result=null}},t.prototype._settleMaybeThenable=function(t,e,r){var n=this._instanceConstructor;if(this._isUsingOwnResolve){var o=p(t);if(o===A&&t._state!==a)t._onError=null,this._settledAt(t._state,e,t._result,r);else if(\"function\"!=typeof o)this._settledAt(f,e,t,r);else if(this._isUsingOwnPromise){var i=new n(c);d(i,t,o),this._willSettleAt(i,e,r)}else this._willSettleAt(new n(function(e){return e(t)}),e,r)}else this._willSettleAt(n.resolve(t),e,r)},t.prototype._eachEntry=function(t,e,r){null!==t&&\"object\"==typeof t?this._settleMaybeThenable(t,e,r):this._setResultAt(f,e,t,r)},t.prototype._settledAt=function(t,e,r,n){var o=this.promise;o._state===a&&(this._abortOnReject&&t===l?g(o,r):(this._setResultAt(t,e,r,n),this._checkFullfillment()))},t.prototype._setResultAt=function(t,e,r,n){this._remaining--,this._result[e]=r},t.prototype._willSettleAt=function(t,e,r){var n=this;j(t,void 0,function(t){return n._settledAt(f,e,t,r)},function(t){return n._settledAt(l,e,t,r)})},t}();function P(t,e,r){this._remaining--,this._result[e]=t===f?{state:\"fulfilled\",value:r}:{state:\"rejected\",reason:r}}var S=\"rsvp_\"+Date.now()+\"-\",R=0;var k=function(){function t(e,r){this._id=R++,this._label=r,this._state=void 0,this._result=void 0,this._subscribers=[],n.instrument&&s(\"created\",this),c!==e&&(\"function\"!=typeof e&&function(){throw new TypeError(\"You must pass a resolver function as the first argument to the promise constructor\")}(),this instanceof t?function(t,e){var r=!1;try{e(function(e){r||(r=!0,m(t,e))},function(e){r||(r=!0,g(t,e))})}catch(e){g(t,e)}}(this,e):function(){throw new TypeError(\"Failed to construct 'Promise': Please use the 'new' operator, this object constructor cannot be called as a function.\")}())}return t.prototype._onError=function(t){var e=this;n.after(function(){e._onError&&n.trigger(\"error\",t,e._label)})},t.prototype.catch=function(t,e){return this.then(void 0,t,e)},t.prototype.finally=function(t,e){var r=this.constructor;return\"function\"==typeof t?this.then(function(e){return r.resolve(t()).then(function(){return e})},function(e){return r.resolve(t()).then(function(){throw e})}):this.then(t,t)},t}();function x(t,e){return{then:function(r,n){return t.call(e,r,n)}}}function M(t,e){var r=function(){for(var r=arguments.length,n=new Array(r+1),o=!1,i=0;i<r;++i){var s=arguments[i];if(!o){if((o=F(s))===h){var u=h.error;h.error=null;var a=new k(c);return g(a,u),a}o&&!0!==o&&(s=x(o,s))}n[i]=s}var f=new k(c);return n[r]=function(t,r){t?g(f,t):void 0===e?m(f,r):!0===e?m(f,function(t){for(var e=t.length,r=new Array(e-1),n=1;n<e;n++)r[n-1]=t[n];return r}(arguments)):Array.isArray(e)?m(f,function(t,e){for(var r={},n=t.length,o=new Array(n),i=0;i<n;i++)o[i]=t[i];for(var s=0;s<e.length;s++)r[e[s]]=o[s+1];return r}(arguments,e)):m(f,r)},o?function(t,e,r,n){return k.all(e).then(function(e){return C(t,e,r,n)})}(f,n,t,this):C(f,n,t,this)};return r.__proto__=t,r}function C(t,e,r,n){if(v(r).apply(n,e)===h){var o=h.error;h.error=null,g(t,o)}return t}function F(t){return null!==t&&\"object\"==typeof t&&(t.constructor===k||p(t))}function I(t,e){return k.all(t,e)}k.cast=u,k.all=function(t,e){return Array.isArray(t)?new T(this,t,!0,e).promise:this.reject(new TypeError(\"Promise.all must be called with an array\"),e)},k.race=function(t,e){var r=new this(c,e);if(!Array.isArray(t))return g(r,new TypeError(\"Promise.race must be called with an array\")),r;for(var n=0;r._state===a&&n<t.length;n++)j(this.resolve(t[n]),void 0,function(t){return m(r,t)},function(t){return g(r,t)});return r},k.resolve=u,k.reject=function(t,e){var r=new this(c,e);return g(r,t),r},k.prototype._guidKey=S,k.prototype.then=A;var N=function(t){function e(e,r,n){return function(t,e){if(!t)throw new ReferenceError(\"this hasn't been initialised - super() hasn't been called\");return!e||\"object\"!=typeof e&&\"function\"!=typeof e?t:e}(this,t.call(this,e,r,!1,n))}return function(t,e){if(\"function\"!=typeof e&&null!==e)throw new TypeError(\"Super expression must either be null or a function, not \"+typeof e);t.prototype=Object.create(e&&e.prototype,{constructor:{value:t,enumerable:!1,writable:!0,configurable:!0}}),e&&(Object.setPrototypeOf?Object.setPrototypeOf(t,e):t.__proto__=e)}(e,t),e}(T);function U(t,e){return Array.isArray(t)?new N(k,t,e).promise:k.reject(new TypeError(\"Promise.allSettled must be called with an array\"),e)}function D(t,e){return k.race(t,e)}N.prototype._setResultAt=P;var K=function(t){function e(e,r){var n=!(arguments.length>2&&void 0!==arguments[2])||arguments[2],o=arguments[3];return function(t,e){if(!t)throw new ReferenceError(\"this hasn't been initialised - super() hasn't been called\");return!e||\"object\"!=typeof e&&\"function\"!=typeof e?t:e}(this,t.call(this,e,r,n,o))}return function(t,e){if(\"function\"!=typeof e&&null!==e)throw new TypeError(\"Super expression must either be null or a function, not \"+typeof e);t.prototype=Object.create(e&&e.prototype,{constructor:{value:t,enumerable:!1,writable:!0,configurable:!0}}),e&&(Object.setPrototypeOf?Object.setPrototypeOf(t,e):t.__proto__=e)}(e,t),e.prototype._init=function(t,e){this._result={},this._enumerate(e)},e.prototype._enumerate=function(t){var e=Object.keys(t),r=e.length,n=this.promise;this._remaining=r;for(var o=void 0,i=void 0,s=0;n._state===a&&s<r;s++)i=t[o=e[s]],this._eachEntry(i,o,!0);this._checkFullfillment()},e}(T);function q(t,e){return k.resolve(t,e).then(function(t){if(null===t||\"object\"!=typeof t)throw new TypeError(\"Promise.hash must be called with an object\");return new K(k,t,e).promise})}var G=function(t){function e(e,r,n){return function(t,e){if(!t)throw new ReferenceError(\"this hasn't been initialised - super() hasn't been called\");return!e||\"object\"!=typeof e&&\"function\"!=typeof e?t:e}(this,t.call(this,e,r,!1,n))}return function(t,e){if(\"function\"!=typeof e&&null!==e)throw new TypeError(\"Super expression must either be null or a function, not \"+typeof e);t.prototype=Object.create(e&&e.prototype,{constructor:{value:t,enumerable:!1,writable:!0,configurable:!0}}),e&&(Object.setPrototypeOf?Object.setPrototypeOf(t,e):t.__proto__=e)}(e,t),e}(K);function L(t,e){return k.resolve(t,e).then(function(t){if(null===t||\"object\"!=typeof t)throw new TypeError(\"hashSettled must be called with an object\");return new G(k,t,!1,e).promise})}function V(t){throw setTimeout(function(){throw t}),t}function W(t){var e={resolve:void 0,reject:void 0};return e.promise=new k(function(t,r){e.resolve=t,e.reject=r},t),e}G.prototype._setResultAt=P;var Y=function(t){function e(e,r,n,o){return function(t,e){if(!t)throw new ReferenceError(\"this hasn't been initialised - super() hasn't been called\");return!e||\"object\"!=typeof e&&\"function\"!=typeof e?t:e}(this,t.call(this,e,r,!0,o,n))}return function(t,e){if(\"function\"!=typeof e&&null!==e)throw new TypeError(\"Super expression must either be null or a function, not \"+typeof e);t.prototype=Object.create(e&&e.prototype,{constructor:{value:t,enumerable:!1,writable:!0,configurable:!0}}),e&&(Object.setPrototypeOf?Object.setPrototypeOf(t,e):t.__proto__=e)}(e,t),e.prototype._init=function(t,e,r,n,o){var i=e.length||0;this.length=i,this._remaining=i,this._result=new Array(i),this._mapFn=o,this._enumerate(e)},e.prototype._setResultAt=function(t,e,r,n){if(n){var o=v(this._mapFn)(r,e);o===h?this._settledAt(l,e,o.error,!1):this._eachEntry(o,e,!1)}else this._remaining--,this._result[e]=r},e}(T);function $(t,e,r){return\"function\"!=typeof e?k.reject(new TypeError(\"map expects a function as a second argument\"),r):k.resolve(t,r).then(function(t){if(!Array.isArray(t))throw new TypeError(\"map must be called with an array\");return new Y(k,t,e,r).promise})}function z(t,e){return k.resolve(t,e)}function B(t,e){return k.reject(t,e)}var H={},J=function(t){function e(){return function(t,e){if(!t)throw new ReferenceError(\"this hasn't been initialised - super() hasn't been called\");return!e||\"object\"!=typeof e&&\"function\"!=typeof e?t:e}(this,t.apply(this,arguments))}return function(t,e){if(\"function\"!=typeof e&&null!==e)throw new TypeError(\"Super expression must either be null or a function, not \"+typeof e);t.prototype=Object.create(e&&e.prototype,{constructor:{value:t,enumerable:!1,writable:!0,configurable:!0}}),e&&(Object.setPrototypeOf?Object.setPrototypeOf(t,e):t.__proto__=e)}(e,t),e.prototype._checkFullfillment=function(){if(0===this._remaining&&null!==this._result){var t=this._result.filter(function(t){return t!==H});b(this.promise,t),this._result=null}},e.prototype._setResultAt=function(t,e,r,n){if(n){this._result[e]=r;var o=v(this._mapFn)(r,e);o===h?this._settledAt(l,e,o.error,!1):this._eachEntry(o,e,!1)}else this._remaining--,r||(this._result[e]=H)},e}(Y);function Q(t,e,r){return\"function\"!=typeof e?k.reject(new TypeError(\"filter expects function as a second argument\"),r):k.resolve(t,r).then(function(t){if(!Array.isArray(t))throw new TypeError(\"filter must be called with an array\");return new J(k,t,e,r).promise})}var X=0,Z=void 0;function tt(t,e){ut[X]=t,ut[X+1]=e,2===(X+=2)&&_t()}var et=\"undefined\"!=typeof window?window:void 0,rt=et||{},nt=rt.MutationObserver||rt.WebKitMutationObserver,ot=\"undefined\"==typeof self&&\"undefined\"!=typeof process&&\"[object process]\"==={}.toString.call(process),it=\"undefined\"!=typeof Uint8ClampedArray&&\"undefined\"!=typeof importScripts&&\"undefined\"!=typeof MessageChannel;function st(){return function(){return setTimeout(ct,1)}}var ut=new Array(1e3);function ct(){for(var t=0;t<X;t+=2){(0,ut[t])(ut[t+1]),ut[t]=void 0,ut[t+1]=void 0}X=0}var at,ft,lt,ht,pt,yt,_t=void 0;ot?(pt=process.nextTick,yt=process.versions.node.match(/^(?:(\\\\d+)\\\\.)?(?:(\\\\d+)\\\\.)?(\\\\*|\\\\d+)$/),Array.isArray(yt)&&\"0\"===yt[1]&&\"10\"===yt[2]&&(pt=setImmediate),_t=function(){return pt(ct)}):nt?(ft=0,lt=new nt(ct),ht=document.createTextNode(\"\"),lt.observe(ht,{characterData:!0}),_t=function(){return ht.data=ft=++ft%2}):it?((at=new MessageChannel).port1.onmessage=ct,_t=function(){return at.port2.postMessage(0)}):_t=void 0===et&&\"function\"==typeof require?function(){try{var t=Function(\"return this\")().require(\"vertx\");return void 0!==(Z=t.runOnLoop||t.runOnContext)?function(){Z(ct)}:st()}catch(t){return st()}}():st(),n.async=tt,n.after=function(t){return setTimeout(t,0)};var vt=z,dt=function(t,e){return n.async(t,e)};function mt(){n.on.apply(n,arguments)}function wt(){n.off.apply(n,arguments)}if(\"undefined\"!=typeof window&&\"object\"==typeof window.__PROMISE_INSTRUMENTATION__){var bt=window.__PROMISE_INSTRUMENTATION__;for(var gt in o(\"instrument\",!0),bt)bt.hasOwnProperty(gt)&&mt(gt,bt[gt])}var jt={asap:tt,cast:vt,Promise:k,EventTarget:r,all:I,allSettled:U,race:D,hash:q,hashSettled:L,rethrow:V,defer:W,denodeify:M,configure:o,on:mt,off:wt,resolve:z,reject:B,map:$,async:dt,filter:Q};t.default=jt,t.asap=tt,t.cast=vt,t.Promise=k,t.EventTarget=r,t.all=I,t.allSettled=U,t.race=D,t.hash=q,t.hashSettled=L,t.rethrow=V,t.defer=W,t.denodeify=M,t.configure=o,t.on=mt,t.off=wt,t.resolve=z,t.reject=B,t.map=$,t.async=dt,t.filter=Q,Object.defineProperty(t,\"__esModule\",{value:!0})});" +
" function isArray(x) {" +
" return Boolean(x && typeof x.length !== \"undefined\");" +
" };" +
" function noop() {}" +
" function bind(fn, thisArg) {" +
" return function() {" +
" fn.apply(thisArg, arguments);" +
" };" +
" };" +
" function Promise(fn) {" +
" if (!(this instanceof Promise))" +
" throw new TypeError(\"Promises must be constructed via new\");" +
" if (typeof fn !== \"function\") throw new TypeError(\"not a function\");" +
" this._state = 0;" +
" this._handled = false;" +
" this._value = undefined;" +
" this._deferreds = [];" +
" doResolve(fn, this);" +
" };" +
" function handle(self, deferred) {" +
" while (self._state === 3) {" +
" self = self._value;" +
" }" +
" if (self._state === 0) {" +
" self._deferreds.push(deferred);" +
" return;" +
" }" +
" self._handled = true;" +
" Promise._immediateFn(function() {" +
" var cb = self._state === 1 ? deferred.onFulfilled : deferred.onRejected;" +
" if (cb === null) {" +
" (self._state === 1 ? resolve : reject)(deferred.promise, self._value);" +
" return;" +
" }" +
" var ret;" +
" try {" +
" ret = cb(self._value);" +
" } catch (e) {" +
" reject(deferred.promise, e);" +
" return;" +
" }" +
" resolve(deferred.promise, ret);" +
" });" +
" };" +
" function resolve(self, newValue) {" +
" try {" +
" if (newValue === self)" +
" throw new TypeError(\"A promise cannot be resolved with itself.\");" +
" if (" +
" newValue &&" +
" (typeof newValue === \"object\" || typeof newValue === \"function\")" +
" ) {" +
" var then = newValue.then;" +
" if (newValue instanceof Promise) {" +
" self._state = 3;" +
" self._value = newValue;" +
" finale(self);" +
" return;" +
" } else if (typeof then === \"function\") {" +
" doResolve(bind(then, newValue), self);" +
" return;" +
" }" +
" }" +
" self._state = 1;" +
" self._value = newValue;" +
" finale(self);" +
" } catch (e) {" +
" reject(self, e);" +
" }" +
" };" +
" function reject(self, newValue) {" +
" self._state = 2;" +
" self._value = newValue;" +
" finale(self);" +
" };" +
" function finale(self) {" +
" if (self._state === 2 && self._deferreds.length === 0) {" +
" Promise._immediateFn(function() {" +
" if (!self._handled) {" +
" Promise._unhandledRejectionFn(self._value);" +
" }" +
" });" +
" }" +
" for (var i = 0, len = self._deferreds.length; i < len; i++) {" +
" handle(self, self._deferreds[i]);" +
" }" +
" self._deferreds = null;" +
" };" +
" function Handler(onFulfilled, onRejected, promise) {" +
" this.onFulfilled = typeof onFulfilled === \"function\" ? onFulfilled : null;" +
" this.onRejected = typeof onRejected === \"function\" ? onRejected : null;" +
" this.promise = promise;" +
" };" +
" function doResolve(fn, self) {" +
" var done = false;" +
" try {" +
" fn(" +
" function(value) {" +
" if (done) return;" +
" done = true;" +
" resolve(self, value);" +
" }," +
" function(reason) {" +
" if (done) return;" +
" done = true;" +
" reject(self, reason);" +
" }" +
" );" +
" } catch (ex) {" +
" if (done) return;" +
" done = true;" +
" reject(self, ex);" +
" }" +
" };" +
" Promise.prototype[\"catch\"] = function(onRejected) {" +
" return this.then(null, onRejected);" +
" };" +
" Promise.prototype.then = function(onFulfilled, onRejected) {" +
" var prom = new this.constructor(noop);" +
" handle(this, new Handler(onFulfilled, onRejected, prom));" +
" return prom;" +
" };" +
" Promise.prototype[\"finally\"] = function finallyConstructor(callback) {" +
" var constructor = this.constructor;" +
" return this.then(" +
" function(value) {" +
" return constructor.resolve(callback()).then(function() {" +
" return value;" +
" });" +
" }," +
" function(reason) {" +
" return constructor.resolve(callback()).then(function() {" +
" return constructor.reject(reason);" +
" });" +
" }" +
" );" +
" };" +
" Promise.all = function(arr) {" +
" return new Promise(function(resolve, reject) {" +
" if (!isArray(arr)) {" +
" return reject(new TypeError(\"Promise.all accepts an array\"));" +
" }" +
" var args = Array.prototype.slice.call(arr);" +
" if (args.length === 0) return resolve([]);" +
" var remaining = args.length;" +
" function res(i, val) {" +
" try {" +
" if (val && (typeof val === \"object\" || typeof val === \"function\")) {" +
" var then = val.then;" +
" if (typeof then === \"function\") {" +
" then.call(" +
" val," +
" function(val) {" +
" res(i, val);" +
" }," +
" reject" +
" );" +
" return;" +
" }" +
" }" +
" args[i] = val;" +
" if (--remaining === 0) {" +
" resolve(args);" +
" }" +
" } catch (ex) {" +
" reject(ex);" +
" }" +
" }" +
" for (var i = 0; i < args.length; i++) {" +
" res(i, args[i]);" +
" }" +
" });" +
" };" +
" Promise.resolve = function(value) {" +
" if (value && typeof value === \"object\" && value.constructor === Promise) {" +
" return value;" +
" }" +
"" +
" return new Promise(function(resolve) {" +
" resolve(value);" +
" });" +
" };" +
" Promise.reject = function(value) {" +
" return new Promise(function(resolve, reject) {" +
" reject(value);" +
" });" +
" };" +
" Promise.race = function(arr) {" +
" return new Promise(function(resolve, reject) {" +
" if (!isArray(arr)) {" +
" return reject(new TypeError(\"Promise.race accepts an array\"));" +
" }" +
" for (var i = 0, len = arr.length; i < len; i++) {" +
" Promise.resolve(arr[i]).then(resolve, reject);" +
" }" +
" });" +
" };" +
" Promise._immediateFn =" +
" (typeof setImmediate === \"function\" &&" +
" function(fn) {" +
" setImmediate(fn);" +
" }) ||" +
" function(fn) {" +
" setTimeoutFunc(fn, 0);" +
" };" +
" Promise._unhandledRejectionFn = function _unhandledRejectionFn(err) {" +
" if (typeof console !== \"undefined\" && console) {" +
" console.warn(\"Possible Unhandled Promise Rejection:\", err);" +
" }" +
" };" +
"}"; "}";
public static final String flutterInAppBroserJSClass = promisePolyfillJS + " " + "window." + name + ".callHandler = function() {" + public static final String flutterInAppBroserJSClass = promisePolyfillJS + " " + "window." + name + ".callHandler = function() {" +
......
package com.pichillilorenzo.flutter_inappwebview; package com.pichillilorenzo.flutter_inappwebview;
import android.util.Log;
import java.lang.reflect.Field;
import java.util.Iterator;
import java.util.Map;
import java.util.HashMap; import java.util.HashMap;
public class Options { public interface Options {
static String LOG_TAG = "Options"; static String LOG_TAG = "Options";
public Options parse(HashMap<String, Object> options);
public Options parse(HashMap<String, Object> options) { public HashMap<String, Object> getHashMap();
Iterator it = options.entrySet().iterator();
while (it.hasNext()) {
Map.Entry<String, Object> pair = (Map.Entry<String, Object>) it.next();
Object value = this.onParse(pair);
try {
this.getClass().getDeclaredField(pair.getKey()).set(this, value);
} catch (NoSuchFieldException e) {
Log.d(LOG_TAG, e.getMessage());
} catch (IllegalAccessException e) {
Log.d(LOG_TAG, e.getMessage());
}
}
return this;
}
public Object onParse(Map.Entry<String, Object> pair) {
return pair.getValue();
}
public HashMap<String, Object> getHashMap() {
HashMap<String, Object> options = new HashMap<>();
for (Field field : this.getClass().getDeclaredFields()) {
options.put(field.getName(), onGetHashMap(field));
}
return options;
}
public Object onGetHashMap(Field field) {
try {
return field.get(this);
} catch (IllegalAccessException e) {
Log.d(LOG_TAG, e.getMessage());
}
return null;
}
} }
File mode changed from 100644 to 100755
File mode changed from 100644 to 100755
File mode changed from 100644 to 100755
File mode changed from 100644 to 100755
File mode changed from 100644 to 100755
{"_info":"// This is a generated file; do not edit or check into version control.","dependencyGraph":[{"name":"connectivity","dependencies":[]},{"name":"flutter_downloader","dependencies":[]},{"name":"flutter_inappwebview","dependencies":[]},{"name":"path_provider","dependencies":[]},{"name":"permission_handler","dependencies":[]}]} {"info":"This is a generated file; do not edit or check into version control.","plugins":{"ios":[{"name":"connectivity","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/connectivity-0.4.8+5/","dependencies":[]},{"name":"flutter_downloader","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/flutter_downloader-1.4.4/","dependencies":[]},{"name":"flutter_inappwebview","path":"/Users/lorenzopichilli/Desktop/flutter_inappwebview/","dependencies":[]},{"name":"path_provider","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/path_provider-1.6.7/","dependencies":[]},{"name":"permission_handler","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/permission_handler-3.3.0/","dependencies":[]}],"android":[{"name":"connectivity","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/connectivity-0.4.8+5/","dependencies":[]},{"name":"flutter_downloader","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/flutter_downloader-1.4.4/","dependencies":[]},{"name":"flutter_inappwebview","path":"/Users/lorenzopichilli/Desktop/flutter_inappwebview/","dependencies":[]},{"name":"path_provider","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/path_provider-1.6.7/","dependencies":[]},{"name":"permission_handler","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/permission_handler-3.3.0/","dependencies":[]}],"macos":[{"name":"connectivity_macos","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/connectivity_macos-0.1.0+3/","dependencies":[]},{"name":"path_provider_macos","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/path_provider_macos-0.0.4+2/","dependencies":[]}],"linux":[],"windows":[],"web":[]},"dependencyGraph":[{"name":"connectivity","dependencies":["connectivity_macos"]},{"name":"connectivity_macos","dependencies":[]},{"name":"flutter_downloader","dependencies":[]},{"name":"flutter_inappwebview","dependencies":[]},{"name":"path_provider","dependencies":["path_provider_macos"]},{"name":"path_provider_macos","dependencies":[]},{"name":"permission_handler","dependencies":[]}],"date_created":"2020-05-09 04:31:57.281568","version":"1.17.0"}
\ No newline at end of file \ No newline at end of file
File mode changed from 100644 to 100755
File mode changed from 100644 to 100755
File mode changed from 100644 to 100755
File mode changed from 100644 to 100755
File mode changed from 100644 to 100755
package com.pichillilorenzo.flutterwebviewexample; package com.pichillilorenzo.flutterwebviewexample;
import android.os.Bundle; import android.os.Bundle;
import android.view.ActionMode;
import android.view.Menu;
import io.flutter.Log;
import io.flutter.app.FlutterActivity; import io.flutter.app.FlutterActivity;
import io.flutter.plugins.GeneratedPluginRegistrant; import io.flutter.plugins.GeneratedPluginRegistrant;
......
File mode changed from 100644 to 100755
File mode changed from 100644 to 100755
File mode changed from 100644 to 100755
File mode changed from 100644 to 100755
File mode changed from 100644 to 100755
File mode changed from 100644 to 100755
File mode changed from 100644 to 100755
File mode changed from 100644 to 100755
File mode changed from 100644 to 100755
File mode changed from 100644 to 100755
File mode changed from 100644 to 100755
File mode changed from 100644 to 100755
File mode changed from 100644 to 100755
File mode changed from 100644 to 100755
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
<html lang="en"> <html lang="en">
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0"> <!-- <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0"> -->
<meta http-equiv="X-UA-Compatible" content="ie=edge"> <meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Flutter InAppWebView</title> <title>Flutter InAppWebView</title>
<link rel="stylesheet" href="https://getbootstrap.com/docs/4.3/dist/css/bootstrap.min.css"> <link rel="stylesheet" href="https://getbootstrap.com/docs/4.3/dist/css/bootstrap.min.css">
...@@ -26,6 +26,7 @@ ...@@ -26,6 +26,7 @@
<main role="main" class="inner cover"> <main role="main" class="inner cover">
<h1 class="cover-heading">Inline WebView</h1> <h1 class="cover-heading">Inline WebView</h1>
<img src="images/flutter-logo.svg" alt="flutter logo"> <img src="images/flutter-logo.svg" alt="flutter logo">
<a href="index.html"><img src="images/flutter-logo.svg" alt="flutter logo"></a>
<p class="lead">Cover is a one-page template for building simple and beautiful home pages. Download, edit the text, and add your own fullscreen background photo to make it your own.</p> <p class="lead">Cover is a one-page template for building simple and beautiful home pages. Download, edit the text, and add your own fullscreen background photo to make it your own.</p>
</main> </main>
......
File mode changed from 100644 to 100755
File mode changed from 100644 to 100755
File mode changed from 100644 to 100755
File mode changed from 100644 to 100755
...@@ -2,9 +2,11 @@ ...@@ -2,9 +2,11 @@
# This is a generated file; do not edit or check into version control. # This is a generated file; do not edit or check into version control.
export "FLUTTER_ROOT=/Users/lorenzopichilli/flutter" export "FLUTTER_ROOT=/Users/lorenzopichilli/flutter"
export "FLUTTER_APPLICATION_PATH=/Users/lorenzopichilli/Desktop/flutter_inappwebview/example" export "FLUTTER_APPLICATION_PATH=/Users/lorenzopichilli/Desktop/flutter_inappwebview/example"
export "FLUTTER_TARGET=lib/main.dart" export "FLUTTER_TARGET=/Users/lorenzopichilli/Desktop/flutter_inappwebview/example/lib/main.dart"
export "FLUTTER_BUILD_DIR=build" export "FLUTTER_BUILD_DIR=build"
export "SYMROOT=${SOURCE_ROOT}/../build/ios" export "SYMROOT=${SOURCE_ROOT}/../build/ios"
export "OTHER_LDFLAGS=$(inherited) -framework Flutter"
export "FLUTTER_FRAMEWORK_DIR=/Users/lorenzopichilli/flutter/bin/cache/artifacts/engine/ios" export "FLUTTER_FRAMEWORK_DIR=/Users/lorenzopichilli/flutter/bin/cache/artifacts/engine/ios"
export "FLUTTER_BUILD_NAME=1.0.0" export "FLUTTER_BUILD_NAME=1.0.0"
export "FLUTTER_BUILD_NUMBER=1" export "FLUTTER_BUILD_NUMBER=1"
export "TRACK_WIDGET_CREATION=true"
# Uncomment this line to define a global platform for your project # Uncomment this line to define a global platform for your project
platform :ios, '8.0' # platform :ios, '9.0'
# CocoaPods analytics sends network stats synchronously affecting flutter build latency. # CocoaPods analytics sends network stats synchronously affecting flutter build latency.
ENV['COCOAPODS_DISABLE_STATS'] = 'true' ENV['COCOAPODS_DISABLE_STATS'] = 'true'
project 'Runner', {
'Debug' => :debug,
'Profile' => :release,
'Release' => :release,
}
def parse_KV_file(file, separator='=') def parse_KV_file(file, separator='=')
file_abs_path = File.expand_path(file) file_abs_path = File.expand_path(file)
if !File.exists? file_abs_path if !File.exists? file_abs_path
return []; return [];
end end
pods_ary = [] generated_key_values = {}
skip_line_start_symbols = ["#", "/"] skip_line_start_symbols = ["#", "/"]
File.foreach(file_abs_path) { |line| File.foreach(file_abs_path) do |line|
next if skip_line_start_symbols.any? { |symbol| line =~ /^\s*#{symbol}/ } next if skip_line_start_symbols.any? { |symbol| line =~ /^\s*#{symbol}/ }
plugin = line.split(pattern=separator) plugin = line.split(pattern=separator)
if plugin.length == 2 if plugin.length == 2
podname = plugin[0].strip() podname = plugin[0].strip()
path = plugin[1].strip() path = plugin[1].strip()
podpath = File.expand_path("#{path}", file_abs_path) podpath = File.expand_path("#{path}", file_abs_path)
pods_ary.push({:name => podname, :path => podpath}); generated_key_values[podname] = podpath
else else
puts "Invalid plugin specification: #{line}" puts "Invalid plugin specification: #{line}"
end end
} end
return pods_ary generated_key_values
end end
target 'Runner' do target 'Runner' do
use_frameworks! use_frameworks!
use_modular_headers!
# Prepare symlinks folder. We use symlinks to avoid having Podfile.lock # Flutter Pod
# referring to absolute paths on developers' machines.
system('rm -rf .symlinks')
system('mkdir -p .symlinks/plugins')
# Flutter Pods copied_flutter_dir = File.join(__dir__, 'Flutter')
generated_xcode_build_settings = parse_KV_file('./Flutter/Generated.xcconfig') copied_framework_path = File.join(copied_flutter_dir, 'Flutter.framework')
if generated_xcode_build_settings.empty? copied_podspec_path = File.join(copied_flutter_dir, 'Flutter.podspec')
puts "Generated.xcconfig must exist. If you're running pod install manually, make sure flutter packages get is executed first." unless File.exist?(copied_framework_path) && File.exist?(copied_podspec_path)
end # Copy Flutter.framework and Flutter.podspec to Flutter/ to have something to link against if the xcode backend script has not run yet.
generated_xcode_build_settings.map { |p| # That script will copy the correct debug/profile/release version of the framework based on the currently selected Xcode configuration.
if p[:name] == 'FLUTTER_FRAMEWORK_DIR' # CocoaPods will not embed the framework on pod install (before any build phases can generate) if the dylib does not exist.
symlink = File.join('.symlinks', 'flutter')
File.symlink(File.dirname(p[:path]), symlink) generated_xcode_build_settings_path = File.join(copied_flutter_dir, 'Generated.xcconfig')
pod 'Flutter', :path => File.join(symlink, File.basename(p[:path])) unless File.exist?(generated_xcode_build_settings_path)
raise "Generated.xcconfig must exist. If you're running pod install manually, make sure flutter pub get is executed first"
end end
} generated_xcode_build_settings = parse_KV_file(generated_xcode_build_settings_path)
cached_framework_dir = generated_xcode_build_settings['FLUTTER_FRAMEWORK_DIR'];
unless File.exist?(copied_framework_path)
FileUtils.cp_r(File.join(cached_framework_dir, 'Flutter.framework'), copied_flutter_dir)
end
unless File.exist?(copied_podspec_path)
FileUtils.cp(File.join(cached_framework_dir, 'Flutter.podspec'), copied_flutter_dir)
end
end
# Keep pod path relative so it can be checked into Podfile.lock.
pod 'Flutter', :path => 'Flutter'
# Plugin Pods # Plugin Pods
plugin_pods = parse_KV_file('../.flutter-plugins')
plugin_pods.map { |p|
symlink = File.join('.symlinks', 'plugins', p[:name])
File.symlink(p[:path], symlink)
pod p[:name], :path => File.join(symlink, 'ios')
}
# Prepare symlinks folder. We use symlinks to avoid having Podfile.lock
# referring to absolute paths on developers' machines.
system('rm -rf .symlinks')
system('mkdir -p .symlinks/plugins')
plugin_pods = parse_KV_file('../.flutter-plugins')
plugin_pods.each do |name, path|
symlink = File.join('.symlinks', 'plugins', name)
File.symlink(path, symlink)
pod name, :path => File.join(symlink, 'ios')
end
end end
post_install do |installer| post_install do |installer|
installer.pods_project.targets.each do |target| installer.pods_project.targets.each do |target|
target.build_configurations.each do |config| target.build_configurations.each do |config|
config.build_settings['ENABLE_BITCODE'] = 'NO' config.build_settings['ENABLE_BITCODE'] = 'NO'
config.build_settings['SWIFT_VERSION'] = '5.0'
end end
end end
end end
...@@ -10,12 +10,8 @@ ...@@ -10,12 +10,8 @@
1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; };
25A517508F43E58C47090625 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E8D91E403808A7540F18B75D /* Pods_Runner.framework */; }; 25A517508F43E58C47090625 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E8D91E403808A7540F18B75D /* Pods_Runner.framework */; };
3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; };
3B80C3941E831B6300D905FE /* App.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3B80C3931E831B6300D905FE /* App.framework */; };
3B80C3951E831B6300D905FE /* App.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 3B80C3931E831B6300D905FE /* App.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
61FF730023634CA10069C557 /* libsqlite3.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 61FF72FF23634CA10069C557 /* libsqlite3.tbd */; }; 61FF730023634CA10069C557 /* libsqlite3.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 61FF72FF23634CA10069C557 /* libsqlite3.tbd */; };
74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; }; 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; };
9705A1C61CF904A100538489 /* Flutter.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9740EEBA1CF902C7004384FC /* Flutter.framework */; };
9705A1C71CF904A300538489 /* Flutter.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 9740EEBA1CF902C7004384FC /* Flutter.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; };
97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; };
EDC1147F21735BC200D2247A /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; EDC1147F21735BC200D2247A /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; };
...@@ -28,8 +24,6 @@ ...@@ -28,8 +24,6 @@
dstPath = ""; dstPath = "";
dstSubfolderSpec = 10; dstSubfolderSpec = 10;
files = ( files = (
3B80C3951E831B6300D905FE /* App.framework in Embed Frameworks */,
9705A1C71CF904A300538489 /* Flutter.framework in Embed Frameworks */,
); );
name = "Embed Frameworks"; name = "Embed Frameworks";
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
...@@ -40,7 +34,6 @@ ...@@ -40,7 +34,6 @@
1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = "<group>"; }; 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = "<group>"; };
1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = "<group>"; }; 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = "<group>"; };
3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = "<group>"; }; 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = "<group>"; };
3B80C3931E831B6300D905FE /* App.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = App.framework; path = Flutter/App.framework; sourceTree = "<group>"; };
61FF72FF23634CA10069C557 /* libsqlite3.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libsqlite3.tbd; path = usr/lib/libsqlite3.tbd; sourceTree = SDKROOT; }; 61FF72FF23634CA10069C557 /* libsqlite3.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libsqlite3.tbd; path = usr/lib/libsqlite3.tbd; sourceTree = SDKROOT; };
61FF730123634DD10069C557 /* flutter_downloader.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = flutter_downloader.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 61FF730123634DD10069C557 /* flutter_downloader.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = flutter_downloader.framework; sourceTree = BUILT_PRODUCTS_DIR; };
74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = "<group>"; }; 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = "<group>"; };
...@@ -48,7 +41,6 @@ ...@@ -48,7 +41,6 @@
7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = "<group>"; }; 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = "<group>"; };
9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = "<group>"; }; 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = "<group>"; };
9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = "<group>"; }; 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = "<group>"; };
9740EEBA1CF902C7004384FC /* Flutter.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Flutter.framework; path = Flutter/Flutter.framework; sourceTree = "<group>"; };
97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; };
97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; }; 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; };
97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; }; 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
...@@ -65,8 +57,6 @@ ...@@ -65,8 +57,6 @@
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
61FF730023634CA10069C557 /* libsqlite3.tbd in Frameworks */, 61FF730023634CA10069C557 /* libsqlite3.tbd in Frameworks */,
9705A1C61CF904A100538489 /* Flutter.framework in Frameworks */,
3B80C3941E831B6300D905FE /* App.framework in Frameworks */,
25A517508F43E58C47090625 /* Pods_Runner.framework in Frameworks */, 25A517508F43E58C47090625 /* Pods_Runner.framework in Frameworks */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
...@@ -96,9 +86,7 @@ ...@@ -96,9 +86,7 @@
9740EEB11CF90186004384FC /* Flutter */ = { 9740EEB11CF90186004384FC /* Flutter */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
3B80C3931E831B6300D905FE /* App.framework */,
3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */, 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */,
9740EEBA1CF902C7004384FC /* Flutter.framework */,
9740EEB21CF90195004384FC /* Debug.xcconfig */, 9740EEB21CF90195004384FC /* Debug.xcconfig */,
7AFA3C8E1D35360C0083082E /* Release.xcconfig */, 7AFA3C8E1D35360C0083082E /* Release.xcconfig */,
9740EEB31CF90195004384FC /* Generated.xcconfig */, 9740EEB31CF90195004384FC /* Generated.xcconfig */,
...@@ -239,7 +227,7 @@ ...@@ -239,7 +227,7 @@
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh; shellPath = /bin/sh;
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" thin"; shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin";
}; };
3F62E6580DEAFBE72C090A85 /* [CP] Check Pods Manifest.lock */ = { 3F62E6580DEAFBE72C090A85 /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase; isa = PBXShellScriptBuildPhase;
...@@ -266,7 +254,7 @@ ...@@ -266,7 +254,7 @@
); );
inputPaths = ( inputPaths = (
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh", "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh",
"${PODS_ROOT}/../.symlinks/flutter/ios/Flutter.framework", "${PODS_ROOT}/../Flutter/Flutter.framework",
"${BUILT_PRODUCTS_DIR}/Reachability/Reachability.framework", "${BUILT_PRODUCTS_DIR}/Reachability/Reachability.framework",
"${BUILT_PRODUCTS_DIR}/connectivity/connectivity.framework", "${BUILT_PRODUCTS_DIR}/connectivity/connectivity.framework",
"${BUILT_PRODUCTS_DIR}/flutter_downloader/flutter_downloader.framework", "${BUILT_PRODUCTS_DIR}/flutter_downloader/flutter_downloader.framework",
......
File mode changed from 100644 to 100755
File mode changed from 100644 to 100755
...@@ -50,12 +50,12 @@ class _InAppWebViewExampleScreenState extends State<InAppWebViewExampleScreen> { ...@@ -50,12 +50,12 @@ class _InAppWebViewExampleScreenState extends State<InAppWebViewExampleScreen> {
BoxDecoration(border: Border.all(color: Colors.blueAccent)), BoxDecoration(border: Border.all(color: Colors.blueAccent)),
child: InAppWebView( child: InAppWebView(
initialUrl: "https://flutter.dev/", initialUrl: "https://flutter.dev/",
//initialFile: "assets/index.html", // initialFile: "assets/index.html",
initialHeaders: {}, initialHeaders: {},
initialOptions: InAppWebViewWidgetOptions( initialOptions: InAppWebViewWidgetOptions(
crossPlatform: InAppWebViewOptions( crossPlatform: InAppWebViewOptions(
debuggingEnabled: true, debuggingEnabled: true,
) ),
), ),
onWebViewCreated: (InAppWebViewController controller) { onWebViewCreated: (InAppWebViewController controller) {
webView = controller; webView = controller;
......
...@@ -10,6 +10,7 @@ import 'package:flutter_inappwebview_example/in_app_browser_example.screen.dart' ...@@ -10,6 +10,7 @@ import 'package:flutter_inappwebview_example/in_app_browser_example.screen.dart'
// InAppLocalhostServer localhostServer = new InAppLocalhostServer(); // InAppLocalhostServer localhostServer = new InAppLocalhostServer();
Future main() async { Future main() async {
WidgetsFlutterBinding.ensureInitialized();
// await localhostServer.start(); // await localhostServer.start();
runApp(new MyApp()); runApp(new MyApp());
} }
......
File mode changed from 100644 to 100755
File mode changed from 100644 to 100755
File mode changed from 100644 to 100755
File mode changed from 100644 to 100755
File mode changed from 100644 to 100755
File mode changed from 100644 to 100755
File mode changed from 100644 to 100755
File mode changed from 100644 to 100755
File mode changed from 100644 to 100755
File mode changed from 100644 to 100755
File mode changed from 100644 to 100755
File mode changed from 100644 to 100755
File mode changed from 100644 to 100755
File mode changed from 100644 to 100755
File mode changed from 100644 to 100755
File mode changed from 100644 to 100755
...@@ -8,7 +8,7 @@ import '.env.dart'; ...@@ -8,7 +8,7 @@ import '.env.dart';
void main() { void main() {
group('Flutter InAppBrowser', () { group('Flutter InAppWebView', () {
FlutterDriver driver; FlutterDriver driver;
// Connect to the Flutter driver before running any tests. // Connect to the Flutter driver before running any tests.
......
File mode changed from 100644 to 100755
File mode changed from 100644 to 100755
File mode changed from 100644 to 100755
File mode changed from 100644 to 100755
File mode changed from 100644 to 100755
File mode changed from 100644 to 100755
File mode changed from 100644 to 100755
File mode changed from 100644 to 100755
File mode changed from 100644 to 100755
File mode changed from 100644 to 100755
File mode changed from 100644 to 100755
File mode changed from 100644 to 100755
File mode changed from 100644 to 100755
File mode changed from 100644 to 100755
File mode changed from 100644 to 100755
File mode changed from 100644 to 100755
...@@ -53,7 +53,7 @@ setTimeout(function() { ...@@ -53,7 +53,7 @@ setTimeout(function() {
}, 100); }, 100);
"""); """);
}, },
onNavigationStateChange: (InAppWebViewController controller, String url) async { onUpdateVisitedHistory: (InAppWebViewController controller, String url, bool androidIsReload) async {
if (url.endsWith("second-push")) { if (url.endsWith("second-push")) {
setState(() { setState(() {
appBarTitle += " " + url; appBarTitle += " " + url;
......
File mode changed from 100644 to 100755
File mode changed from 100644 to 100755
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'dart:io' show Platform;
import 'package:flutter_inappwebview/flutter_inappwebview.dart'; import 'package:flutter_inappwebview/flutter_inappwebview.dart';
import 'custom_widget_test.dart'; import 'custom_widget_test.dart';
...@@ -51,8 +51,11 @@ class InAppWebViewShouldOverrideUrlLoadingTestState extends WidgetTestState { ...@@ -51,8 +51,11 @@ class InAppWebViewShouldOverrideUrlLoadingTestState extends WidgetTestState {
} }
}, },
shouldOverrideUrlLoading: (InAppWebViewController controller, ShouldOverrideUrlLoadingRequest shouldOverrideUrlLoadingRequest) async { shouldOverrideUrlLoading: (InAppWebViewController controller, ShouldOverrideUrlLoadingRequest shouldOverrideUrlLoadingRequest) async {
controller.loadUrl(url: "https://flutter.dev/"); if (Platform.isAndroid || shouldOverrideUrlLoadingRequest.iosWKNavigationType == IOSWKNavigationType.LINK_ACTIVATED) {
return ShouldOverrideUrlLoadingAction.CANCEL; await controller.loadUrl(url: "https://flutter.dev/");
return ShouldOverrideUrlLoadingAction.CANCEL;
}
return ShouldOverrideUrlLoadingAction.ALLOW;
}, },
), ),
), ),
......
File mode changed from 100644 to 100755
...@@ -121,6 +121,7 @@ Drawer myDrawer({@required context}) { ...@@ -121,6 +121,7 @@ Drawer myDrawer({@required context}) {
} }
Future main() async { Future main() async {
WidgetsFlutterBinding.ensureInitialized();
runApp(new MyApp()); runApp(new MyApp());
} }
......
...@@ -26,7 +26,6 @@ ...@@ -26,7 +26,6 @@
<excludeFolder url="file://$MODULE_DIR$/example/ios/.symlinks/plugins/flutter_inappwebview/example/.dart_tool" /> <excludeFolder url="file://$MODULE_DIR$/example/ios/.symlinks/plugins/flutter_inappwebview/example/.dart_tool" />
<excludeFolder url="file://$MODULE_DIR$/example/ios/.symlinks/plugins/flutter_inappwebview/example/.pub" /> <excludeFolder url="file://$MODULE_DIR$/example/ios/.symlinks/plugins/flutter_inappwebview/example/.pub" />
<excludeFolder url="file://$MODULE_DIR$/example/ios/.symlinks/plugins/flutter_inappwebview/example/build" /> <excludeFolder url="file://$MODULE_DIR$/example/ios/.symlinks/plugins/flutter_inappwebview/example/build" />
<excludeFolder url="file://$MODULE_DIR$/example/ios/.symlinks/plugins/flutter_inappwebview/example/ios/Flutter/App.framework/flutter_assets/packages" />
<excludeFolder url="file://$MODULE_DIR$/example/ios/Flutter/App.framework/flutter_assets/packages" /> <excludeFolder url="file://$MODULE_DIR$/example/ios/Flutter/App.framework/flutter_assets/packages" />
<excludeFolder url="file://$MODULE_DIR$/flutter_inappbrowser_tests/.dart_tool" /> <excludeFolder url="file://$MODULE_DIR$/flutter_inappbrowser_tests/.dart_tool" />
<excludeFolder url="file://$MODULE_DIR$/flutter_inappbrowser_tests/.pub" /> <excludeFolder url="file://$MODULE_DIR$/flutter_inappbrowser_tests/.pub" />
......
File mode changed from 100644 to 100755
File mode changed from 100644 to 100755
File mode changed from 100644 to 100755
File mode changed from 100644 to 100755
File mode changed from 100644 to 100755
...@@ -40,6 +40,8 @@ public class FlutterWebViewController: FlutterMethodCallDelegate, FlutterPlatfor ...@@ -40,6 +40,8 @@ public class FlutterWebViewController: FlutterMethodCallDelegate, FlutterPlatfor
webView = InAppWebView(frame: myView!.bounds, configuration: preWebviewConfiguration, IABController: nil, channel: channel!) webView = InAppWebView(frame: myView!.bounds, configuration: preWebviewConfiguration, IABController: nil, channel: channel!)
webView!.autoresizingMask = [.flexibleWidth, .flexibleHeight] webView!.autoresizingMask = [.flexibleWidth, .flexibleHeight]
myView!.autoresizesSubviews = true
myView!.autoresizingMask = [.flexibleWidth, .flexibleHeight]
myView!.addSubview(webView!) myView!.addSubview(webView!)
webView!.options = options webView!.options = options
...@@ -104,8 +106,8 @@ public class FlutterWebViewController: FlutterMethodCallDelegate, FlutterPlatfor ...@@ -104,8 +106,8 @@ public class FlutterWebViewController: FlutterMethodCallDelegate, FlutterPlatfor
let baseUrl = initialData!["baseUrl"]! let baseUrl = initialData!["baseUrl"]!
webView!.loadData(data: data, mimeType: mimeType, encoding: encoding, baseUrl: baseUrl) webView!.loadData(data: data, mimeType: mimeType, encoding: encoding, baseUrl: baseUrl)
} }
else { else if let url = URL(string: initialUrl!) {
webView!.loadUrl(url: URL(string: initialUrl!)!, headers: initialHeaders) webView!.loadUrl(url: url, headers: initialHeaders)
} }
} }
...@@ -374,6 +376,9 @@ public class FlutterWebViewController: FlutterMethodCallDelegate, FlutterPlatfor ...@@ -374,6 +376,9 @@ public class FlutterWebViewController: FlutterMethodCallDelegate, FlutterPlatfor
case "getScale": case "getScale":
result( (webView != nil) ? webView!.getScale() : nil ) result( (webView != nil) ? webView!.getScale() : nil )
break break
case "hasOnlySecureContent":
result( (webView != nil) ? webView!.hasOnlySecureContent : nil )
break
default: default:
result(FlutterMethodNotImplemented) result(FlutterMethodNotImplemented)
break break
......
File mode changed from 100644 to 100755
...@@ -278,6 +278,9 @@ public class InAppBrowserWebViewController: UIViewController, FlutterPlugin, UIS ...@@ -278,6 +278,9 @@ public class InAppBrowserWebViewController: UIViewController, FlutterPlugin, UIS
case "getScale": case "getScale":
result(webView.getScale()) result(webView.getScale())
break break
case "hasOnlySecureContent":
result(webView.hasOnlySecureContent)
break
default: default:
result(FlutterMethodNotImplemented) result(FlutterMethodNotImplemented)
break break
......
...@@ -41,220 +41,11 @@ func JSONStringify(value: Any, prettyPrinted: Bool = false) -> String { ...@@ -41,220 +41,11 @@ func JSONStringify(value: Any, prettyPrinted: Bool = false) -> String {
let JAVASCRIPT_BRIDGE_NAME = "flutter_inappwebview" let JAVASCRIPT_BRIDGE_NAME = "flutter_inappwebview"
// https://github.com/taylorhakes/promise-polyfill/blob/master/src/index.js // https://github.com/tildeio/rsvp.js
let promisePolyfillJS = """ let promisePolyfillJS = """
if (window.Promise == null) { if (window.Promise == null) {
var setTimeoutFunc = setTimeout; !function(t,e){"object"==typeof exports&&"undefined"!=typeof module?e(exports):"function"==typeof define&&define.amd?define(["exports"],e):e(t.RSVP={})}(this,function(t){"use strict";function e(t){var e=t._promiseCallbacks;return e||(e=t._promiseCallbacks={}),e}var r={mixin:function(t){return t.on=this.on,t.off=this.off,t.trigger=this.trigger,t._promiseCallbacks=void 0,t},on:function(t,r){if("function"!=typeof r)throw new TypeError("Callback must be a function");var n=e(this),o=n[t];o||(o=n[t]=[]),-1===o.indexOf(r)&&o.push(r)},off:function(t,r){var n=e(this);if(r){var o=n[t],i=o.indexOf(r);-1!==i&&o.splice(i,1)}else n[t]=[]},trigger:function(t,r,n){var o=e(this)[t];if(o)for(var i=0;i<o.length;i++)(0,o[i])(r,n)}},n={instrument:!1};function o(t,e){if(2!==arguments.length)return n[t];n[t]=e}r.mixin(n);var i=[];function s(t,e,r){1===i.push({name:t,payload:{key:e._guidKey,id:e._id,eventName:t,detail:e._result,childId:r&&r._id,label:e._label,timeStamp:Date.now(),error:n["instrument-with-stack"]?new Error(e._label):null}})&&setTimeout(function(){for(var t=0;t<i.length;t++){var e=i[t],r=e.payload;r.guid=r.key+r.id,r.childGuid=r.key+r.childId,r.error&&(r.stack=r.error.stack),n.trigger(e.name,e.payload)}i.length=0},50)}function u(t,e){if(t&&"object"==typeof t&&t.constructor===this)return t;var r=new this(c,e);return m(r,t),r}function c(){}var a=void 0,f=1,l=2,h={error:null};function p(t){try{return t.then}catch(t){return h.error=t,h}}var y=void 0;function _(){try{var t=y;return y=null,t.apply(this,arguments)}catch(t){return h.error=t,h}}function v(t){return y=t,_}function d(t,e,r){if(e.constructor===t.constructor&&r===A&&t.constructor.resolve===u)!function(t,e){e._state===f?b(t,e._result):e._state===l?(e._onError=null,g(t,e._result)):j(e,void 0,function(r){e===r?b(t,r):m(t,r)},function(e){return g(t,e)})}(t,e);else if(r===h){var o=h.error;h.error=null,g(t,o)}else"function"==typeof r?function(t,e,r){n.async(function(t){var n=!1,o=v(r).call(e,function(r){n||(n=!0,e===r?b(t,r):m(t,r))},function(e){n||(n=!0,g(t,e))},"Settle: "+(t._label||" unknown promise"));if(!n&&o===h){n=!0;var i=h.error;h.error=null,g(t,i)}},t)}(t,e,r):b(t,e)}function m(t,e){var r,n;t===e?b(t,e):(n=typeof(r=e),null===r||"object"!==n&&"function"!==n?b(t,e):d(t,e,p(e)))}function w(t){t._onError&&t._onError(t._result),O(t)}function b(t,e){t._state===a&&(t._result=e,t._state=f,0===t._subscribers.length?n.instrument&&s("fulfilled",t):n.async(O,t))}function g(t,e){t._state===a&&(t._state=l,t._result=e,n.async(w,t))}function j(t,e,r,o){var i=t._subscribers,s=i.length;t._onError=null,i[s]=e,i[s+f]=r,i[s+l]=o,0===s&&t._state&&n.async(O,t)}function O(t){var e=t._subscribers,r=t._state;if(n.instrument&&s(r===f?"fulfilled":"rejected",t),0!==e.length){for(var o=void 0,i=void 0,u=t._result,c=0;c<e.length;c+=3)o=e[c],i=e[c+r],o?E(r,o,i,u):i(u);t._subscribers.length=0}}function E(t,e,r,n){var o="function"==typeof r,i=void 0;if(i=o?v(r)(n):n,e._state!==a);else if(i===e)g(e,new TypeError("A promises callback cannot return that same promise."));else if(i===h){var s=h.error;h.error=null,g(e,s)}else o?m(e,i):t===f?b(e,i):t===l&&g(e,i)}function A(t,e,r){var o=this._state;if(o===f&&!t||o===l&&!e)return n.instrument&&s("chained",this,this),this;this._onError=null;var i=new this.constructor(c,r),u=this._result;if(n.instrument&&s("chained",this,i),o===a)j(this,i,t,e);else{var h=o===f?t:e;n.async(function(){return E(o,i,h,u)})}return i}var T=function(){function t(t,e,r,n){this._instanceConstructor=t,this.promise=new t(c,n),this._abortOnReject=r,this._isUsingOwnPromise=t===k,this._isUsingOwnResolve=t.resolve===u,this._init.apply(this,arguments)}return t.prototype._init=function(t,e){var r=e.length||0;this.length=r,this._remaining=r,this._result=new Array(r),this._enumerate(e)},t.prototype._enumerate=function(t){for(var e=this.length,r=this.promise,n=0;r._state===a&&n<e;n++)this._eachEntry(t[n],n,!0);this._checkFullfillment()},t.prototype._checkFullfillment=function(){if(0===this._remaining){var t=this._result;b(this.promise,t),this._result=null}},t.prototype._settleMaybeThenable=function(t,e,r){var n=this._instanceConstructor;if(this._isUsingOwnResolve){var o=p(t);if(o===A&&t._state!==a)t._onError=null,this._settledAt(t._state,e,t._result,r);else if("function"!=typeof o)this._settledAt(f,e,t,r);else if(this._isUsingOwnPromise){var i=new n(c);d(i,t,o),this._willSettleAt(i,e,r)}else this._willSettleAt(new n(function(e){return e(t)}),e,r)}else this._willSettleAt(n.resolve(t),e,r)},t.prototype._eachEntry=function(t,e,r){null!==t&&"object"==typeof t?this._settleMaybeThenable(t,e,r):this._setResultAt(f,e,t,r)},t.prototype._settledAt=function(t,e,r,n){var o=this.promise;o._state===a&&(this._abortOnReject&&t===l?g(o,r):(this._setResultAt(t,e,r,n),this._checkFullfillment()))},t.prototype._setResultAt=function(t,e,r,n){this._remaining--,this._result[e]=r},t.prototype._willSettleAt=function(t,e,r){var n=this;j(t,void 0,function(t){return n._settledAt(f,e,t,r)},function(t){return n._settledAt(l,e,t,r)})},t}();function P(t,e,r){this._remaining--,this._result[e]=t===f?{state:"fulfilled",value:r}:{state:"rejected",reason:r}}var S="rsvp_"+Date.now()+"-",R=0;var k=function(){function t(e,r){this._id=R++,this._label=r,this._state=void 0,this._result=void 0,this._subscribers=[],n.instrument&&s("created",this),c!==e&&("function"!=typeof e&&function(){throw new TypeError("You must pass a resolver function as the first argument to the promise constructor")}(),this instanceof t?function(t,e){var r=!1;try{e(function(e){r||(r=!0,m(t,e))},function(e){r||(r=!0,g(t,e))})}catch(e){g(t,e)}}(this,e):function(){throw new TypeError("Failed to construct 'Promise': Please use the 'new' operator, this object constructor cannot be called as a function.")}())}return t.prototype._onError=function(t){var e=this;n.after(function(){e._onError&&n.trigger("error",t,e._label)})},t.prototype.catch=function(t,e){return this.then(void 0,t,e)},t.prototype.finally=function(t,e){var r=this.constructor;return"function"==typeof t?this.then(function(e){return r.resolve(t()).then(function(){return e})},function(e){return r.resolve(t()).then(function(){throw e})}):this.then(t,t)},t}();function x(t,e){return{then:function(r,n){return t.call(e,r,n)}}}function M(t,e){var r=function(){for(var r=arguments.length,n=new Array(r+1),o=!1,i=0;i<r;++i){var s=arguments[i];if(!o){if((o=F(s))===h){var u=h.error;h.error=null;var a=new k(c);return g(a,u),a}o&&!0!==o&&(s=x(o,s))}n[i]=s}var f=new k(c);return n[r]=function(t,r){t?g(f,t):void 0===e?m(f,r):!0===e?m(f,function(t){for(var e=t.length,r=new Array(e-1),n=1;n<e;n++)r[n-1]=t[n];return r}(arguments)):Array.isArray(e)?m(f,function(t,e){for(var r={},n=t.length,o=new Array(n),i=0;i<n;i++)o[i]=t[i];for(var s=0;s<e.length;s++)r[e[s]]=o[s+1];return r}(arguments,e)):m(f,r)},o?function(t,e,r,n){return k.all(e).then(function(e){return C(t,e,r,n)})}(f,n,t,this):C(f,n,t,this)};return r.__proto__=t,r}function C(t,e,r,n){if(v(r).apply(n,e)===h){var o=h.error;h.error=null,g(t,o)}return t}function F(t){return null!==t&&"object"==typeof t&&(t.constructor===k||p(t))}function I(t,e){return k.all(t,e)}k.cast=u,k.all=function(t,e){return Array.isArray(t)?new T(this,t,!0,e).promise:this.reject(new TypeError("Promise.all must be called with an array"),e)},k.race=function(t,e){var r=new this(c,e);if(!Array.isArray(t))return g(r,new TypeError("Promise.race must be called with an array")),r;for(var n=0;r._state===a&&n<t.length;n++)j(this.resolve(t[n]),void 0,function(t){return m(r,t)},function(t){return g(r,t)});return r},k.resolve=u,k.reject=function(t,e){var r=new this(c,e);return g(r,t),r},k.prototype._guidKey=S,k.prototype.then=A;var N=function(t){function e(e,r,n){return function(t,e){if(!t)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!e||"object"!=typeof e&&"function"!=typeof e?t:e}(this,t.call(this,e,r,!1,n))}return function(t,e){if("function"!=typeof e&&null!==e)throw new TypeError("Super expression must either be null or a function, not "+typeof e);t.prototype=Object.create(e&&e.prototype,{constructor:{value:t,enumerable:!1,writable:!0,configurable:!0}}),e&&(Object.setPrototypeOf?Object.setPrototypeOf(t,e):t.__proto__=e)}(e,t),e}(T);function U(t,e){return Array.isArray(t)?new N(k,t,e).promise:k.reject(new TypeError("Promise.allSettled must be called with an array"),e)}function D(t,e){return k.race(t,e)}N.prototype._setResultAt=P;var K=function(t){function e(e,r){var n=!(arguments.length>2&&void 0!==arguments[2])||arguments[2],o=arguments[3];return function(t,e){if(!t)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!e||"object"!=typeof e&&"function"!=typeof e?t:e}(this,t.call(this,e,r,n,o))}return function(t,e){if("function"!=typeof e&&null!==e)throw new TypeError("Super expression must either be null or a function, not "+typeof e);t.prototype=Object.create(e&&e.prototype,{constructor:{value:t,enumerable:!1,writable:!0,configurable:!0}}),e&&(Object.setPrototypeOf?Object.setPrototypeOf(t,e):t.__proto__=e)}(e,t),e.prototype._init=function(t,e){this._result={},this._enumerate(e)},e.prototype._enumerate=function(t){var e=Object.keys(t),r=e.length,n=this.promise;this._remaining=r;for(var o=void 0,i=void 0,s=0;n._state===a&&s<r;s++)i=t[o=e[s]],this._eachEntry(i,o,!0);this._checkFullfillment()},e}(T);function q(t,e){return k.resolve(t,e).then(function(t){if(null===t||"object"!=typeof t)throw new TypeError("Promise.hash must be called with an object");return new K(k,t,e).promise})}var G=function(t){function e(e,r,n){return function(t,e){if(!t)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!e||"object"!=typeof e&&"function"!=typeof e?t:e}(this,t.call(this,e,r,!1,n))}return function(t,e){if("function"!=typeof e&&null!==e)throw new TypeError("Super expression must either be null or a function, not "+typeof e);t.prototype=Object.create(e&&e.prototype,{constructor:{value:t,enumerable:!1,writable:!0,configurable:!0}}),e&&(Object.setPrototypeOf?Object.setPrototypeOf(t,e):t.__proto__=e)}(e,t),e}(K);function L(t,e){return k.resolve(t,e).then(function(t){if(null===t||"object"!=typeof t)throw new TypeError("hashSettled must be called with an object");return new G(k,t,!1,e).promise})}function V(t){throw setTimeout(function(){throw t}),t}function W(t){var e={resolve:void 0,reject:void 0};return e.promise=new k(function(t,r){e.resolve=t,e.reject=r},t),e}G.prototype._setResultAt=P;var Y=function(t){function e(e,r,n,o){return function(t,e){if(!t)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!e||"object"!=typeof e&&"function"!=typeof e?t:e}(this,t.call(this,e,r,!0,o,n))}return function(t,e){if("function"!=typeof e&&null!==e)throw new TypeError("Super expression must either be null or a function, not "+typeof e);t.prototype=Object.create(e&&e.prototype,{constructor:{value:t,enumerable:!1,writable:!0,configurable:!0}}),e&&(Object.setPrototypeOf?Object.setPrototypeOf(t,e):t.__proto__=e)}(e,t),e.prototype._init=function(t,e,r,n,o){var i=e.length||0;this.length=i,this._remaining=i,this._result=new Array(i),this._mapFn=o,this._enumerate(e)},e.prototype._setResultAt=function(t,e,r,n){if(n){var o=v(this._mapFn)(r,e);o===h?this._settledAt(l,e,o.error,!1):this._eachEntry(o,e,!1)}else this._remaining--,this._result[e]=r},e}(T);function $(t,e,r){return"function"!=typeof e?k.reject(new TypeError("map expects a function as a second argument"),r):k.resolve(t,r).then(function(t){if(!Array.isArray(t))throw new TypeError("map must be called with an array");return new Y(k,t,e,r).promise})}function z(t,e){return k.resolve(t,e)}function B(t,e){return k.reject(t,e)}var H={},J=function(t){function e(){return function(t,e){if(!t)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!e||"object"!=typeof e&&"function"!=typeof e?t:e}(this,t.apply(this,arguments))}return function(t,e){if("function"!=typeof e&&null!==e)throw new TypeError("Super expression must either be null or a function, not "+typeof e);t.prototype=Object.create(e&&e.prototype,{constructor:{value:t,enumerable:!1,writable:!0,configurable:!0}}),e&&(Object.setPrototypeOf?Object.setPrototypeOf(t,e):t.__proto__=e)}(e,t),e.prototype._checkFullfillment=function(){if(0===this._remaining&&null!==this._result){var t=this._result.filter(function(t){return t!==H});b(this.promise,t),this._result=null}},e.prototype._setResultAt=function(t,e,r,n){if(n){this._result[e]=r;var o=v(this._mapFn)(r,e);o===h?this._settledAt(l,e,o.error,!1):this._eachEntry(o,e,!1)}else this._remaining--,r||(this._result[e]=H)},e}(Y);function Q(t,e,r){return"function"!=typeof e?k.reject(new TypeError("filter expects function as a second argument"),r):k.resolve(t,r).then(function(t){if(!Array.isArray(t))throw new TypeError("filter must be called with an array");return new J(k,t,e,r).promise})}var X=0,Z=void 0;function tt(t,e){ut[X]=t,ut[X+1]=e,2===(X+=2)&&_t()}var et="undefined"!=typeof window?window:void 0,rt=et||{},nt=rt.MutationObserver||rt.WebKitMutationObserver,ot="undefined"==typeof self&&"undefined"!=typeof process&&"[object process]"==={}.toString.call(process),it="undefined"!=typeof Uint8ClampedArray&&"undefined"!=typeof importScripts&&"undefined"!=typeof MessageChannel;function st(){return function(){return setTimeout(ct,1)}}var ut=new Array(1e3);function ct(){for(var t=0;t<X;t+=2){(0,ut[t])(ut[t+1]),ut[t]=void 0,ut[t+1]=void 0}X=0}var at,ft,lt,ht,pt,yt,_t=void 0;ot?(pt=process.nextTick,yt=process.versions.node.match(/^(?:(\\d+)\\.)?(?:(\\d+)\\.)?(\\*|\\d+)$/),Array.isArray(yt)&&"0"===yt[1]&&"10"===yt[2]&&(pt=setImmediate),_t=function(){return pt(ct)}):nt?(ft=0,lt=new nt(ct),ht=document.createTextNode(""),lt.observe(ht,{characterData:!0}),_t=function(){return ht.data=ft=++ft%2}):it?((at=new MessageChannel).port1.onmessage=ct,_t=function(){return at.port2.postMessage(0)}):_t=void 0===et&&"function"==typeof require?function(){try{var t=Function("return this")().require("vertx");return void 0!==(Z=t.runOnLoop||t.runOnContext)?function(){Z(ct)}:st()}catch(t){return st()}}():st(),n.async=tt,n.after=function(t){return setTimeout(t,0)};var vt=z,dt=function(t,e){return n.async(t,e)};function mt(){n.on.apply(n,arguments)}function wt(){n.off.apply(n,arguments)}if("undefined"!=typeof window&&"object"==typeof window.__PROMISE_INSTRUMENTATION__){var bt=window.__PROMISE_INSTRUMENTATION__;for(var gt in o("instrument",!0),bt)bt.hasOwnProperty(gt)&&mt(gt,bt[gt])}var jt={asap:tt,cast:vt,Promise:k,EventTarget:r,all:I,allSettled:U,race:D,hash:q,hashSettled:L,rethrow:V,defer:W,denodeify:M,configure:o,on:mt,off:wt,resolve:z,reject:B,map:$,async:dt,filter:Q};t.default=jt,t.asap=tt,t.cast=vt,t.Promise=k,t.EventTarget=r,t.all=I,t.allSettled=U,t.race=D,t.hash=q,t.hashSettled=L,t.rethrow=V,t.defer=W,t.denodeify=M,t.configure=o,t.on=mt,t.off=wt,t.resolve=z,t.reject=B,t.map=$,t.async=dt,t.filter=Q,Object.defineProperty(t,"__esModule",{value:!0})});
function isArray(x) { window.Promise = RSVP.Promise;
return Boolean(x && typeof x.length !== "undefined");
};
function noop() {}
function bind(fn, thisArg) {
return function() {
fn.apply(thisArg, arguments);
};
};
function Promise(fn) {
if (!(this instanceof Promise))
throw new TypeError("Promises must be constructed via new");
if (typeof fn !== "function") throw new TypeError("not a function");
this._state = 0;
this._handled = false;
this._value = undefined;
this._deferreds = [];
doResolve(fn, this);
};
function handle(self, deferred) {
while (self._state === 3) {
self = self._value;
}
if (self._state === 0) {
self._deferreds.push(deferred);
return;
}
self._handled = true;
Promise._immediateFn(function() {
var cb = self._state === 1 ? deferred.onFulfilled : deferred.onRejected;
if (cb === null) {
(self._state === 1 ? resolve : reject)(deferred.promise, self._value);
return;
}
var ret;
try {
ret = cb(self._value);
} catch (e) {
reject(deferred.promise, e);
return;
}
resolve(deferred.promise, ret);
});
};
function resolve(self, newValue) {
try {
if (newValue === self)
throw new TypeError("A promise cannot be resolved with itself.");
if (
newValue &&
(typeof newValue === "object" || typeof newValue === "function")
) {
var then = newValue.then;
if (newValue instanceof Promise) {
self._state = 3;
self._value = newValue;
finale(self);
return;
} else if (typeof then === "function") {
doResolve(bind(then, newValue), self);
return;
}
}
self._state = 1;
self._value = newValue;
finale(self);
} catch (e) {
reject(self, e);
}
};
function reject(self, newValue) {
self._state = 2;
self._value = newValue;
finale(self);
};
function finale(self) {
if (self._state === 2 && self._deferreds.length === 0) {
Promise._immediateFn(function() {
if (!self._handled) {
Promise._unhandledRejectionFn(self._value);
}
});
}
for (var i = 0, len = self._deferreds.length; i < len; i++) {
handle(self, self._deferreds[i]);
}
self._deferreds = null;
};
function Handler(onFulfilled, onRejected, promise) {
this.onFulfilled = typeof onFulfilled === "function" ? onFulfilled : null;
this.onRejected = typeof onRejected === "function" ? onRejected : null;
this.promise = promise;
};
function doResolve(fn, self) {
var done = false;
try {
fn(
function(value) {
if (done) return;
done = true;
resolve(self, value);
},
function(reason) {
if (done) return;
done = true;
reject(self, reason);
}
);
} catch (ex) {
if (done) return;
done = true;
reject(self, ex);
}
};
Promise.prototype["catch"] = function(onRejected) {
return this.then(null, onRejected);
};
Promise.prototype.then = function(onFulfilled, onRejected) {
var prom = new this.constructor(noop);
handle(this, new Handler(onFulfilled, onRejected, prom));
return prom;
};
Promise.prototype["finally"] = function finallyConstructor(callback) {
var constructor = this.constructor;
return this.then(
function(value) {
return constructor.resolve(callback()).then(function() {
return value;
});
},
function(reason) {
return constructor.resolve(callback()).then(function() {
return constructor.reject(reason);
});
}
);
};
Promise.all = function(arr) {
return new Promise(function(resolve, reject) {
if (!isArray(arr)) {
return reject(new TypeError("Promise.all accepts an array"));
}
var args = Array.prototype.slice.call(arr);
if (args.length === 0) return resolve([]);
var remaining = args.length;
function res(i, val) {
try {
if (val && (typeof val === "object" || typeof val === "function")) {
var then = val.then;
if (typeof then === "function") {
then.call(
val,
function(val) {
res(i, val);
},
reject
);
return;
}
}
args[i] = val;
if (--remaining === 0) {
resolve(args);
}
} catch (ex) {
reject(ex);
}
}
for (var i = 0; i < args.length; i++) {
res(i, args[i]);
}
});
};
Promise.resolve = function(value) {
if (value && typeof value === "object" && value.constructor === Promise) {
return value;
}
return new Promise(function(resolve) {
resolve(value);
});
};
Promise.reject = function(value) {
return new Promise(function(resolve, reject) {
reject(value);
});
};
Promise.race = function(arr) {
return new Promise(function(resolve, reject) {
if (!isArray(arr)) {
return reject(new TypeError("Promise.race accepts an array"));
}
for (var i = 0, len = arr.length; i < len; i++) {
Promise.resolve(arr[i]).then(resolve, reject);
}
});
};
Promise._immediateFn =
(typeof setImmediate === "function" &&
function(fn) {
setImmediate(fn);
}) ||
function(fn) {
setTimeoutFunc(fn, 0);
};
Promise._unhandledRejectionFn = function _unhandledRejectionFn(err) {
if (typeof console !== "undefined" && console) {
console.warn("Possible Unhandled Promise Rejection:", err);
}
};
} }
""" """
...@@ -293,6 +84,7 @@ let consoleLogJS = """ ...@@ -293,6 +84,7 @@ let consoleLogJS = """
} }
} }
window.webkit.messageHandlers[oldLog].postMessage(message); window.webkit.messageHandlers[oldLog].postMessage(message);
oldLogs[oldLog].apply(null, arguments);
} }
})(k); })(k);
} }
...@@ -873,7 +665,63 @@ let interceptFetchRequestsJS = """ ...@@ -873,7 +665,63 @@ let interceptFetchRequestsJS = """
})(window.fetch); })(window.fetch);
""" """
public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavigationDelegate, WKScriptMessageHandler { /**
https://developer.android.com/reference/android/webkit/WebView.HitTestResult
*/
let findElementsAtPointJS = """
window.\(JAVASCRIPT_BRIDGE_NAME)._findElementsAtPoint = function(x, y) {
var hitTestResultType = {
UNKNOWN_TYPE: 0,
PHONE_TYPE: 2,
GEO_TYPE: 3,
EMAIL_TYPE: 4,
IMAGE_TYPE: 5,
SRC_ANCHOR_TYPE: 7,
SRC_IMAGE_ANCHOR_TYPE: 8,
EDIT_TEXT_TYPE: 9
};
var element = document.elementFromPoint(x, y);
console.log(element);
var data = {
type: 0,
extra: null
};
while (element) {
if (element.tagName === 'IMG' && element.src) {
if (element.parentNode && element.parentNode.tagName === 'A' && element.parentNode.href) {
data.type = hitTestResultType.SRC_IMAGE_ANCHOR_TYPE;
} else {
data.type = hitTestResultType.IMAGE_TYPE;
}
data.extra = element.src;
break;
} else if (element.tagName === 'A' && element.href) {
if (element.href.indexOf('mailto:') === 0) {
data.type = hitTestResultType.EMAIL_TYPE;
data.extra = element.href.replace('mailto:', '');
} else if (element.href.indexOf('tel:') === 0) {
data.type = hitTestResultType.PHONE_TYPE;
data.extra = element.href.replace('tel:', '');
} else if (element.href.indexOf('geo:') === 0) {
data.type = hitTestResultType.GEO_TYPE;
data.extra = element.href.replace('geo:', '');
} else {
data.type = hitTestResultType.SRC_ANCHOR_TYPE;
data.extra = element.href;
}
break;
} else if (
(element.tagName === 'INPUT' && ['text', 'email', 'password', 'number', 'search', 'tel', 'url'].indexOf(element.type) >= 0) ||
element.tagName === 'TEXTAREA') {
data.type = hitTestResultType.EDIT_TEXT_TYPE
}
element = element.parentNode;
}
return data;
}
"""
public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavigationDelegate, WKScriptMessageHandler, UIGestureRecognizerDelegate {
var IABController: InAppBrowserWebViewController? var IABController: InAppBrowserWebViewController?
var channel: FlutterMethodChannel? var channel: FlutterMethodChannel?
...@@ -889,6 +737,11 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavi ...@@ -889,6 +737,11 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavi
// in order to have the same behavior as Android // in order to have the same behavior as Android
var activateShouldOverrideUrlLoading = false var activateShouldOverrideUrlLoading = false
// https://github.com/mozilla-mobile/firefox-ios/blob/50531a7e9e4d459fb11d4fcb7d4322e08103501f/Client/Frontend/Browser/ContextMenuHelper.swift
fileprivate var nativeHighlightLongPressRecognizer: UILongPressGestureRecognizer?
var longPressRecognizer: UILongPressGestureRecognizer?
init(frame: CGRect, configuration: WKWebViewConfiguration, IABController: InAppBrowserWebViewController?, channel: FlutterMethodChannel?) { init(frame: CGRect, configuration: WKWebViewConfiguration, IABController: InAppBrowserWebViewController?, channel: FlutterMethodChannel?) {
super.init(frame: frame, configuration: configuration) super.init(frame: frame, configuration: configuration)
self.channel = channel self.channel = channel
...@@ -896,14 +749,102 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavi ...@@ -896,14 +749,102 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavi
uiDelegate = self uiDelegate = self
navigationDelegate = self navigationDelegate = self
scrollView.delegate = self scrollView.delegate = self
self.longPressRecognizer = UILongPressGestureRecognizer()
self.longPressRecognizer!.delegate = self
self.longPressRecognizer!.addTarget(self, action: #selector(longPressGestureDetected))
} }
required public init(coder aDecoder: NSCoder) { required public init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)! super.init(coder: aDecoder)!
} }
/*
public override func canPerformAction(_ action: Selector, withSender sender: Any?) -> Bool {
if let _ = sender as? UIMenuController {
print(action)
// disable/hide UIMenuController
return false
}
return super.canPerformAction(action, withSender: sender)
}*/
// @objc func uiMenuViewControllerDidShowMenu() {
// print("MENU\n\n")
// }
public func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
return true
}
// BVC KVO events for all changes on the webview will call this.
// It is called frequently during a page load (particularly on progress changes and URL changes).
// As of iOS 12, WKContentView gesture setup is async, but it has been called by the time
// the webview is ready to load an URL. After this has happened, we can override the gesture.
func replaceGestureHandlerIfNeeded() {
DispatchQueue.main.async {
if self.gestureRecognizerWithDescriptionFragment("InAppWebView") == nil {
self.replaceWebViewLongPress()
}
}
}
private func replaceWebViewLongPress() {
// WebKit installs gesture handlers async. If `replaceWebViewLongPress` is called after a wkwebview in most cases a small delay is sufficient
// See also https://bugs.webkit.org/show_bug.cgi?id=193366
nativeHighlightLongPressRecognizer = gestureRecognizerWithDescriptionFragment("action=_highlightLongPressRecognized:")
if let nativeLongPressRecognizer = gestureRecognizerWithDescriptionFragment("action=_longPressRecognized:") {
nativeLongPressRecognizer.removeTarget(nil, action: nil)
nativeLongPressRecognizer.addTarget(self, action: #selector(self.longPressGestureDetected))
}
}
private func gestureRecognizerWithDescriptionFragment(_ descriptionFragment: String) -> UILongPressGestureRecognizer? {
let result = self.scrollView.subviews.compactMap({ $0.gestureRecognizers }).joined().first(where: {
(($0 as? UILongPressGestureRecognizer) != nil) && $0.description.contains(descriptionFragment)
})
return result as? UILongPressGestureRecognizer
}
@objc func longPressGestureDetected(_ sender: UIGestureRecognizer) {
if sender.state == .cancelled {
return
}
guard sender.state == .began else {
return
}
// To prevent the tapped link from proceeding with navigation, "cancel" the native WKWebView
// `_highlightLongPressRecognizer`. This preserves the original behavior as seen here:
// https://github.com/WebKit/webkit/blob/d591647baf54b4b300ca5501c21a68455429e182/Source/WebKit/UIProcess/ios/WKContentViewInteraction.mm#L1600-L1614
if let nativeHighlightLongPressRecognizer = self.nativeHighlightLongPressRecognizer,
nativeHighlightLongPressRecognizer.isEnabled {
nativeHighlightLongPressRecognizer.isEnabled = false
nativeHighlightLongPressRecognizer.isEnabled = true
}
//Finding actual touch location in webView
var touchLocation = sender.location(in: self)
touchLocation.x -= self.scrollView.contentInset.left
touchLocation.y -= self.scrollView.contentInset.top
touchLocation.x /= self.scrollView.zoomScale
touchLocation.y /= self.scrollView.zoomScale
self.evaluateJavaScript("window.\(JAVASCRIPT_BRIDGE_NAME)._findElementsAtPoint(\(touchLocation.x),\(touchLocation.y))", completionHandler: {(value, error) in
if error != nil {
print("Long press gesture recognizer error: \(error?.localizedDescription)")
} else {
self.onLongPressHitTestResult(hitTestResult: value as! [String: Any?])
}
})
}
public func prepare() { public func prepare() {
self.scrollView.addGestureRecognizer(self.longPressRecognizer!)
addObserver(self, addObserver(self,
forKeyPath: #keyPath(WKWebView.estimatedProgress), forKeyPath: #keyPath(WKWebView.estimatedProgress),
options: .new, options: .new,
...@@ -914,6 +855,12 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavi ...@@ -914,6 +855,12 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavi
options: [.new, .old], options: [.new, .old],
context: nil) context: nil)
// NotificationCenter.default.addObserver(
// self,
// selector: #selector(uiMenuViewControllerDidShowMenu),
// name: UIMenuController.didShowMenuNotification,
// object: nil)
configuration.userContentController = WKUserContentController() configuration.userContentController = WKUserContentController()
configuration.preferences = WKPreferences() configuration.preferences = WKPreferences()
...@@ -943,10 +890,6 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavi ...@@ -943,10 +890,6 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavi
configuration.userContentController.addUserScript(userScript) configuration.userContentController.addUserScript(userScript)
} }
// Prevents long press on links that cause WKWebView exit
let jscriptWebkitTouchCallout = WKUserScript(source: "document.body.style.webkitTouchCallout='none';", injectionTime: .atDocumentEnd, forMainFrameOnly: true)
configuration.userContentController.addUserScript(jscriptWebkitTouchCallout)
let promisePolyfillJSScript = WKUserScript(source: promisePolyfillJS, injectionTime: .atDocumentStart, forMainFrameOnly: false) let promisePolyfillJSScript = WKUserScript(source: promisePolyfillJS, injectionTime: .atDocumentStart, forMainFrameOnly: false)
configuration.userContentController.addUserScript(promisePolyfillJSScript) configuration.userContentController.addUserScript(promisePolyfillJSScript)
...@@ -962,6 +905,9 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavi ...@@ -962,6 +905,9 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavi
configuration.userContentController.add(self, name: "consoleInfo") configuration.userContentController.add(self, name: "consoleInfo")
configuration.userContentController.add(self, name: "consoleWarn") configuration.userContentController.add(self, name: "consoleWarn")
let findElementsAtPointJSScript = WKUserScript(source: findElementsAtPointJS, injectionTime: .atDocumentStart, forMainFrameOnly: false)
configuration.userContentController.addUserScript(findElementsAtPointJSScript)
let printJSScript = WKUserScript(source: printJS, injectionTime: .atDocumentStart, forMainFrameOnly: false) let printJSScript = WKUserScript(source: printJS, injectionTime: .atDocumentStart, forMainFrameOnly: false)
configuration.userContentController.addUserScript(printJSScript) configuration.userContentController.addUserScript(printJSScript)
...@@ -1006,6 +952,7 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavi ...@@ -1006,6 +952,7 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavi
configuration.websiteDataStore.httpCookieStore.setCookie(cookie, completionHandler: nil) configuration.websiteDataStore.httpCookieStore.setCookie(cookie, completionHandler: nil)
} }
} }
accessibilityIgnoresInvertColors = (options?.accessibilityIgnoresInvertColors)!
} }
configuration.suppressesIncrementalRendering = (options?.suppressesIncrementalRendering)! configuration.suppressesIncrementalRendering = (options?.suppressesIncrementalRendering)!
...@@ -1045,10 +992,18 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavi ...@@ -1045,10 +992,18 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavi
scrollView.automaticallyAdjustsScrollIndicatorInsets = (options?.automaticallyAdjustsScrollIndicatorInsets)! scrollView.automaticallyAdjustsScrollIndicatorInsets = (options?.automaticallyAdjustsScrollIndicatorInsets)!
} }
scrollView.showsVerticalScrollIndicator = (options?.verticalScrollBarEnabled)!
scrollView.showsHorizontalScrollIndicator = (options?.horizontalScrollBarEnabled)!
scrollView.showsVerticalScrollIndicator = !(options?.disableVerticalScroll)! scrollView.showsVerticalScrollIndicator = !(options?.disableVerticalScroll)!
scrollView.showsHorizontalScrollIndicator = !(options?.disableHorizontalScroll)! scrollView.showsHorizontalScrollIndicator = !(options?.disableHorizontalScroll)!
scrollView.showsVerticalScrollIndicator = (options?.verticalScrollBarEnabled)!
scrollView.showsHorizontalScrollIndicator = (options?.horizontalScrollBarEnabled)!
scrollView.decelerationRate = getDecelerationRate(type: (options?.decelerationRate)!)
scrollView.alwaysBounceVertical = !(options?.alwaysBounceVertical)!
scrollView.alwaysBounceHorizontal = !(options?.alwaysBounceHorizontal)!
scrollView.scrollsToTop = !(options?.scrollsToTop)!
scrollView.isPagingEnabled = !(options?.isPagingEnabled)!
scrollView.maximumZoomScale = CGFloat((options?.maximumZoomScale)!)
scrollView.minimumZoomScale = CGFloat((options?.minimumZoomScale)!)
// options.debuggingEnabled is always enabled for iOS. // options.debuggingEnabled is always enabled for iOS.
...@@ -1085,6 +1040,17 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavi ...@@ -1085,6 +1040,17 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavi
} }
} }
public func getDecelerationRate(type: String) -> UIScrollView.DecelerationRate {
switch type {
case "NORMAL":
return .normal
case "FAST":
return .fast
default:
return .normal
}
}
public static func preWKWebViewConfiguration(options: InAppWebViewOptions?) -> WKWebViewConfiguration { public static func preWKWebViewConfiguration(options: InAppWebViewOptions?) -> WKWebViewConfiguration {
let configuration = WKWebViewConfiguration() let configuration = WKWebViewConfiguration()
...@@ -1119,6 +1085,7 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavi ...@@ -1119,6 +1085,7 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavi
let newUrl = change?[NSKeyValueChangeKey.newKey] as? URL let newUrl = change?[NSKeyValueChangeKey.newKey] as? URL
onUpdateVisitedHistory(url: newUrl!.absoluteString) onUpdateVisitedHistory(url: newUrl!.absoluteString)
} }
replaceGestureHandlerIfNeeded()
} }
public func goBackOrForward(steps: Int) { public func goBackOrForward(steps: Int) {
...@@ -1252,6 +1219,9 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavi ...@@ -1252,6 +1219,9 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavi
configuration.websiteDataStore.httpCookieStore.setCookie(cookie, completionHandler: nil) configuration.websiteDataStore.httpCookieStore.setCookie(cookie, completionHandler: nil)
} }
} }
if newOptionsMap["accessibilityIgnoresInvertColors"] != nil && options?.accessibilityIgnoresInvertColors != newOptions.accessibilityIgnoresInvertColors {
accessibilityIgnoresInvertColors = newOptions.accessibilityIgnoresInvertColors
}
} }
if newOptionsMap["enableViewportScale"] != nil && options?.enableViewportScale != newOptions.enableViewportScale && newOptions.enableViewportScale { if newOptionsMap["enableViewportScale"] != nil && options?.enableViewportScale != newOptions.enableViewportScale && newOptions.enableViewportScale {
...@@ -1342,6 +1312,13 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavi ...@@ -1342,6 +1312,13 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavi
} }
} }
if newOptionsMap["disableVerticalScroll"] != nil && options?.disableVerticalScroll != newOptions.disableVerticalScroll {
scrollView.showsVerticalScrollIndicator = !newOptions.disableVerticalScroll
}
if newOptionsMap["disableHorizontalScroll"] != nil && options?.disableHorizontalScroll != newOptions.disableHorizontalScroll {
scrollView.showsHorizontalScrollIndicator = !newOptions.disableHorizontalScroll
}
if newOptionsMap["verticalScrollBarEnabled"] != nil && options?.verticalScrollBarEnabled != newOptions.verticalScrollBarEnabled { if newOptionsMap["verticalScrollBarEnabled"] != nil && options?.verticalScrollBarEnabled != newOptions.verticalScrollBarEnabled {
scrollView.showsVerticalScrollIndicator = newOptions.verticalScrollBarEnabled scrollView.showsVerticalScrollIndicator = newOptions.verticalScrollBarEnabled
} }
...@@ -1349,11 +1326,26 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavi ...@@ -1349,11 +1326,26 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavi
scrollView.showsHorizontalScrollIndicator = newOptions.horizontalScrollBarEnabled scrollView.showsHorizontalScrollIndicator = newOptions.horizontalScrollBarEnabled
} }
if newOptionsMap["disableVerticalScroll"] != nil && options?.disableVerticalScroll != newOptions.disableVerticalScroll { if newOptionsMap["decelerationRate"] != nil && options?.decelerationRate != newOptions.decelerationRate {
scrollView.showsVerticalScrollIndicator = !newOptions.disableVerticalScroll scrollView.decelerationRate = getDecelerationRate(type: newOptions.decelerationRate)
} }
if newOptionsMap["disableHorizontalScroll"] != nil && options?.disableHorizontalScroll != newOptions.disableHorizontalScroll { if newOptionsMap["alwaysBounceVertical"] != nil && options?.alwaysBounceVertical != newOptions.alwaysBounceVertical {
scrollView.showsHorizontalScrollIndicator = !newOptions.disableHorizontalScroll scrollView.alwaysBounceVertical = newOptions.alwaysBounceVertical
}
if newOptionsMap["alwaysBounceHorizontal"] != nil && options?.alwaysBounceHorizontal != newOptions.alwaysBounceHorizontal {
scrollView.alwaysBounceHorizontal = newOptions.alwaysBounceHorizontal
}
if newOptionsMap["scrollsToTop"] != nil && options?.scrollsToTop != newOptions.scrollsToTop {
scrollView.scrollsToTop = newOptions.scrollsToTop
}
if newOptionsMap["isPagingEnabled"] != nil && options?.isPagingEnabled != newOptions.isPagingEnabled {
scrollView.scrollsToTop = newOptions.isPagingEnabled
}
if newOptionsMap["maximumZoomScale"] != nil && options?.maximumZoomScale != newOptions.maximumZoomScale {
scrollView.maximumZoomScale = CGFloat(newOptions.maximumZoomScale)
}
if newOptionsMap["minimumZoomScale"] != nil && options?.minimumZoomScale != newOptions.minimumZoomScale {
scrollView.minimumZoomScale = CGFloat(newOptions.minimumZoomScale)
} }
if #available(iOS 9.0, *) { if #available(iOS 9.0, *) {
...@@ -1512,10 +1504,13 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavi ...@@ -1512,10 +1504,13 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavi
shouldOverrideUrlLoading(url: url, method: navigationAction.request.httpMethod, headers: navigationAction.request.allHTTPHeaderFields, isForMainFrame: isForMainFrame, navigationType: navigationAction.navigationType, result: { (result) -> Void in shouldOverrideUrlLoading(url: url, method: navigationAction.request.httpMethod, headers: navigationAction.request.allHTTPHeaderFields, isForMainFrame: isForMainFrame, navigationType: navigationAction.navigationType, result: { (result) -> Void in
if result is FlutterError { if result is FlutterError {
print((result as! FlutterError).message ?? "") print((result as! FlutterError).message ?? "")
decisionHandler(.allow)
return
} }
else if (result as? NSObject) == FlutterMethodNotImplemented { else if (result as? NSObject) == FlutterMethodNotImplemented {
self.updateUrlTextFieldForIABController(navigationAction: navigationAction) self.updateUrlTextFieldForIABController(navigationAction: navigationAction)
decisionHandler(.allow) decisionHandler(.allow)
return
} }
else { else {
var response: [String: Any] var response: [String: Any]
...@@ -2067,6 +2062,115 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavi ...@@ -2067,6 +2062,115 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavi
return nil return nil
} }
public func webViewWebContentProcessDidTerminate(_ webView: WKWebView) {
onWebContentProcessDidTerminate()
}
public func webView(_ webView: WKWebView,
didCommit navigation: WKNavigation!) {
onDidCommit()
}
public func webView(_ webView: WKWebView,
didReceiveServerRedirectForProvisionalNavigation navigation: WKNavigation!) {
onDidReceiveServerRedirectForProvisionalNavigation()
}
// @available(iOS 13.0, *)
// public func webView(_ webView: WKWebView,
// contextMenuConfigurationForElement elementInfo: WKContextMenuElementInfo,
// completionHandler: @escaping (UIContextMenuConfiguration?) -> Void) {
// let actionProvider: UIContextMenuActionProvider = { _ in
// let editMenu = UIMenu(title: "Edit...", children: [
// UIAction(title: "Copy") { action in
//
// },
// UIAction(title: "Duplicate") { action in
//
// }
// ])
// return UIMenu(title: "Title", children: [
// UIAction(title: "Share") { action in
//
// },
// editMenu
// ])
// }
// let contextMenuConfiguration = UIContextMenuConfiguration(identifier: nil, previewProvider: nil, actionProvider: actionProvider)
// //completionHandler(contextMenuConfiguration)
// completionHandler(nil)
// onContextMenuConfigurationForElement(linkURL: elementInfo.linkURL?.absoluteString, result: nil/*{(result) -> Void in
// if result is FlutterError {
// print((result as! FlutterError).message ?? "")
// }
// else if (result as? NSObject) == FlutterMethodNotImplemented {
// completionHandler(nil)
// }
// else {
// var response: [String: Any]
// if let r = result {
// response = r as! [String: Any]
// var action = response["action"] as? Int
// action = action != nil ? action : 0;
// switch action {
// case 0:
// break
// case 1:
// break
// default:
// completionHandler(nil)
// }
// return;
// }
// completionHandler(nil)
// }
// }*/)
// }
//
// @available(iOS 13.0, *)
// public func webView(_ webView: WKWebView,
// contextMenuDidEndForElement elementInfo: WKContextMenuElementInfo) {
// onContextMenuDidEndForElement(linkURL: elementInfo.linkURL?.absoluteString)
// }
//
// @available(iOS 13.0, *)
// public func webView(_ webView: WKWebView,
// contextMenuForElement elementInfo: WKContextMenuElementInfo,
// willCommitWithAnimator animator: UIContextMenuInteractionCommitAnimating) {
// onWillCommitWithAnimator(linkURL: elementInfo.linkURL?.absoluteString, result: nil/*{(result) -> Void in
// if result is FlutterError {
// print((result as! FlutterError).message ?? "")
// }
// else if (result as? NSObject) == FlutterMethodNotImplemented {
//
// }
// else {
// var response: [String: Any]
// if let r = result {
// response = r as! [String: Any]
// var action = response["action"] as? Int
// action = action != nil ? action : 0;
//// switch action {
//// case 0:
//// break
//// case 1:
//// break
//// default:
////
//// }
// return;
// }
//
// }
// }*/)
// }
//
// @available(iOS 13.0, *)
// public func webView(_ webView: WKWebView,
// contextMenuWillPresentForElement elementInfo: WKContextMenuElementInfo) {
// onContextMenuWillPresentForElement(linkURL: elementInfo.linkURL?.absoluteString)
// }
public func onLoadStart(url: String) { public func onLoadStart(url: String) {
let arguments: [String: Any] = ["url": url] let arguments: [String: Any] = ["url": url]
channel?.invokeMethod("onLoadStart", arguments: arguments) channel?.invokeMethod("onLoadStart", arguments: arguments)
...@@ -2211,6 +2315,13 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavi ...@@ -2211,6 +2315,13 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavi
channel?.invokeMethod("onUpdateVisitedHistory", arguments: arguments) channel?.invokeMethod("onUpdateVisitedHistory", arguments: arguments)
} }
public func onLongPressHitTestResult(hitTestResult: [String: Any?]) {
let arguments: [String: Any?] = [
"hitTestResult": hitTestResult
]
channel?.invokeMethod("onLongPressHitTestResult", arguments: arguments)
}
public func onCallJsHandler(handlerName: String, _callHandlerID: Int64, args: String) { public func onCallJsHandler(handlerName: String, _callHandlerID: Int64, args: String) {
let arguments: [String: Any] = ["handlerName": handlerName, "args": args] let arguments: [String: Any] = ["handlerName": handlerName, "args": args]
channel?.invokeMethod("onCallJsHandler", arguments: arguments, result: {(result) -> Void in channel?.invokeMethod("onCallJsHandler", arguments: arguments, result: {(result) -> Void in
...@@ -2228,6 +2339,38 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavi ...@@ -2228,6 +2339,38 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavi
}) })
} }
public func onWebContentProcessDidTerminate() {
channel?.invokeMethod("onWebContentProcessDidTerminate", arguments: [])
}
public func onDidCommit() {
channel?.invokeMethod("onDidCommit", arguments: [])
}
public func onDidReceiveServerRedirectForProvisionalNavigation() {
channel?.invokeMethod("onDidReceiveServerRedirectForProvisionalNavigation", arguments: [])
}
// public func onContextMenuConfigurationForElement(linkURL: String?, result: FlutterResult?) {
// let arguments: [String: Any?] = ["linkURL": linkURL]
// channel?.invokeMethod("onContextMenuConfigurationForElement", arguments: arguments, result: result)
// }
//
// public func onContextMenuDidEndForElement(linkURL: String?) {
// let arguments: [String: Any?] = ["linkURL": linkURL]
// channel?.invokeMethod("onContextMenuDidEndForElement", arguments: arguments)
// }
//
// public func onWillCommitWithAnimator(linkURL: String?, result: FlutterResult?) {
// let arguments: [String: Any?] = ["linkURL": linkURL]
// channel?.invokeMethod("onWillCommitWithAnimator", arguments: arguments, result: result)
// }
//
// public func onContextMenuWillPresentForElement(linkURL: String?) {
// let arguments: [String: Any?] = ["linkURL": linkURL]
// channel?.invokeMethod("onContextMenuWillPresentForElement", arguments: arguments)
// }
public func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) { public func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
if message.name.starts(with: "console") { if message.name.starts(with: "console") {
var messageLevel = 1 var messageLevel = 1
...@@ -2361,6 +2504,9 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavi ...@@ -2361,6 +2504,9 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavi
if #available(iOS 11.0, *) { if #available(iOS 11.0, *) {
configuration.userContentController.removeAllContentRuleLists() configuration.userContentController.removeAllContentRuleLists()
} }
longPressRecognizer!.removeTarget(self, action: #selector(longPressGestureDetected))
longPressRecognizer!.delegate = nil
self.scrollView.removeGestureRecognizer(longPressRecognizer!)
uiDelegate = nil uiDelegate = nil
navigationDelegate = nil navigationDelegate = nil
scrollView.delegate = nil scrollView.delegate = nil
......
File mode changed from 100644 to 100755
File mode changed from 100644 to 100755
...@@ -49,11 +49,17 @@ public class InAppWebViewOptions: Options { ...@@ -49,11 +49,17 @@ public class InAppWebViewOptions: Options {
var preferredContentMode = 0 var preferredContentMode = 0
var sharedCookiesEnabled = false var sharedCookiesEnabled = false
var automaticallyAdjustsScrollIndicatorInsets = false var automaticallyAdjustsScrollIndicatorInsets = false
var accessibilityIgnoresInvertColors = false
var decelerationRate = "NORMAL" // UIScrollView.DecelerationRate.normal
var alwaysBounceVertical = false
var alwaysBounceHorizontal = false
var scrollsToTop = true
var isPagingEnabled = false
var maximumZoomScale = 1.0
var minimumZoomScale = 1.0
override init(){ override init(){
super.init() super.init()
} }
} }
File mode changed from 100644 to 100755
File mode changed from 100644 to 100755
...@@ -90,7 +90,9 @@ class MyCookieManager: NSObject, FlutterPlugin { ...@@ -90,7 +90,9 @@ class MyCookieManager: NSObject, FlutterPlugin {
if maxAge != nil { if maxAge != nil {
properties[.maximumAge] = String(maxAge!) properties[.maximumAge] = String(maxAge!)
} }
properties[.secure] = (isSecure != nil && isSecure!) ? "TRUE" : "FALSE" if isSecure != nil && isSecure! {
properties[.secure] = "TRUE"
}
let cookie = HTTPCookie(properties: properties)! let cookie = HTTPCookie(properties: properties)!
MyCookieManager.httpCookieStore!.setCookie(cookie, completionHandler: {() in MyCookieManager.httpCookieStore!.setCookie(cookie, completionHandler: {() in
......
File mode changed from 100644 to 100755
File mode changed from 100644 to 100755
File mode changed from 100644 to 100755
File mode changed from 100644 to 100755
File mode changed from 100644 to 100755
File mode changed from 100644 to 100755
File mode changed from 100644 to 100755
...@@ -497,6 +497,11 @@ class InAppBrowser { ...@@ -497,6 +497,11 @@ class InAppBrowser {
///**NOTE**: available on Android 21+. ///**NOTE**: available on Android 21+.
void onPrint(String url) {} void onPrint(String url) {}
///Event fired when an HTML element of the webview has been clicked and held.
///
///[hitTestResult] represents the hit result for hitting an HTML elements.
void onLongPressHitTestResult(LongPressHitTestResult hitTestResult) {}
///Event fired when the WebView notifies that a loading URL has been flagged by Safe Browsing. ///Event fired when the WebView notifies that a loading URL has been flagged by Safe Browsing.
///The default behavior is to show an interstitial to the user, with the reporting checkbox visible. ///The default behavior is to show an interstitial to the user, with the reporting checkbox visible.
/// ///
...@@ -537,6 +542,21 @@ class InAppBrowser { ...@@ -537,6 +542,21 @@ class InAppBrowser {
///**NOTE**: available only on Android. ///**NOTE**: available only on Android.
void androidOnGeolocationPermissionsHidePrompt() {} void androidOnGeolocationPermissionsHidePrompt() {}
///Invoked when the web view's web content process is terminated.
///
///**NOTE**: available only on iOS.
void iosOnWebContentProcessDidTerminate() {}
///Called when the web view begins to receive web content.
///
///**NOTE**: available only on iOS.
void iosOnDidCommit() {}
///Called when a web view receives a server redirect.
///
///**NOTE**: available only on iOS.
void iosOnDidReceiveServerRedirectForProvisionalNavigation() {}
void throwIsAlreadyOpened({String message = ''}) { void throwIsAlreadyOpened({String message = ''}) {
if (this.isOpened()) { if (this.isOpened()) {
throw Exception([ throw Exception([
......
File mode changed from 100644 to 100755
...@@ -253,6 +253,11 @@ class InAppWebView extends StatefulWidget { ...@@ -253,6 +253,11 @@ class InAppWebView extends StatefulWidget {
///**NOTE**: available on Android 21+. ///**NOTE**: available on Android 21+.
final void Function(InAppWebViewController controller, String url) onPrint; final void Function(InAppWebViewController controller, String url) onPrint;
///Event fired when an HTML element of the webview has been clicked and held.
///
///[hitTestResult] represents the hit result for hitting an HTML elements.
final void Function(InAppWebViewController controller, LongPressHitTestResult hitTestResult) onLongPressHitTestResult;
///Event fired when the webview notifies that a loading URL has been flagged by Safe Browsing. ///Event fired when the webview notifies that a loading URL has been flagged by Safe Browsing.
///The default behavior is to show an interstitial to the user, with the reporting checkbox visible. ///The default behavior is to show an interstitial to the user, with the reporting checkbox visible.
/// ///
...@@ -293,6 +298,21 @@ class InAppWebView extends StatefulWidget { ...@@ -293,6 +298,21 @@ class InAppWebView extends StatefulWidget {
///**NOTE**: available only on Android. ///**NOTE**: available only on Android.
final Future<void> Function(InAppWebViewController controller) androidOnGeolocationPermissionsHidePrompt; final Future<void> Function(InAppWebViewController controller) androidOnGeolocationPermissionsHidePrompt;
///Invoked when the web view's web content process is terminated.
///
///**NOTE**: available only on iOS.
final Future<void> Function(InAppWebViewController controller) iosOnWebContentProcessDidTerminate;
///Called when the web view begins to receive web content.
///
///**NOTE**: available only on iOS.
final Future<void> Function(InAppWebViewController controller) iosOnDidCommit;
///Called when a web view receives a server redirect.
///
///**NOTE**: available only on iOS.
final Future<void> Function(InAppWebViewController controller) iosOnDidReceiveServerRedirectForProvisionalNavigation;
///Initial url that will be loaded. ///Initial url that will be loaded.
final String initialUrl; final String initialUrl;
...@@ -350,10 +370,14 @@ class InAppWebView extends StatefulWidget { ...@@ -350,10 +370,14 @@ class InAppWebView extends StatefulWidget {
this.shouldInterceptFetchRequest, this.shouldInterceptFetchRequest,
this.onUpdateVisitedHistory, this.onUpdateVisitedHistory,
this.onPrint, this.onPrint,
this.onLongPressHitTestResult,
this.androidOnSafeBrowsingHit, this.androidOnSafeBrowsingHit,
this.androidOnPermissionRequest, this.androidOnPermissionRequest,
this.androidOnGeolocationPermissionsShowPrompt, this.androidOnGeolocationPermissionsShowPrompt,
this.androidOnGeolocationPermissionsHidePrompt, this.androidOnGeolocationPermissionsHidePrompt,
this.iosOnWebContentProcessDidTerminate,
this.iosOnDidCommit,
this.iosOnDidReceiveServerRedirectForProvisionalNavigation,
this.gestureRecognizers, this.gestureRecognizers,
}) : super(key: key); }) : super(key: key);
...@@ -383,7 +407,7 @@ class _InAppWebViewState extends State<InAppWebView> { ...@@ -383,7 +407,7 @@ class _InAppWebViewState extends State<InAppWebView> {
gestureRecognizers: widget.gestureRecognizers, gestureRecognizers: widget.gestureRecognizers,
layoutDirection: TextDirection.rtl, layoutDirection: TextDirection.rtl,
creationParams: <String, dynamic>{ creationParams: <String, dynamic>{
'initialUrl': widget.initialUrl, 'initialUrl': '${Uri.parse(widget.initialUrl)}',
'initialFile': widget.initialFile, 'initialFile': widget.initialFile,
'initialData': widget.initialData?.toMap(), 'initialData': widget.initialData?.toMap(),
'initialHeaders': widget.initialHeaders, 'initialHeaders': widget.initialHeaders,
...@@ -401,7 +425,7 @@ class _InAppWebViewState extends State<InAppWebView> { ...@@ -401,7 +425,7 @@ class _InAppWebViewState extends State<InAppWebView> {
gestureRecognizers: widget.gestureRecognizers, gestureRecognizers: widget.gestureRecognizers,
layoutDirection: TextDirection.rtl, layoutDirection: TextDirection.rtl,
creationParams: <String, dynamic>{ creationParams: <String, dynamic>{
'initialUrl': widget.initialUrl, 'initialUrl': '${Uri.parse(widget.initialUrl)}',
'initialFile': widget.initialFile, 'initialFile': widget.initialFile,
'initialData': widget.initialData?.toMap(), 'initialData': widget.initialData?.toMap(),
'initialHeaders': widget.initialHeaders, 'initialHeaders': widget.initialHeaders,
...@@ -416,7 +440,7 @@ class _InAppWebViewState extends State<InAppWebView> { ...@@ -416,7 +440,7 @@ class _InAppWebViewState extends State<InAppWebView> {
onPlatformViewCreated: _onPlatformViewCreated, onPlatformViewCreated: _onPlatformViewCreated,
gestureRecognizers: widget.gestureRecognizers, gestureRecognizers: widget.gestureRecognizers,
creationParams: <String, dynamic>{ creationParams: <String, dynamic>{
'initialUrl': widget.initialUrl, 'initialUrl': '${Uri.parse(widget.initialUrl)}',
'initialFile': widget.initialFile, 'initialFile': widget.initialFile,
'initialData': widget.initialData?.toMap(), 'initialData': widget.initialData?.toMap(),
'initialHeaders': widget.initialHeaders, 'initialHeaders': widget.initialHeaders,
...@@ -744,6 +768,35 @@ class InAppWebViewController { ...@@ -744,6 +768,35 @@ class InAppWebViewController {
else if (_inAppBrowser != null) else if (_inAppBrowser != null)
_inAppBrowser.onUpdateVisitedHistory(url, androidIsReload); _inAppBrowser.onUpdateVisitedHistory(url, androidIsReload);
return null; return null;
case "onWebContentProcessDidTerminate":
if (_widget != null && _widget.iosOnWebContentProcessDidTerminate != null)
_widget.iosOnWebContentProcessDidTerminate(this);
else if (_inAppBrowser != null)
_inAppBrowser.iosOnWebContentProcessDidTerminate();
return null;
case "onDidCommit":
if (_widget != null && _widget.iosOnDidCommit != null)
_widget.iosOnDidCommit(this);
else if (_inAppBrowser != null)
_inAppBrowser.iosOnDidCommit();
return null;
case "onDidReceiveServerRedirectForProvisionalNavigation":
if (_widget != null && _widget.iosOnDidReceiveServerRedirectForProvisionalNavigation != null)
_widget.iosOnDidReceiveServerRedirectForProvisionalNavigation(this);
else if (_inAppBrowser != null)
_inAppBrowser.iosOnDidReceiveServerRedirectForProvisionalNavigation();
return null;
case "onLongPressHitTestResult":
Map<dynamic, dynamic> hitTestResultMap = call.arguments["hitTestResult"];
LongPressHitTestResultType type = LongPressHitTestResultType.fromValue(hitTestResultMap["type"].toInt());
String extra = hitTestResultMap["extra"];
LongPressHitTestResult hitTestResult = new LongPressHitTestResult(type: type, extra: extra);
if (_widget != null && _widget.onLongPressHitTestResult != null)
_widget.onLongPressHitTestResult(this, hitTestResult);
else if (_inAppBrowser != null)
_inAppBrowser.onLongPressHitTestResult(hitTestResult);
break;
case "onCallJsHandler": case "onCallJsHandler":
String handlerName = call.arguments["handlerName"]; String handlerName = call.arguments["handlerName"];
// decode args to json // decode args to json
...@@ -966,30 +1019,18 @@ class InAppWebViewController { ...@@ -966,30 +1019,18 @@ class InAppWebViewController {
///This is not always the same as the URL passed to [InAppWebView.onLoadStarted] because although the load for that URL has begun, the current page may not have changed. ///This is not always the same as the URL passed to [InAppWebView.onLoadStarted] because although the load for that URL has begun, the current page may not have changed.
Future<String> getUrl() async { Future<String> getUrl() async {
Map<String, dynamic> args = <String, dynamic>{}; Map<String, dynamic> args = <String, dynamic>{};
if (_inAppBrowserUuid != null && _inAppBrowser != null) {
_inAppBrowser.throwIsNotOpened();
args.putIfAbsent('uuid', () => _inAppBrowserUuid);
}
return await _channel.invokeMethod('getUrl', args); return await _channel.invokeMethod('getUrl', args);
} }
///Gets the title for the current page. ///Gets the title for the current page.
Future<String> getTitle() async { Future<String> getTitle() async {
Map<String, dynamic> args = <String, dynamic>{}; Map<String, dynamic> args = <String, dynamic>{};
if (_inAppBrowserUuid != null && _inAppBrowser != null) {
_inAppBrowser.throwIsNotOpened();
args.putIfAbsent('uuid', () => _inAppBrowserUuid);
}
return await _channel.invokeMethod('getTitle', args); return await _channel.invokeMethod('getTitle', args);
} }
///Gets the progress for the current page. The progress value is between 0 and 100. ///Gets the progress for the current page. The progress value is between 0 and 100.
Future<int> getProgress() async { Future<int> getProgress() async {
Map<String, dynamic> args = <String, dynamic>{}; Map<String, dynamic> args = <String, dynamic>{};
if (_inAppBrowserUuid != null && _inAppBrowser != null) {
_inAppBrowser.throwIsNotOpened();
args.putIfAbsent('uuid', () => _inAppBrowserUuid);
}
return await _channel.invokeMethod('getProgress', args); return await _channel.invokeMethod('getProgress', args);
} }
...@@ -1160,10 +1201,6 @@ class InAppWebViewController { ...@@ -1160,10 +1201,6 @@ class InAppWebViewController {
{@required String url, Map<String, String> headers = const {}}) async { {@required String url, Map<String, String> headers = const {}}) async {
assert(url != null && url.isNotEmpty); assert(url != null && url.isNotEmpty);
Map<String, dynamic> args = <String, dynamic>{}; Map<String, dynamic> args = <String, dynamic>{};
if (_inAppBrowserUuid != null && _inAppBrowser != null) {
_inAppBrowser.throwIsNotOpened(message: 'Cannot laod $url!');
args.putIfAbsent('uuid', () => _inAppBrowserUuid);
}
args.putIfAbsent('url', () => url); args.putIfAbsent('url', () => url);
args.putIfAbsent('headers', () => headers); args.putIfAbsent('headers', () => headers);
await _channel.invokeMethod('loadUrl', args); await _channel.invokeMethod('loadUrl', args);
...@@ -1175,10 +1212,6 @@ class InAppWebViewController { ...@@ -1175,10 +1212,6 @@ class InAppWebViewController {
assert(url != null && url.isNotEmpty); assert(url != null && url.isNotEmpty);
assert(postData != null); assert(postData != null);
Map<String, dynamic> args = <String, dynamic>{}; Map<String, dynamic> args = <String, dynamic>{};
if (_inAppBrowserUuid != null && _inAppBrowser != null) {
_inAppBrowser.throwIsNotOpened(message: 'Cannot laod $url!');
args.putIfAbsent('uuid', () => _inAppBrowserUuid);
}
args.putIfAbsent('url', () => url); args.putIfAbsent('url', () => url);
args.putIfAbsent('postData', () => postData); args.putIfAbsent('postData', () => postData);
await _channel.invokeMethod('postUrl', args); await _channel.invokeMethod('postUrl', args);
...@@ -1199,10 +1232,6 @@ class InAppWebViewController { ...@@ -1199,10 +1232,6 @@ class InAppWebViewController {
String androidHistoryUrl = "about:blank"}) async { String androidHistoryUrl = "about:blank"}) async {
assert(data != null); assert(data != null);
Map<String, dynamic> args = <String, dynamic>{}; Map<String, dynamic> args = <String, dynamic>{};
if (_inAppBrowserUuid != null && _inAppBrowser != null) {
_inAppBrowser.throwIsNotOpened();
args.putIfAbsent('uuid', () => _inAppBrowserUuid);
}
args.putIfAbsent('data', () => data); args.putIfAbsent('data', () => data);
args.putIfAbsent('mimeType', () => mimeType); args.putIfAbsent('mimeType', () => mimeType);
args.putIfAbsent('encoding', () => encoding); args.putIfAbsent('encoding', () => encoding);
...@@ -1245,10 +1274,6 @@ class InAppWebViewController { ...@@ -1245,10 +1274,6 @@ class InAppWebViewController {
Map<String, String> headers = const {}}) async { Map<String, String> headers = const {}}) async {
assert(assetFilePath != null && assetFilePath.isNotEmpty); assert(assetFilePath != null && assetFilePath.isNotEmpty);
Map<String, dynamic> args = <String, dynamic>{}; Map<String, dynamic> args = <String, dynamic>{};
if (_inAppBrowserUuid != null && _inAppBrowser != null) {
_inAppBrowser.throwIsNotOpened(message: 'Cannot laod $assetFilePath!');
args.putIfAbsent('uuid', () => _inAppBrowserUuid);
}
args.putIfAbsent('url', () => assetFilePath); args.putIfAbsent('url', () => assetFilePath);
args.putIfAbsent('headers', () => headers); args.putIfAbsent('headers', () => headers);
await _channel.invokeMethod('loadFile', args); await _channel.invokeMethod('loadFile', args);
...@@ -1257,50 +1282,30 @@ class InAppWebViewController { ...@@ -1257,50 +1282,30 @@ class InAppWebViewController {
///Reloads the WebView. ///Reloads the WebView.
Future<void> reload() async { Future<void> reload() async {
Map<String, dynamic> args = <String, dynamic>{}; Map<String, dynamic> args = <String, dynamic>{};
if (_inAppBrowserUuid != null && _inAppBrowser != null) {
_inAppBrowser.throwIsNotOpened();
args.putIfAbsent('uuid', () => _inAppBrowserUuid);
}
await _channel.invokeMethod('reload', args); await _channel.invokeMethod('reload', args);
} }
///Goes back in the history of the WebView. ///Goes back in the history of the WebView.
Future<void> goBack() async { Future<void> goBack() async {
Map<String, dynamic> args = <String, dynamic>{}; Map<String, dynamic> args = <String, dynamic>{};
if (_inAppBrowserUuid != null && _inAppBrowser != null) {
_inAppBrowser.throwIsNotOpened();
args.putIfAbsent('uuid', () => _inAppBrowserUuid);
}
await _channel.invokeMethod('goBack', args); await _channel.invokeMethod('goBack', args);
} }
///Returns a boolean value indicating whether the WebView can move backward. ///Returns a boolean value indicating whether the WebView can move backward.
Future<bool> canGoBack() async { Future<bool> canGoBack() async {
Map<String, dynamic> args = <String, dynamic>{}; Map<String, dynamic> args = <String, dynamic>{};
if (_inAppBrowserUuid != null && _inAppBrowser != null) {
_inAppBrowser.throwIsNotOpened();
args.putIfAbsent('uuid', () => _inAppBrowserUuid);
}
return await _channel.invokeMethod('canGoBack', args); return await _channel.invokeMethod('canGoBack', args);
} }
///Goes forward in the history of the WebView. ///Goes forward in the history of the WebView.
Future<void> goForward() async { Future<void> goForward() async {
Map<String, dynamic> args = <String, dynamic>{}; Map<String, dynamic> args = <String, dynamic>{};
if (_inAppBrowserUuid != null && _inAppBrowser != null) {
_inAppBrowser.throwIsNotOpened();
args.putIfAbsent('uuid', () => _inAppBrowserUuid);
}
await _channel.invokeMethod('goForward', args); await _channel.invokeMethod('goForward', args);
} }
///Returns a boolean value indicating whether the WebView can move forward. ///Returns a boolean value indicating whether the WebView can move forward.
Future<bool> canGoForward() async { Future<bool> canGoForward() async {
Map<String, dynamic> args = <String, dynamic>{}; Map<String, dynamic> args = <String, dynamic>{};
if (_inAppBrowserUuid != null && _inAppBrowser != null) {
_inAppBrowser.throwIsNotOpened();
args.putIfAbsent('uuid', () => _inAppBrowserUuid);
}
return await _channel.invokeMethod('canGoForward', args); return await _channel.invokeMethod('canGoForward', args);
} }
...@@ -1309,10 +1314,6 @@ class InAppWebViewController { ...@@ -1309,10 +1314,6 @@ class InAppWebViewController {
assert(steps != null); assert(steps != null);
Map<String, dynamic> args = <String, dynamic>{}; Map<String, dynamic> args = <String, dynamic>{};
if (_inAppBrowserUuid != null && _inAppBrowser != null) {
_inAppBrowser.throwIsNotOpened();
args.putIfAbsent('uuid', () => _inAppBrowserUuid);
}
args.putIfAbsent('steps', () => steps); args.putIfAbsent('steps', () => steps);
await _channel.invokeMethod('goBackOrForward', args); await _channel.invokeMethod('goBackOrForward', args);
} }
...@@ -1322,10 +1323,6 @@ class InAppWebViewController { ...@@ -1322,10 +1323,6 @@ class InAppWebViewController {
assert(steps != null); assert(steps != null);
Map<String, dynamic> args = <String, dynamic>{}; Map<String, dynamic> args = <String, dynamic>{};
if (_inAppBrowserUuid != null && _inAppBrowser != null) {
_inAppBrowser.throwIsNotOpened();
args.putIfAbsent('uuid', () => _inAppBrowserUuid);
}
args.putIfAbsent('steps', () => steps); args.putIfAbsent('steps', () => steps);
return await _channel.invokeMethod('canGoBackOrForward', args); return await _channel.invokeMethod('canGoBackOrForward', args);
} }
...@@ -1338,30 +1335,18 @@ class InAppWebViewController { ...@@ -1338,30 +1335,18 @@ class InAppWebViewController {
///Check if the WebView instance is in a loading state. ///Check if the WebView instance is in a loading state.
Future<bool> isLoading() async { Future<bool> isLoading() async {
Map<String, dynamic> args = <String, dynamic>{}; Map<String, dynamic> args = <String, dynamic>{};
if (_inAppBrowserUuid != null && _inAppBrowser != null) {
_inAppBrowser.throwIsNotOpened();
args.putIfAbsent('uuid', () => _inAppBrowserUuid);
}
return await _channel.invokeMethod('isLoading', args); return await _channel.invokeMethod('isLoading', args);
} }
///Stops the WebView from loading. ///Stops the WebView from loading.
Future<void> stopLoading() async { Future<void> stopLoading() async {
Map<String, dynamic> args = <String, dynamic>{}; Map<String, dynamic> args = <String, dynamic>{};
if (_inAppBrowserUuid != null && _inAppBrowser != null) {
_inAppBrowser.throwIsNotOpened();
args.putIfAbsent('uuid', () => _inAppBrowserUuid);
}
await _channel.invokeMethod('stopLoading', args); await _channel.invokeMethod('stopLoading', args);
} }
///Evaluates JavaScript code into the WebView and returns the result of the evaluation. ///Evaluates JavaScript code into the WebView and returns the result of the evaluation.
Future<dynamic> evaluateJavascript({@required String source}) async { Future<dynamic> evaluateJavascript({@required String source}) async {
Map<String, dynamic> args = <String, dynamic>{}; Map<String, dynamic> args = <String, dynamic>{};
if (_inAppBrowserUuid != null && _inAppBrowser != null) {
_inAppBrowser.throwIsNotOpened();
args.putIfAbsent('uuid', () => _inAppBrowserUuid);
}
args.putIfAbsent('source', () => source); args.putIfAbsent('source', () => source);
var data = await _channel.invokeMethod('evaluateJavascript', args); var data = await _channel.invokeMethod('evaluateJavascript', args);
if (data != null && Platform.isAndroid) data = json.decode(data); if (data != null && Platform.isAndroid) data = json.decode(data);
...@@ -1371,10 +1356,6 @@ class InAppWebViewController { ...@@ -1371,10 +1356,6 @@ class InAppWebViewController {
///Injects an external JavaScript file into the WebView from a defined url. ///Injects an external JavaScript file into the WebView from a defined url.
Future<void> injectJavascriptFileFromUrl({@required String urlFile}) async { Future<void> injectJavascriptFileFromUrl({@required String urlFile}) async {
Map<String, dynamic> args = <String, dynamic>{}; Map<String, dynamic> args = <String, dynamic>{};
if (_inAppBrowserUuid != null && _inAppBrowser != null) {
_inAppBrowser.throwIsNotOpened();
args.putIfAbsent('uuid', () => _inAppBrowserUuid);
}
args.putIfAbsent('urlFile', () => urlFile); args.putIfAbsent('urlFile', () => urlFile);
await _channel.invokeMethod('injectJavascriptFileFromUrl', args); await _channel.invokeMethod('injectJavascriptFileFromUrl', args);
} }
...@@ -1389,10 +1370,6 @@ class InAppWebViewController { ...@@ -1389,10 +1370,6 @@ class InAppWebViewController {
///Injects CSS into the WebView. ///Injects CSS into the WebView.
Future<void> injectCSSCode({@required String source}) async { Future<void> injectCSSCode({@required String source}) async {
Map<String, dynamic> args = <String, dynamic>{}; Map<String, dynamic> args = <String, dynamic>{};
if (_inAppBrowserUuid != null && _inAppBrowser != null) {
_inAppBrowser.throwIsNotOpened();
args.putIfAbsent('uuid', () => _inAppBrowserUuid);
}
args.putIfAbsent('source', () => source); args.putIfAbsent('source', () => source);
await _channel.invokeMethod('injectCSSCode', args); await _channel.invokeMethod('injectCSSCode', args);
} }
...@@ -1400,10 +1377,6 @@ class InAppWebViewController { ...@@ -1400,10 +1377,6 @@ class InAppWebViewController {
///Injects an external CSS file into the WebView from a defined url. ///Injects an external CSS file into the WebView from a defined url.
Future<void> injectCSSFileFromUrl({@required String urlFile}) async { Future<void> injectCSSFileFromUrl({@required String urlFile}) async {
Map<String, dynamic> args = <String, dynamic>{}; Map<String, dynamic> args = <String, dynamic>{};
if (_inAppBrowserUuid != null && _inAppBrowser != null) {
_inAppBrowser.throwIsNotOpened();
args.putIfAbsent('uuid', () => _inAppBrowserUuid);
}
args.putIfAbsent('urlFile', () => urlFile); args.putIfAbsent('urlFile', () => urlFile);
await _channel.invokeMethod('injectStyleFile', args); await _channel.invokeMethod('injectStyleFile', args);
} }
...@@ -1480,20 +1453,12 @@ class InAppWebViewController { ...@@ -1480,20 +1453,12 @@ class InAppWebViewController {
///**NOTE for iOS**: available from iOS 11.0+. ///**NOTE for iOS**: available from iOS 11.0+.
Future<Uint8List> takeScreenshot() async { Future<Uint8List> takeScreenshot() async {
Map<String, dynamic> args = <String, dynamic>{}; Map<String, dynamic> args = <String, dynamic>{};
if (_inAppBrowserUuid != null && _inAppBrowser != null) {
_inAppBrowser.throwIsNotOpened();
args.putIfAbsent('uuid', () => _inAppBrowserUuid);
}
return await _channel.invokeMethod('takeScreenshot', args); return await _channel.invokeMethod('takeScreenshot', args);
} }
///Sets the WebView options with the new [options] and evaluates them. ///Sets the WebView options with the new [options] and evaluates them.
Future<void> setOptions({@required InAppWebViewWidgetOptions options}) async { Future<void> setOptions({@required InAppWebViewWidgetOptions options}) async {
Map<String, dynamic> args = <String, dynamic>{}; Map<String, dynamic> args = <String, dynamic>{};
if (_inAppBrowserUuid != null && _inAppBrowser != null) {
_inAppBrowser.throwIsNotOpened();
args.putIfAbsent('uuid', () => _inAppBrowserUuid);
}
Map<String, dynamic> optionsMap = {}; Map<String, dynamic> optionsMap = {};
optionsMap.addAll(options.crossPlatform?.toMap() ?? {}); optionsMap.addAll(options.crossPlatform?.toMap() ?? {});
...@@ -1509,10 +1474,6 @@ class InAppWebViewController { ...@@ -1509,10 +1474,6 @@ class InAppWebViewController {
///Gets the current WebView options. Returns the options with `null` value if they are not set yet. ///Gets the current WebView options. Returns the options with `null` value if they are not set yet.
Future<InAppWebViewWidgetOptions> getOptions() async { Future<InAppWebViewWidgetOptions> getOptions() async {
Map<String, dynamic> args = <String, dynamic>{}; Map<String, dynamic> args = <String, dynamic>{};
if (_inAppBrowserUuid != null && _inAppBrowser != null) {
_inAppBrowser.throwIsNotOpened();
args.putIfAbsent('uuid', () => _inAppBrowserUuid);
}
InAppWebViewWidgetOptions inAppWebViewWidgetOptions = InAppWebViewWidgetOptions inAppWebViewWidgetOptions =
InAppWebViewWidgetOptions(); InAppWebViewWidgetOptions();
...@@ -1539,10 +1500,6 @@ class InAppWebViewController { ...@@ -1539,10 +1500,6 @@ class InAppWebViewController {
///The object returned from this method will not be updated to reflect any new state. ///The object returned from this method will not be updated to reflect any new state.
Future<WebHistory> getCopyBackForwardList() async { Future<WebHistory> getCopyBackForwardList() async {
Map<String, dynamic> args = <String, dynamic>{}; Map<String, dynamic> args = <String, dynamic>{};
if (_inAppBrowserUuid != null && _inAppBrowser != null) {
_inAppBrowser.throwIsNotOpened();
args.putIfAbsent('uuid', () => _inAppBrowserUuid);
}
Map<dynamic, dynamic> result = Map<dynamic, dynamic> result =
await _channel.invokeMethod('getCopyBackForwardList', args); await _channel.invokeMethod('getCopyBackForwardList', args);
result = result.cast<String, dynamic>(); result = result.cast<String, dynamic>();
...@@ -1568,10 +1525,6 @@ class InAppWebViewController { ...@@ -1568,10 +1525,6 @@ class InAppWebViewController {
///Clears all the webview's cache. ///Clears all the webview's cache.
Future<void> clearCache() async { Future<void> clearCache() async {
Map<String, dynamic> args = <String, dynamic>{}; Map<String, dynamic> args = <String, dynamic>{};
if (_inAppBrowserUuid != null && _inAppBrowser != null) {
_inAppBrowser.throwIsNotOpened();
args.putIfAbsent('uuid', () => _inAppBrowserUuid);
}
await _channel.invokeMethod('clearCache', args); await _channel.invokeMethod('clearCache', args);
} }
...@@ -1585,10 +1538,6 @@ class InAppWebViewController { ...@@ -1585,10 +1538,6 @@ class InAppWebViewController {
Future<void> findAllAsync({@required String find}) async { Future<void> findAllAsync({@required String find}) async {
assert(find != null); assert(find != null);
Map<String, dynamic> args = <String, dynamic>{}; Map<String, dynamic> args = <String, dynamic>{};
if (_inAppBrowserUuid != null && _inAppBrowser != null) {
_inAppBrowser.throwIsNotOpened();
args.putIfAbsent('uuid', () => _inAppBrowserUuid);
}
args.putIfAbsent('find', () => find); args.putIfAbsent('find', () => find);
await _channel.invokeMethod('findAllAsync', args); await _channel.invokeMethod('findAllAsync', args);
} }
...@@ -1601,10 +1550,6 @@ class InAppWebViewController { ...@@ -1601,10 +1550,6 @@ class InAppWebViewController {
Future<void> findNext({@required bool forward}) async { Future<void> findNext({@required bool forward}) async {
assert(forward != null); assert(forward != null);
Map<String, dynamic> args = <String, dynamic>{}; Map<String, dynamic> args = <String, dynamic>{};
if (_inAppBrowserUuid != null && _inAppBrowser != null) {
_inAppBrowser.throwIsNotOpened();
args.putIfAbsent('uuid', () => _inAppBrowserUuid);
}
args.putIfAbsent('forward', () => forward); args.putIfAbsent('forward', () => forward);
await _channel.invokeMethod('findNext', args); await _channel.invokeMethod('findNext', args);
} }
...@@ -1614,10 +1559,6 @@ class InAppWebViewController { ...@@ -1614,10 +1559,6 @@ class InAppWebViewController {
///**NOTE**: on iOS, this is implemented using CSS and Javascript. ///**NOTE**: on iOS, this is implemented using CSS and Javascript.
Future<void> clearMatches() async { Future<void> clearMatches() async {
Map<String, dynamic> args = <String, dynamic>{}; Map<String, dynamic> args = <String, dynamic>{};
if (_inAppBrowserUuid != null && _inAppBrowser != null) {
_inAppBrowser.throwIsNotOpened();
args.putIfAbsent('uuid', () => _inAppBrowserUuid);
}
await _channel.invokeMethod('clearMatches', args); await _channel.invokeMethod('clearMatches', args);
} }
...@@ -1641,10 +1582,6 @@ class InAppWebViewController { ...@@ -1641,10 +1582,6 @@ class InAppWebViewController {
Future<void> scrollTo({@required int x, @required int y}) async { Future<void> scrollTo({@required int x, @required int y}) async {
assert(x != null && y != null); assert(x != null && y != null);
Map<String, dynamic> args = <String, dynamic>{}; Map<String, dynamic> args = <String, dynamic>{};
if (_inAppBrowserUuid != null && _inAppBrowser != null) {
_inAppBrowser.throwIsNotOpened();
args.putIfAbsent('uuid', () => _inAppBrowserUuid);
}
args.putIfAbsent('x', () => x); args.putIfAbsent('x', () => x);
args.putIfAbsent('y', () => y); args.putIfAbsent('y', () => y);
await _channel.invokeMethod('scrollTo', args); await _channel.invokeMethod('scrollTo', args);
...@@ -1658,10 +1595,6 @@ class InAppWebViewController { ...@@ -1658,10 +1595,6 @@ class InAppWebViewController {
Future<void> scrollBy({@required int x, @required int y}) async { Future<void> scrollBy({@required int x, @required int y}) async {
assert(x != null && y != null); assert(x != null && y != null);
Map<String, dynamic> args = <String, dynamic>{}; Map<String, dynamic> args = <String, dynamic>{};
if (_inAppBrowserUuid != null && _inAppBrowser != null) {
_inAppBrowser.throwIsNotOpened();
args.putIfAbsent('uuid', () => _inAppBrowserUuid);
}
args.putIfAbsent('x', () => x); args.putIfAbsent('x', () => x);
args.putIfAbsent('y', () => y); args.putIfAbsent('y', () => y);
await _channel.invokeMethod('scrollBy', args); await _channel.invokeMethod('scrollBy', args);
...@@ -1673,10 +1606,6 @@ class InAppWebViewController { ...@@ -1673,10 +1606,6 @@ class InAppWebViewController {
///On iOS, it is restricted to just this WebView. ///On iOS, it is restricted to just this WebView.
Future<void> pauseTimers() async { Future<void> pauseTimers() async {
Map<String, dynamic> args = <String, dynamic>{}; Map<String, dynamic> args = <String, dynamic>{};
if (_inAppBrowserUuid != null && _inAppBrowser != null) {
_inAppBrowser.throwIsNotOpened();
args.putIfAbsent('uuid', () => _inAppBrowserUuid);
}
await _channel.invokeMethod('pauseTimers', args); await _channel.invokeMethod('pauseTimers', args);
} }
...@@ -1685,10 +1614,6 @@ class InAppWebViewController { ...@@ -1685,10 +1614,6 @@ class InAppWebViewController {
///On iOS, it resumes all layout, parsing, and JavaScript timers to just this WebView. ///On iOS, it resumes all layout, parsing, and JavaScript timers to just this WebView.
Future<void> resumeTimers() async { Future<void> resumeTimers() async {
Map<String, dynamic> args = <String, dynamic>{}; Map<String, dynamic> args = <String, dynamic>{};
if (_inAppBrowserUuid != null && _inAppBrowser != null) {
_inAppBrowser.throwIsNotOpened();
args.putIfAbsent('uuid', () => _inAppBrowserUuid);
}
await _channel.invokeMethod('resumeTimers', args); await _channel.invokeMethod('resumeTimers', args);
} }
...@@ -1697,20 +1622,12 @@ class InAppWebViewController { ...@@ -1697,20 +1622,12 @@ class InAppWebViewController {
///**NOTE**: available on Android 21+. ///**NOTE**: available on Android 21+.
Future<void> printCurrentPage() async { Future<void> printCurrentPage() async {
Map<String, dynamic> args = <String, dynamic>{}; Map<String, dynamic> args = <String, dynamic>{};
if (_inAppBrowserUuid != null && _inAppBrowser != null) {
_inAppBrowser.throwIsNotOpened();
args.putIfAbsent('uuid', () => _inAppBrowserUuid);
}
await _channel.invokeMethod('printCurrentPage', args); await _channel.invokeMethod('printCurrentPage', args);
} }
///Gets the height of the HTML content. ///Gets the height of the HTML content.
Future<int> getContentHeight() async { Future<int> getContentHeight() async {
Map<String, dynamic> args = <String, dynamic>{}; Map<String, dynamic> args = <String, dynamic>{};
if (_inAppBrowserUuid != null && _inAppBrowser != null) {
_inAppBrowser.throwIsNotOpened();
args.putIfAbsent('uuid', () => _inAppBrowserUuid);
}
return await _channel.invokeMethod('getContentHeight', args); return await _channel.invokeMethod('getContentHeight', args);
} }
...@@ -1721,10 +1638,6 @@ class InAppWebViewController { ...@@ -1721,10 +1638,6 @@ class InAppWebViewController {
///**NOTE**: available on Android 21+. ///**NOTE**: available on Android 21+.
Future<void> zoomBy(double zoomFactor) async { Future<void> zoomBy(double zoomFactor) async {
Map<String, dynamic> args = <String, dynamic>{}; Map<String, dynamic> args = <String, dynamic>{};
if (_inAppBrowserUuid != null && _inAppBrowser != null) {
_inAppBrowser.throwIsNotOpened();
args.putIfAbsent('uuid', () => _inAppBrowserUuid);
}
args.putIfAbsent('zoomFactor', () => zoomFactor); args.putIfAbsent('zoomFactor', () => zoomFactor);
return await _channel.invokeMethod('zoomBy', args); return await _channel.invokeMethod('zoomBy', args);
} }
...@@ -1732,10 +1645,6 @@ class InAppWebViewController { ...@@ -1732,10 +1645,6 @@ class InAppWebViewController {
///Gets the current scale of this WebView. ///Gets the current scale of this WebView.
Future<double> getScale() async { Future<double> getScale() async {
Map<String, dynamic> args = <String, dynamic>{}; Map<String, dynamic> args = <String, dynamic>{};
if (_inAppBrowserUuid != null && _inAppBrowser != null) {
_inAppBrowser.throwIsNotOpened();
args.putIfAbsent('uuid', () => _inAppBrowserUuid);
}
return await _channel.invokeMethod('getScale', args); return await _channel.invokeMethod('getScale', args);
} }
...@@ -1766,10 +1675,6 @@ class AndroidInAppWebViewController { ...@@ -1766,10 +1675,6 @@ class AndroidInAppWebViewController {
///**NOTE**: available only on Android 27+. ///**NOTE**: available only on Android 27+.
Future<bool> startSafeBrowsing() async { Future<bool> startSafeBrowsing() async {
Map<String, dynamic> args = <String, dynamic>{}; Map<String, dynamic> args = <String, dynamic>{};
if (_controller._inAppBrowserUuid != null && _controller._inAppBrowser != null) {
_controller._inAppBrowser.throwIsNotOpened();
args.putIfAbsent('uuid', () => _controller._inAppBrowserUuid);
}
return await _controller._channel.invokeMethod('startSafeBrowsing', args); return await _controller._channel.invokeMethod('startSafeBrowsing', args);
} }
...@@ -1791,10 +1696,6 @@ class AndroidInAppWebViewController { ...@@ -1791,10 +1696,6 @@ class AndroidInAppWebViewController {
Future<bool> setSafeBrowsingWhitelist({@required List<String> hosts}) async { Future<bool> setSafeBrowsingWhitelist({@required List<String> hosts}) async {
assert(hosts != null); assert(hosts != null);
Map<String, dynamic> args = <String, dynamic>{}; Map<String, dynamic> args = <String, dynamic>{};
if (_controller._inAppBrowserUuid != null && _controller._inAppBrowser != null) {
_controller._inAppBrowser.throwIsNotOpened();
args.putIfAbsent('uuid', () => _controller._inAppBrowserUuid);
}
args.putIfAbsent('hosts', () => hosts); args.putIfAbsent('hosts', () => hosts);
return await _controller._channel.invokeMethod('setSafeBrowsingWhitelist', args); return await _controller._channel.invokeMethod('setSafeBrowsingWhitelist', args);
} }
...@@ -1804,20 +1705,12 @@ class AndroidInAppWebViewController { ...@@ -1804,20 +1705,12 @@ class AndroidInAppWebViewController {
///**NOTE**: available only on Android 27+. ///**NOTE**: available only on Android 27+.
Future<String> getSafeBrowsingPrivacyPolicyUrl() async { Future<String> getSafeBrowsingPrivacyPolicyUrl() async {
Map<String, dynamic> args = <String, dynamic>{}; Map<String, dynamic> args = <String, dynamic>{};
if (_controller._inAppBrowserUuid != null && _controller._inAppBrowser != null) {
_controller._inAppBrowser.throwIsNotOpened();
args.putIfAbsent('uuid', () => _controller._inAppBrowserUuid);
}
return await _controller._channel.invokeMethod('getSafeBrowsingPrivacyPolicyUrl', args); return await _controller._channel.invokeMethod('getSafeBrowsingPrivacyPolicyUrl', args);
} }
///Clears the SSL preferences table stored in response to proceeding with SSL certificate errors. ///Clears the SSL preferences table stored in response to proceeding with SSL certificate errors.
Future<void> clearSslPreferences() async { Future<void> clearSslPreferences() async {
Map<String, dynamic> args = <String, dynamic>{}; Map<String, dynamic> args = <String, dynamic>{};
if (_controller._inAppBrowserUuid != null && _controller._inAppBrowser != null) {
_controller._inAppBrowser.throwIsNotOpened();
args.putIfAbsent('uuid', () => _controller._inAppBrowserUuid);
}
await _controller._channel.invokeMethod('clearSslPreferences', args); await _controller._channel.invokeMethod('clearSslPreferences', args);
} }
...@@ -1830,10 +1723,6 @@ class AndroidInAppWebViewController { ...@@ -1830,10 +1723,6 @@ class AndroidInAppWebViewController {
///**NOTE**: available on Android 21+. ///**NOTE**: available on Android 21+.
Future<void> clearClientCertPreferences() async { Future<void> clearClientCertPreferences() async {
Map<String, dynamic> args = <String, dynamic>{}; Map<String, dynamic> args = <String, dynamic>{};
if (_controller._inAppBrowserUuid != null && _controller._inAppBrowser != null) {
_controller._inAppBrowser.throwIsNotOpened();
args.putIfAbsent('uuid', () => _controller._inAppBrowserUuid);
}
await _controller._channel.invokeMethod('clearClientCertPreferences', args); await _controller._channel.invokeMethod('clearClientCertPreferences', args);
} }
...@@ -1841,20 +1730,12 @@ class AndroidInAppWebViewController { ...@@ -1841,20 +1730,12 @@ class AndroidInAppWebViewController {
///To pause JavaScript globally, use [pauseTimers()]. To resume WebView, call [resume()]. ///To pause JavaScript globally, use [pauseTimers()]. To resume WebView, call [resume()].
Future<void> pause() async { Future<void> pause() async {
Map<String, dynamic> args = <String, dynamic>{}; Map<String, dynamic> args = <String, dynamic>{};
if (_controller._inAppBrowserUuid != null && _controller._inAppBrowser != null) {
_controller._inAppBrowser.throwIsNotOpened();
args.putIfAbsent('uuid', () => _controller._inAppBrowserUuid);
}
await _controller._channel.invokeMethod('pause', args); await _controller._channel.invokeMethod('pause', args);
} }
///Resumes a WebView after a previous call to [pause()]. ///Resumes a WebView after a previous call to [pause()].
Future<void> resume() async { Future<void> resume() async {
Map<String, dynamic> args = <String, dynamic>{}; Map<String, dynamic> args = <String, dynamic>{};
if (_controller._inAppBrowserUuid != null && _controller._inAppBrowser != null) {
_controller._inAppBrowser.throwIsNotOpened();
args.putIfAbsent('uuid', () => _controller._inAppBrowserUuid);
}
await _controller._channel.invokeMethod('resume', args); await _controller._channel.invokeMethod('resume', args);
} }
...@@ -1863,10 +1744,6 @@ class AndroidInAppWebViewController { ...@@ -1863,10 +1744,6 @@ class AndroidInAppWebViewController {
///the current page may not have changed. Also, there may have been redirects resulting in a different URL to that originally requested. ///the current page may not have changed. Also, there may have been redirects resulting in a different URL to that originally requested.
Future<String> getOriginalUrl() async { Future<String> getOriginalUrl() async {
Map<String, dynamic> args = <String, dynamic>{}; Map<String, dynamic> args = <String, dynamic>{};
if (_controller._inAppBrowserUuid != null && _controller._inAppBrowser != null) {
_controller._inAppBrowser.throwIsNotOpened();
args.putIfAbsent('uuid', () => _controller._inAppBrowserUuid);
}
return await _controller._channel.invokeMethod('getOriginalUrl', args); return await _controller._channel.invokeMethod('getOriginalUrl', args);
} }
...@@ -1883,10 +1760,12 @@ class IOSInAppWebViewController { ...@@ -1883,10 +1760,12 @@ class IOSInAppWebViewController {
///Reloads the current page, performing end-to-end revalidation using cache-validating conditionals if possible. ///Reloads the current page, performing end-to-end revalidation using cache-validating conditionals if possible.
Future<void> reloadFromOrigin() async { Future<void> reloadFromOrigin() async {
Map<String, dynamic> args = <String, dynamic>{}; Map<String, dynamic> args = <String, dynamic>{};
if (_controller._inAppBrowserUuid != null && _controller._inAppBrowser != null) {
_controller._inAppBrowser.throwIsNotOpened();
args.putIfAbsent('uuid', () => _controller._inAppBrowserUuid);
}
await _controller._channel.invokeMethod('reloadFromOrigin', args); await _controller._channel.invokeMethod('reloadFromOrigin', args);
} }
///A Boolean value indicating whether all resources on the page have been loaded over securely encrypted connections.
Future<bool> hasOnlySecureContent() async {
Map<String, dynamic> args = <String, dynamic>{};
return await _controller._channel.invokeMethod('hasOnlySecureContent', args);
}
} }
\ No newline at end of file
...@@ -1048,6 +1048,35 @@ class IOSWKDataDetectorTypes { ...@@ -1048,6 +1048,35 @@ class IOSWKDataDetectorTypes {
int get hashCode => _value.hashCode; int get hashCode => _value.hashCode;
} }
///IOSUIScrollViewDecelerationRate class represents a floating-point value that determines the rate of deceleration after the user lifts their finger.
class IOSUIScrollViewDecelerationRate {
final String _value;
const IOSUIScrollViewDecelerationRate._internal(this._value);
static IOSUIScrollViewDecelerationRate fromValue(String value) {
return ([
"NORMAL",
"FAST"
].contains(value))
? IOSUIScrollViewDecelerationRate._internal(value)
: null;
}
String toValue() => _value;
@override
String toString() => _value;
///The default deceleration rate for a scroll view: `0.998`.
static const NORMAL = const IOSUIScrollViewDecelerationRate._internal("NORMAL");
///A fast deceleration rate for a scroll view: `0.99`.
static const FAST = const IOSUIScrollViewDecelerationRate._internal("FAST");
bool operator ==(value) => value == _value;
@override
int get hashCode => _value.hashCode;
}
///UserPreferredContentMode class represents the content mode to prefer when loading and rendering a webpage. ///UserPreferredContentMode class represents the content mode to prefer when loading and rendering a webpage.
class UserPreferredContentMode { class UserPreferredContentMode {
final int _value; final int _value;
...@@ -2218,4 +2247,71 @@ class IOSWKWebsiteDataRecord { ...@@ -2218,4 +2247,71 @@ class IOSWKWebsiteDataRecord {
String toString() { String toString() {
return toMap().toString(); return toMap().toString();
} }
} }
\ No newline at end of file
///Class representing the [LongPressHitTestResult] type.
class LongPressHitTestResultType {
final int _value;
const LongPressHitTestResultType._internal(this._value);
static LongPressHitTestResultType fromValue(int value) {
if (value != null && [0, 2, 3, 4, 5, 7, 8, 9].contains(value))
return LongPressHitTestResultType._internal(value);
return null;
}
int toValue() => _value;
@override
String toString() {
switch (_value) {
case 2:
return "PHONE_TYPE";
case 3:
return "GEO_TYPE";
case 4:
return "EMAIL_TYPE";
case 5:
return "IMAGE_TYPE";
case 7:
return "SRC_ANCHOR_TYPE";
case 8:
return "SRC_IMAGE_ANCHOR_TYPE";
case 9:
return "EDIT_TEXT_TYPE";
case 0:
default:
return "UNKNOWN_TYPE";
}
}
///Default [LongPressHitTestResult], where the target is unknown.
static const UNKNOWN_TYPE = const LongPressHitTestResultType._internal(0);
///[LongPressHitTestResult] for hitting a phone number.
static const PHONE_TYPE = const LongPressHitTestResultType._internal(2);
///[LongPressHitTestResult] for hitting a map address.
static const GEO_TYPE = const LongPressHitTestResultType._internal(3);
///[LongPressHitTestResult] for hitting an email address.
static const EMAIL_TYPE = const LongPressHitTestResultType._internal(4);
///[LongPressHitTestResult] for hitting an HTML::img tag.
static const IMAGE_TYPE = const LongPressHitTestResultType._internal(5);
///[LongPressHitTestResult] for hitting a HTML::a tag with src=http.
static const SRC_ANCHOR_TYPE = const LongPressHitTestResultType._internal(7);
///[LongPressHitTestResult] for hitting a HTML::a tag with src=http + HTML::img.
static const SRC_IMAGE_ANCHOR_TYPE = const LongPressHitTestResultType._internal(8);
///[LongPressHitTestResult] for hitting an edit text area.
static const EDIT_TEXT_TYPE = const LongPressHitTestResultType._internal(9);
bool operator ==(value) => value == _value;
@override
int get hashCode => _value.hashCode;
}
///LongPressHitTestResult class represents the hit result for hitting an HTML elements. Used by [onLongPressHitTestResult] event.
class LongPressHitTestResult {
///The type of the hit test result.
LongPressHitTestResultType type;
///Additional type-dependant information about the result.
String extra;
LongPressHitTestResult({this.type, this.extra});
}
File mode changed from 100644 to 100755
...@@ -621,6 +621,46 @@ class IOSInAppWebViewOptions ...@@ -621,6 +621,46 @@ class IOSInAppWebViewOptions
///**NOTE**: available on iOS 13.0+. ///**NOTE**: available on iOS 13.0+.
bool automaticallyAdjustsScrollIndicatorInsets; bool automaticallyAdjustsScrollIndicatorInsets;
///A Boolean value indicating whether the view ignores an accessibility request to invert its colors.
///The default value is `false`.
///
///**NOTE**: available on iOS 11.0+.
bool accessibilityIgnoresInvertColors;
///A [IOSUIScrollViewDecelerationRate] value that determines the rate of deceleration after the user lifts their finger.
///The default value is [IOSUIScrollViewDecelerationRate.NORMAL].
IOSUIScrollViewDecelerationRate decelerationRate;
///A Boolean value that determines whether bouncing always occurs when vertical scrolling reaches the end of the content.
///The default value is `false`.
bool alwaysBounceVertical;
///A Boolean value that determines whether bouncing always occurs when horizontal scrolling reaches the end of the content view.
///The default value is `false`.
bool alwaysBounceHorizontal;
///A Boolean value that controls whether the scroll-to-top gesture is enabled.
///The scroll-to-top gesture is a tap on the status bar. When a user makes this gesture,
///the system asks the scroll view closest to the status bar to scroll to the top.
///The default value is `true`.
bool scrollsToTop;
///A Boolean value that determines whether paging is enabled for the scroll view.
///If the value of this property is true, the scroll view stops on multiples of the scroll view’s bounds when the user scrolls.
///The default value is `false`.
bool isPagingEnabled;
///A floating-point value that specifies the maximum scale factor that can be applied to the scroll view's content.
///This value determines how large the content can be scaled.
///It must be greater than the minimum zoom scale for zooming to be enabled.
///The default value is `1.0`.
double maximumZoomScale;
///A floating-point value that specifies the minimum scale factor that can be applied to the scroll view's content.
///This value determines how small the content can be scaled.
///The default value is `1.0`.
double minimumZoomScale;
IOSInAppWebViewOptions( IOSInAppWebViewOptions(
{this.disallowOverScroll = false, {this.disallowOverScroll = false,
this.enableViewportScale = false, this.enableViewportScale = false,
...@@ -635,7 +675,15 @@ class IOSInAppWebViewOptions ...@@ -635,7 +675,15 @@ class IOSInAppWebViewOptions
this.selectionGranularity = IOSWKSelectionGranularity.DYNAMIC, this.selectionGranularity = IOSWKSelectionGranularity.DYNAMIC,
this.dataDetectorTypes = const [IOSWKDataDetectorTypes.NONE], this.dataDetectorTypes = const [IOSWKDataDetectorTypes.NONE],
this.sharedCookiesEnabled = false, this.sharedCookiesEnabled = false,
this.automaticallyAdjustsScrollIndicatorInsets = false}); this.automaticallyAdjustsScrollIndicatorInsets = false,
this.accessibilityIgnoresInvertColors = false,
this.decelerationRate = IOSUIScrollViewDecelerationRate.NORMAL,
this.alwaysBounceVertical = false,
this.alwaysBounceHorizontal = false,
this.scrollsToTop = true,
this.isPagingEnabled = false,
this.maximumZoomScale = 1.0,
this.minimumZoomScale = 1.0});
@override @override
Map<String, dynamic> toMap() { Map<String, dynamic> toMap() {
...@@ -660,7 +708,15 @@ class IOSInAppWebViewOptions ...@@ -660,7 +708,15 @@ class IOSInAppWebViewOptions
"selectionGranularity": selectionGranularity.toValue(), "selectionGranularity": selectionGranularity.toValue(),
"dataDetectorTypes": dataDetectorTypesList, "dataDetectorTypes": dataDetectorTypesList,
"sharedCookiesEnabled": sharedCookiesEnabled, "sharedCookiesEnabled": sharedCookiesEnabled,
"automaticallyAdjustsScrollIndicatorInsets": automaticallyAdjustsScrollIndicatorInsets "automaticallyAdjustsScrollIndicatorInsets": automaticallyAdjustsScrollIndicatorInsets,
"accessibilityIgnoresInvertColors": accessibilityIgnoresInvertColors,
"decelerationRate": decelerationRate.toValue(),
"alwaysBounceVertical": alwaysBounceVertical,
"alwaysBounceHorizontal": alwaysBounceHorizontal,
"scrollsToTop": scrollsToTop,
"isPagingEnabled": isPagingEnabled,
"maximumZoomScale": maximumZoomScale,
"minimumZoomScale": minimumZoomScale
}; };
} }
...@@ -695,6 +751,14 @@ class IOSInAppWebViewOptions ...@@ -695,6 +751,14 @@ class IOSInAppWebViewOptions
options.dataDetectorTypes = dataDetectorTypes; options.dataDetectorTypes = dataDetectorTypes;
options.sharedCookiesEnabled = map["sharedCookiesEnabled"]; options.sharedCookiesEnabled = map["sharedCookiesEnabled"];
options.automaticallyAdjustsScrollIndicatorInsets = map["automaticallyAdjustsScrollIndicatorInsets"]; options.automaticallyAdjustsScrollIndicatorInsets = map["automaticallyAdjustsScrollIndicatorInsets"];
options.accessibilityIgnoresInvertColors = map["accessibilityIgnoresInvertColors"];
options.decelerationRate = IOSUIScrollViewDecelerationRate.fromValue(map["decelerationRate"]);
options.alwaysBounceVertical = map["alwaysBounceVertical"];
options.alwaysBounceHorizontal = map["alwaysBounceHorizontal"];
options.scrollsToTop = map["scrollsToTop"];
options.isPagingEnabled = map["isPagingEnabled"];
options.maximumZoomScale = map["maximumZoomScale"];
options.minimumZoomScale = map["minimumZoomScale"];
return options; return options;
} }
} }
...@@ -752,7 +816,7 @@ class AndroidInAppBrowserOptions implements BrowserOptions, AndroidOptions { ...@@ -752,7 +816,7 @@ class AndroidInAppBrowserOptions implements BrowserOptions, AndroidOptions {
///Set to `false` to not close the InAppBrowser when the user click on the back button and the WebView cannot go back to the history. The default value is `true`. ///Set to `false` to not close the InAppBrowser when the user click on the back button and the WebView cannot go back to the history. The default value is `true`.
bool closeOnCannotGoBack; bool closeOnCannotGoBack;
///Set to `false` to hide the progress bar at the bottom of the toolbar at the top. The default value is `true`. ///Set to `false` to hide the progressz bar at the bottom of the toolbar at the top. The default value is `true`.
bool progressBar; bool progressBar;
AndroidInAppBrowserOptions( AndroidInAppBrowserOptions(
......
File mode changed from 100644 to 100755
File mode changed from 100644 to 100755
File mode changed from 100644 to 100755
File mode changed from 100644 to 100755
File mode changed from 100644 to 100755
File mode changed from 100644 to 100755
File mode changed from 100644 to 100755
File mode changed from 100644 to 100755
File mode changed from 100644 to 100755
File mode changed from 100644 to 100755
File mode changed from 100644 to 100755
File mode changed from 100644 to 100755
File mode changed from 100644 to 100755
File mode changed from 100644 to 100755
File mode changed from 100644 to 100755
File mode changed from 100644 to 100755
File mode changed from 100644 to 100755
File mode changed from 100644 to 100755
File mode changed from 100644 to 100755
File mode changed from 100644 to 100755
File mode changed from 100644 to 100755
File mode changed from 100644 to 100755
File mode changed from 100644 to 100755
File mode changed from 100644 to 100755
File mode changed from 100644 to 100755
File mode changed from 100644 to 100755
File mode changed from 100644 to 100755
...@@ -5,8 +5,8 @@ author: Lorenzo Pichilli <pichillilorenzo@gmail.com> ...@@ -5,8 +5,8 @@ author: Lorenzo Pichilli <pichillilorenzo@gmail.com>
homepage: https://github.com/pichillilorenzo/flutter_inappwebview homepage: https://github.com/pichillilorenzo/flutter_inappwebview
environment: environment:
sdk: ">=2.0.0-dev.68.0 <3.0.0" sdk: ">=2.7.0 <3.0.0"
flutter: ">=1.10.0 <2.0.0" flutter: ">=1.12.13+hotfix.5"
dependencies: dependencies:
flutter: flutter:
......
...@@ -5,5 +5,6 @@ dart tool/env.dart ...@@ -5,5 +5,6 @@ dart tool/env.dart
cd nodejs_server_test_auth_basic_and_ssl cd nodejs_server_test_auth_basic_and_ssl
node index.js & node index.js &
cd ../example cd ../example
flutter clean
flutter driver -t test_driver/app.dart flutter driver -t test_driver/app.dart
kill $(jobs -p) kill $(jobs -p)
\ No newline at end of file
File mode changed from 100644 to 100755
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment