merge
This commit is contained in:
commit
dd9043ccf5
13
CHANGELOG.md
13
CHANGELOG.md
|
@ -7,6 +7,19 @@
|
||||||
- Added support for `onPermissionRequest` event on iOS 15.0+
|
- Added support for `onPermissionRequest` event on iOS 15.0+
|
||||||
- Updated `getMetaThemeColor` on iOS 15.0+
|
- Updated `getMetaThemeColor` on iOS 15.0+
|
||||||
|
|
||||||
|
## 5.4.1+1
|
||||||
|
|
||||||
|
- Fixed Android default context menu over custom context menu on API Level 31+
|
||||||
|
|
||||||
|
## 5.4.1
|
||||||
|
|
||||||
|
- Managed iOS native `detachFromEngine` flutter plugin event and updated `dispose` methods
|
||||||
|
- Updated Android native `HeadlessInAppWebViewManager.dispose` and `HeadlessInAppWebView.dispose` methods
|
||||||
|
|
||||||
|
## 5.4.0+3
|
||||||
|
|
||||||
|
- Fixed Android error in some cases when calling `setServiceWorkerClient` java method on `ServiceWorkerManager` initialization
|
||||||
|
|
||||||
## 5.4.0+2
|
## 5.4.0+2
|
||||||
|
|
||||||
- Fixed Android `ChromeCustomTabsActivity` not responding to the `ActionBroadcastReceiver`
|
- Fixed Android `ChromeCustomTabsActivity` not responding to the `ActionBroadcastReceiver`
|
||||||
|
|
|
@ -59,6 +59,4 @@ Add `flutter_inappwebview` as a [dependency in your pubspec.yaml file](https://f
|
||||||
|
|
||||||
## Support
|
## Support
|
||||||
|
|
||||||
Use the [develop branch](https://github.com/pichillilorenzo/flutter_inappwebview/tree/develop) when making a pull request.
|
|
||||||
|
|
||||||
Did you find this plugin useful? Please consider to [make a donation](https://inappwebview.dev/donate/) to help improve it!
|
Did you find this plugin useful? Please consider to [make a donation](https://inappwebview.dev/donate/) to help improve it!
|
|
@ -2,8 +2,8 @@ package com.pichillilorenzo.flutter_inappwebview;
|
||||||
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
public interface IWebViewSettings<T> {
|
public interface ISettings<T> {
|
||||||
public IWebViewSettings parse(Map<String, Object> settings);
|
public ISettings parse(Map<String, Object> settings);
|
||||||
public Map<String, Object> toMap();
|
public Map<String, Object> toMap();
|
||||||
public Map<String, Object> getRealSettings(T obj);
|
public Map<String, Object> getRealSettings(T obj);
|
||||||
}
|
}
|
|
@ -38,50 +38,6 @@ public class ServiceWorkerManager implements MethodChannel.MethodCallHandler {
|
||||||
channel.setMethodCallHandler(this);
|
channel.setMethodCallHandler(this);
|
||||||
if (WebViewFeature.isFeatureSupported(WebViewFeature.SERVICE_WORKER_BASIC_USAGE)) {
|
if (WebViewFeature.isFeatureSupported(WebViewFeature.SERVICE_WORKER_BASIC_USAGE)) {
|
||||||
serviceWorkerController = ServiceWorkerControllerCompat.getInstance();
|
serviceWorkerController = ServiceWorkerControllerCompat.getInstance();
|
||||||
serviceWorkerController.setServiceWorkerClient(new ServiceWorkerClientCompat() {
|
|
||||||
@Nullable
|
|
||||||
@Override
|
|
||||||
public WebResourceResponse shouldInterceptRequest(@NonNull WebResourceRequest request) {
|
|
||||||
final Map<String, Object> obj = new HashMap<>();
|
|
||||||
obj.put("url", request.getUrl().toString());
|
|
||||||
obj.put("method", request.getMethod());
|
|
||||||
obj.put("headers", request.getRequestHeaders());
|
|
||||||
obj.put("isForMainFrame", request.isForMainFrame());
|
|
||||||
obj.put("hasGesture", request.hasGesture());
|
|
||||||
obj.put("isRedirect", request.isRedirect());
|
|
||||||
|
|
||||||
Util.WaitFlutterResult flutterResult;
|
|
||||||
try {
|
|
||||||
flutterResult = Util.invokeMethodAndWait(channel, "shouldInterceptRequest", obj);
|
|
||||||
} catch (InterruptedException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (flutterResult.error != null) {
|
|
||||||
Log.e(LOG_TAG, flutterResult.error);
|
|
||||||
}
|
|
||||||
else if (flutterResult.result != null) {
|
|
||||||
Map<String, Object> res = (Map<String, Object>) flutterResult.result;
|
|
||||||
String contentType = (String) res.get("contentType");
|
|
||||||
String contentEncoding = (String) res.get("contentEncoding");
|
|
||||||
byte[] data = (byte[]) res.get("data");
|
|
||||||
Map<String, String> responseHeaders = (Map<String, String>) res.get("headers");
|
|
||||||
Integer statusCode = (Integer) res.get("statusCode");
|
|
||||||
String reasonPhrase = (String) res.get("reasonPhrase");
|
|
||||||
|
|
||||||
ByteArrayInputStream inputStream = (data != null) ? new ByteArrayInputStream(data) : null;
|
|
||||||
|
|
||||||
if ((responseHeaders == null && statusCode == null && reasonPhrase == null) || Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
|
|
||||||
return new WebResourceResponse(contentType, contentEncoding, inputStream);
|
|
||||||
} else {
|
|
||||||
return new WebResourceResponse(contentType, contentEncoding, statusCode, reasonPhrase, responseHeaders, inputStream);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} else {
|
} else {
|
||||||
serviceWorkerController = null;
|
serviceWorkerController = null;
|
||||||
}
|
}
|
||||||
|
@ -92,6 +48,13 @@ public class ServiceWorkerManager implements MethodChannel.MethodCallHandler {
|
||||||
ServiceWorkerWebSettingsCompat serviceWorkerWebSettings = (serviceWorkerController != null) ? serviceWorkerController.getServiceWorkerWebSettings() : null;
|
ServiceWorkerWebSettingsCompat serviceWorkerWebSettings = (serviceWorkerController != null) ? serviceWorkerController.getServiceWorkerWebSettings() : null;
|
||||||
|
|
||||||
switch (call.method) {
|
switch (call.method) {
|
||||||
|
case "setServiceWorkerClient":
|
||||||
|
{
|
||||||
|
Boolean isNull = (Boolean) call.argument("isNull");
|
||||||
|
setServiceWorkerClient(isNull);
|
||||||
|
}
|
||||||
|
result.success(true);
|
||||||
|
break;
|
||||||
case "getAllowContentAccess":
|
case "getAllowContentAccess":
|
||||||
if (serviceWorkerWebSettings != null && WebViewFeature.isFeatureSupported(WebViewFeature.SERVICE_WORKER_CONTENT_ACCESS)) {
|
if (serviceWorkerWebSettings != null && WebViewFeature.isFeatureSupported(WebViewFeature.SERVICE_WORKER_CONTENT_ACCESS)) {
|
||||||
result.success(serviceWorkerWebSettings.getAllowContentAccess());
|
result.success(serviceWorkerWebSettings.getAllowContentAccess());
|
||||||
|
@ -153,8 +116,61 @@ public class ServiceWorkerManager implements MethodChannel.MethodCallHandler {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void setServiceWorkerClient(Boolean isNull) {
|
||||||
|
if (serviceWorkerController != null) {
|
||||||
|
serviceWorkerController.setServiceWorkerClient(isNull ? null : new ServiceWorkerClientCompat() {
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public WebResourceResponse shouldInterceptRequest(@NonNull WebResourceRequest request) {
|
||||||
|
final Map<String, Object> obj = new HashMap<>();
|
||||||
|
obj.put("url", request.getUrl().toString());
|
||||||
|
obj.put("method", request.getMethod());
|
||||||
|
obj.put("headers", request.getRequestHeaders());
|
||||||
|
obj.put("isForMainFrame", request.isForMainFrame());
|
||||||
|
obj.put("hasGesture", request.hasGesture());
|
||||||
|
obj.put("isRedirect", request.isRedirect());
|
||||||
|
|
||||||
|
Util.WaitFlutterResult flutterResult;
|
||||||
|
try {
|
||||||
|
flutterResult = Util.invokeMethodAndWait(channel, "shouldInterceptRequest", obj);
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (flutterResult.error != null) {
|
||||||
|
Log.e(LOG_TAG, flutterResult.error);
|
||||||
|
}
|
||||||
|
else if (flutterResult.result != null) {
|
||||||
|
Map<String, Object> res = (Map<String, Object>) flutterResult.result;
|
||||||
|
String contentType = (String) res.get("contentType");
|
||||||
|
String contentEncoding = (String) res.get("contentEncoding");
|
||||||
|
byte[] data = (byte[]) res.get("data");
|
||||||
|
Map<String, String> responseHeaders = (Map<String, String>) res.get("headers");
|
||||||
|
Integer statusCode = (Integer) res.get("statusCode");
|
||||||
|
String reasonPhrase = (String) res.get("reasonPhrase");
|
||||||
|
|
||||||
|
ByteArrayInputStream inputStream = (data != null) ? new ByteArrayInputStream(data) : null;
|
||||||
|
|
||||||
|
if ((responseHeaders == null && statusCode == null && reasonPhrase == null) || Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
|
||||||
|
return new WebResourceResponse(contentType, contentEncoding, inputStream);
|
||||||
|
} else {
|
||||||
|
return new WebResourceResponse(contentType, contentEncoding, statusCode, reasonPhrase, responseHeaders, inputStream);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void dispose() {
|
public void dispose() {
|
||||||
channel.setMethodCallHandler(null);
|
channel.setMethodCallHandler(null);
|
||||||
|
if (serviceWorkerController != null) {
|
||||||
|
serviceWorkerController.setServiceWorkerClient(null);
|
||||||
|
serviceWorkerController = null;
|
||||||
|
}
|
||||||
plugin = null;
|
plugin = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,14 +7,14 @@ import androidx.browser.customtabs.CustomTabsIntent;
|
||||||
import androidx.browser.trusted.ScreenOrientation;
|
import androidx.browser.trusted.ScreenOrientation;
|
||||||
import androidx.browser.trusted.TrustedWebActivityDisplayMode;
|
import androidx.browser.trusted.TrustedWebActivityDisplayMode;
|
||||||
|
|
||||||
import com.pichillilorenzo.flutter_inappwebview.IWebViewSettings;
|
import com.pichillilorenzo.flutter_inappwebview.ISettings;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
public class ChromeCustomTabsSettings implements IWebViewSettings<ChromeCustomTabsActivity> {
|
public class ChromeCustomTabsSettings implements ISettings<ChromeCustomTabsActivity> {
|
||||||
|
|
||||||
final static String LOG_TAG = "ChromeCustomTabsSettings";
|
final static String LOG_TAG = "ChromeCustomTabsSettings";
|
||||||
|
|
||||||
|
|
|
@ -110,12 +110,16 @@ public class HeadlessInAppWebView implements MethodChannel.MethodCallHandler {
|
||||||
public void dispose() {
|
public void dispose() {
|
||||||
channel.setMethodCallHandler(null);
|
channel.setMethodCallHandler(null);
|
||||||
HeadlessInAppWebViewManager.webViews.remove(id);
|
HeadlessInAppWebViewManager.webViews.remove(id);
|
||||||
ViewGroup contentView = (ViewGroup) plugin.activity.findViewById(android.R.id.content);
|
if (plugin != null) {
|
||||||
ViewGroup mainView = (ViewGroup) (contentView).getChildAt(0);
|
ViewGroup contentView = (ViewGroup) plugin.activity.findViewById(android.R.id.content);
|
||||||
if (mainView != null) {
|
ViewGroup mainView = (ViewGroup) (contentView).getChildAt(0);
|
||||||
mainView.removeView(flutterWebView.getView());
|
if (mainView != null && flutterWebView != null) {
|
||||||
|
mainView.removeView(flutterWebView.getView());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (flutterWebView != null) {
|
||||||
|
flutterWebView.dispose();
|
||||||
}
|
}
|
||||||
flutterWebView.dispose();
|
|
||||||
flutterWebView = null;
|
flutterWebView = null;
|
||||||
plugin = null;
|
plugin = null;
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,7 +26,9 @@ import androidx.annotation.Nullable;
|
||||||
import com.pichillilorenzo.flutter_inappwebview.InAppWebViewFlutterPlugin;
|
import com.pichillilorenzo.flutter_inappwebview.InAppWebViewFlutterPlugin;
|
||||||
import com.pichillilorenzo.flutter_inappwebview.in_app_webview.FlutterWebView;
|
import com.pichillilorenzo.flutter_inappwebview.in_app_webview.FlutterWebView;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import io.flutter.plugin.common.MethodCall;
|
import io.flutter.plugin.common.MethodCall;
|
||||||
|
@ -77,6 +79,10 @@ public class HeadlessInAppWebViewManager implements MethodChannel.MethodCallHand
|
||||||
|
|
||||||
public void dispose() {
|
public void dispose() {
|
||||||
channel.setMethodCallHandler(null);
|
channel.setMethodCallHandler(null);
|
||||||
|
Collection<HeadlessInAppWebView> headlessInAppWebViews = webViews.values();
|
||||||
|
for (HeadlessInAppWebView headlessInAppWebView : headlessInAppWebViews) {
|
||||||
|
headlessInAppWebView.dispose();
|
||||||
|
}
|
||||||
webViews.clear();
|
webViews.clear();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,13 +2,13 @@ package com.pichillilorenzo.flutter_inappwebview.in_app_browser;
|
||||||
|
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
import com.pichillilorenzo.flutter_inappwebview.IWebViewSettings;
|
import com.pichillilorenzo.flutter_inappwebview.ISettings;
|
||||||
import com.pichillilorenzo.flutter_inappwebview.R;
|
import com.pichillilorenzo.flutter_inappwebview.R;
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
public class InAppBrowserSettings implements IWebViewSettings<InAppBrowserActivity> {
|
public class InAppBrowserSettings implements ISettings<InAppBrowserActivity> {
|
||||||
|
|
||||||
public static final String LOG_TAG = "InAppBrowserSettings";
|
public static final String LOG_TAG = "InAppBrowserSettings";
|
||||||
|
|
||||||
|
|
|
@ -1,16 +1,16 @@
|
||||||
package com.pichillilorenzo.flutter_inappwebview.in_app_webview;
|
package com.pichillilorenzo.flutter_inappwebview.in_app_webview;
|
||||||
|
|
||||||
import com.pichillilorenzo.flutter_inappwebview.IWebViewSettings;
|
import com.pichillilorenzo.flutter_inappwebview.ISettings;
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
public class ContextMenuOptions implements IWebViewSettings<Object> {
|
public class ContextMenuSettings implements ISettings<Object> {
|
||||||
public static final String LOG_TAG = "ContextMenuOptions";
|
public static final String LOG_TAG = "ContextMenuOptions";
|
||||||
|
|
||||||
public Boolean hideDefaultSystemContextMenuItems = false;
|
public Boolean hideDefaultSystemContextMenuItems = false;
|
||||||
|
|
||||||
public ContextMenuOptions parse(Map<String, Object> options) {
|
public ContextMenuSettings parse(Map<String, Object> options) {
|
||||||
for (Map.Entry<String, Object> pair : options.entrySet()) {
|
for (Map.Entry<String, Object> pair : options.entrySet()) {
|
||||||
String key = pair.getKey();
|
String key = pair.getKey();
|
||||||
Object value = pair.getValue();
|
Object value = pair.getValue();
|
|
@ -54,8 +54,6 @@ import androidx.webkit.WebViewCompat;
|
||||||
import androidx.webkit.WebViewFeature;
|
import androidx.webkit.WebViewFeature;
|
||||||
|
|
||||||
import com.pichillilorenzo.flutter_inappwebview.InAppWebViewFlutterPlugin;
|
import com.pichillilorenzo.flutter_inappwebview.InAppWebViewFlutterPlugin;
|
||||||
import com.pichillilorenzo.flutter_inappwebview.types.DownloadStartRequest;
|
|
||||||
import com.pichillilorenzo.flutter_inappwebview.types.InAppWebViewInterface;
|
|
||||||
import com.pichillilorenzo.flutter_inappwebview.JavaScriptBridgeInterface;
|
import com.pichillilorenzo.flutter_inappwebview.JavaScriptBridgeInterface;
|
||||||
import com.pichillilorenzo.flutter_inappwebview.R;
|
import com.pichillilorenzo.flutter_inappwebview.R;
|
||||||
import com.pichillilorenzo.flutter_inappwebview.Util;
|
import com.pichillilorenzo.flutter_inappwebview.Util;
|
||||||
|
@ -76,6 +74,8 @@ import com.pichillilorenzo.flutter_inappwebview.plugin_scripts_js.PrintJS;
|
||||||
import com.pichillilorenzo.flutter_inappwebview.plugin_scripts_js.PromisePolyfillJS;
|
import com.pichillilorenzo.flutter_inappwebview.plugin_scripts_js.PromisePolyfillJS;
|
||||||
import com.pichillilorenzo.flutter_inappwebview.pull_to_refresh.PullToRefreshLayout;
|
import com.pichillilorenzo.flutter_inappwebview.pull_to_refresh.PullToRefreshLayout;
|
||||||
import com.pichillilorenzo.flutter_inappwebview.types.ContentWorld;
|
import com.pichillilorenzo.flutter_inappwebview.types.ContentWorld;
|
||||||
|
import com.pichillilorenzo.flutter_inappwebview.types.DownloadStartRequest;
|
||||||
|
import com.pichillilorenzo.flutter_inappwebview.types.InAppWebViewInterface;
|
||||||
import com.pichillilorenzo.flutter_inappwebview.types.PluginScript;
|
import com.pichillilorenzo.flutter_inappwebview.types.PluginScript;
|
||||||
import com.pichillilorenzo.flutter_inappwebview.types.PreferredContentModeOptionType;
|
import com.pichillilorenzo.flutter_inappwebview.types.PreferredContentModeOptionType;
|
||||||
import com.pichillilorenzo.flutter_inappwebview.types.URLRequest;
|
import com.pichillilorenzo.flutter_inappwebview.types.URLRequest;
|
||||||
|
@ -1356,8 +1356,16 @@ final public class InAppWebView extends InputAwareWebView implements InAppWebVie
|
||||||
}
|
}
|
||||||
|
|
||||||
Menu actionMenu = actionMode.getMenu();
|
Menu actionMenu = actionMode.getMenu();
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||||
|
actionMode.hide(3000);
|
||||||
|
}
|
||||||
|
List<MenuItem> defaultMenuItems = new ArrayList<>();
|
||||||
|
for (int i = 0; i < actionMenu.size(); i++) {
|
||||||
|
defaultMenuItems.add(actionMenu.getItem(i));
|
||||||
|
}
|
||||||
|
actionMenu.clear();
|
||||||
|
actionMode.finish();
|
||||||
if (customSettings.disableContextMenu) {
|
if (customSettings.disableContextMenu) {
|
||||||
actionMenu.clear();
|
|
||||||
return actionMode;
|
return actionMode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1367,19 +1375,18 @@ final public class InAppWebView extends InputAwareWebView implements InAppWebVie
|
||||||
LinearLayout menuItemListLayout = (LinearLayout) horizontalScrollView.getChildAt(0);
|
LinearLayout menuItemListLayout = (LinearLayout) horizontalScrollView.getChildAt(0);
|
||||||
|
|
||||||
List<Map<String, Object>> customMenuItems = new ArrayList<>();
|
List<Map<String, Object>> customMenuItems = new ArrayList<>();
|
||||||
ContextMenuOptions contextMenuOptions = new ContextMenuOptions();
|
ContextMenuSettings contextMenuSettings = new ContextMenuSettings();
|
||||||
if (contextMenu != null) {
|
if (contextMenu != null) {
|
||||||
customMenuItems = (List<Map<String, Object>>) contextMenu.get("menuItems");
|
customMenuItems = (List<Map<String, Object>>) contextMenu.get("menuItems");
|
||||||
Map<String, Object> contextMenuOptionsMap = (Map<String, Object>) contextMenu.get("options");
|
Map<String, Object> contextMenuSettingsMap = (Map<String, Object>) contextMenu.get("settings");
|
||||||
if (contextMenuOptionsMap != null) {
|
if (contextMenuSettingsMap != null) {
|
||||||
contextMenuOptions.parse(contextMenuOptionsMap);
|
contextMenuSettings.parse(contextMenuSettingsMap);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
customMenuItems = customMenuItems == null ? new ArrayList<Map<String, Object>>() : customMenuItems;
|
customMenuItems = customMenuItems == null ? new ArrayList<Map<String, Object>>() : customMenuItems;
|
||||||
|
|
||||||
if (contextMenuOptions.hideDefaultSystemContextMenuItems == null || !contextMenuOptions.hideDefaultSystemContextMenuItems) {
|
if (contextMenuSettings.hideDefaultSystemContextMenuItems == null || !contextMenuSettings.hideDefaultSystemContextMenuItems) {
|
||||||
for (int i = 0; i < actionMenu.size(); i++) {
|
for (final MenuItem menuItem : defaultMenuItems) {
|
||||||
final MenuItem menuItem = actionMenu.getItem(i);
|
|
||||||
final int itemId = menuItem.getItemId();
|
final int itemId = menuItem.getItemId();
|
||||||
final String itemTitle = menuItem.getTitle().toString();
|
final String itemTitle = menuItem.getTitle().toString();
|
||||||
|
|
||||||
|
@ -1393,6 +1400,7 @@ final public class InAppWebView extends InputAwareWebView implements InAppWebVie
|
||||||
callback.onActionItemClicked(actionMode, menuItem);
|
callback.onActionItemClicked(actionMode, menuItem);
|
||||||
|
|
||||||
Map<String, Object> obj = new HashMap<>();
|
Map<String, Object> obj = new HashMap<>();
|
||||||
|
obj.put("id", itemId);
|
||||||
obj.put("androidId", itemId);
|
obj.put("androidId", itemId);
|
||||||
obj.put("iosId", null);
|
obj.put("iosId", null);
|
||||||
obj.put("title", itemTitle);
|
obj.put("title", itemTitle);
|
||||||
|
@ -1406,7 +1414,7 @@ final public class InAppWebView extends InputAwareWebView implements InAppWebVie
|
||||||
}
|
}
|
||||||
|
|
||||||
for (final Map<String, Object> menuItem : customMenuItems) {
|
for (final Map<String, Object> menuItem : customMenuItems) {
|
||||||
final int itemId = (int) menuItem.get("androidId");
|
final int itemId = (int) menuItem.get("id");
|
||||||
final String itemTitle = (String) menuItem.get("title");
|
final String itemTitle = (String) menuItem.get("title");
|
||||||
TextView text = (TextView) LayoutInflater.from(this.getContext())
|
TextView text = (TextView) LayoutInflater.from(this.getContext())
|
||||||
.inflate(R.layout.floating_action_mode_item, this, false);
|
.inflate(R.layout.floating_action_mode_item, this, false);
|
||||||
|
@ -1417,6 +1425,7 @@ final public class InAppWebView extends InputAwareWebView implements InAppWebVie
|
||||||
hideContextMenu();
|
hideContextMenu();
|
||||||
|
|
||||||
Map<String, Object> obj = new HashMap<>();
|
Map<String, Object> obj = new HashMap<>();
|
||||||
|
obj.put("id", itemId);
|
||||||
obj.put("androidId", itemId);
|
obj.put("androidId", itemId);
|
||||||
obj.put("iosId", null);
|
obj.put("iosId", null);
|
||||||
obj.put("title", itemTitle);
|
obj.put("title", itemTitle);
|
||||||
|
@ -1456,7 +1465,6 @@ final public class InAppWebView extends InputAwareWebView implements InAppWebVie
|
||||||
checkContextMenuShouldBeClosedTask.run();
|
checkContextMenuShouldBeClosedTask.run();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
actionMenu.clear();
|
|
||||||
|
|
||||||
return actionMode;
|
return actionMode;
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,7 @@ import android.webkit.WebSettings;
|
||||||
|
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
import com.pichillilorenzo.flutter_inappwebview.IWebViewSettings;
|
import com.pichillilorenzo.flutter_inappwebview.ISettings;
|
||||||
import com.pichillilorenzo.flutter_inappwebview.types.InAppWebViewInterface;
|
import com.pichillilorenzo.flutter_inappwebview.types.InAppWebViewInterface;
|
||||||
import com.pichillilorenzo.flutter_inappwebview.types.PreferredContentModeOptionType;
|
import com.pichillilorenzo.flutter_inappwebview.types.PreferredContentModeOptionType;
|
||||||
|
|
||||||
|
@ -18,7 +18,7 @@ import java.util.Map;
|
||||||
import static android.webkit.WebSettings.LayoutAlgorithm.NARROW_COLUMNS;
|
import static android.webkit.WebSettings.LayoutAlgorithm.NARROW_COLUMNS;
|
||||||
import static android.webkit.WebSettings.LayoutAlgorithm.NORMAL;
|
import static android.webkit.WebSettings.LayoutAlgorithm.NORMAL;
|
||||||
|
|
||||||
public class InAppWebViewSettings implements IWebViewSettings<InAppWebViewInterface> {
|
public class InAppWebViewSettings implements ISettings<InAppWebViewInterface> {
|
||||||
|
|
||||||
public static final String LOG_TAG = "InAppWebViewSettings";
|
public static final String LOG_TAG = "InAppWebViewSettings";
|
||||||
|
|
||||||
|
|
|
@ -2,12 +2,12 @@ package com.pichillilorenzo.flutter_inappwebview.pull_to_refresh;
|
||||||
|
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
import com.pichillilorenzo.flutter_inappwebview.IWebViewSettings;
|
import com.pichillilorenzo.flutter_inappwebview.ISettings;
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
public class PullToRefreshSettings implements IWebViewSettings<PullToRefreshLayout> {
|
public class PullToRefreshSettings implements ISettings<PullToRefreshLayout> {
|
||||||
public static final String LOG_TAG = "PullToRefreshSettings";
|
public static final String LOG_TAG = "PullToRefreshSettings";
|
||||||
|
|
||||||
public Boolean enabled = true;
|
public Boolean enabled = true;
|
||||||
|
|
|
@ -25,6 +25,7 @@ public class NavigationAction {
|
||||||
navigationActionMap.put("navigationType", null);
|
navigationActionMap.put("navigationType", null);
|
||||||
navigationActionMap.put("sourceFrame", null);
|
navigationActionMap.put("sourceFrame", null);
|
||||||
navigationActionMap.put("targetFrame", null);
|
navigationActionMap.put("targetFrame", null);
|
||||||
|
navigationActionMap.put("shouldPerformDownload", null);
|
||||||
return navigationActionMap;
|
return navigationActionMap;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -77,7 +77,7 @@ void main() {
|
||||||
AndroidInAppWebViewController.setWebContentsDebuggingEnabled(true);
|
AndroidInAppWebViewController.setWebContentsDebuggingEnabled(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
group('InAppWebView', () {
|
group('InAppWebView', () {
|
||||||
testWidgets('initialUrlRequest', (WidgetTester tester) async {
|
testWidgets('initialUrlRequest', (WidgetTester tester) async {
|
||||||
final Completer controllerCompleter = Completer<InAppWebViewController>();
|
final Completer controllerCompleter = Completer<InAppWebViewController>();
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
|
@ -1709,13 +1709,14 @@ void main() {
|
||||||
|
|
||||||
group('intercept ajax request', () {
|
group('intercept ajax request', () {
|
||||||
testWidgets('send string data', (WidgetTester tester) async {
|
testWidgets('send string data', (WidgetTester tester) async {
|
||||||
final Completer controllerCompleter = Completer<InAppWebViewController>();
|
final Completer controllerCompleter =
|
||||||
|
Completer<InAppWebViewController>();
|
||||||
final Completer shouldInterceptAjaxPostRequestCompleter =
|
final Completer shouldInterceptAjaxPostRequestCompleter =
|
||||||
Completer<void>();
|
Completer<void>();
|
||||||
final Completer<Map<String, dynamic>> onAjaxReadyStateChangeCompleter =
|
final Completer<Map<String, dynamic>> onAjaxReadyStateChangeCompleter =
|
||||||
Completer<Map<String, dynamic>>();
|
Completer<Map<String, dynamic>>();
|
||||||
final Completer<Map<String, dynamic>> onAjaxProgressCompleter =
|
final Completer<Map<String, dynamic>> onAjaxProgressCompleter =
|
||||||
Completer<Map<String, dynamic>>();
|
Completer<Map<String, dynamic>>();
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
Directionality(
|
Directionality(
|
||||||
textDirection: TextDirection.ltr,
|
textDirection: TextDirection.ltr,
|
||||||
|
@ -1745,9 +1746,9 @@ void main() {
|
||||||
"""),
|
"""),
|
||||||
initialOptions: InAppWebViewGroupOptions(
|
initialOptions: InAppWebViewGroupOptions(
|
||||||
crossPlatform: InAppWebViewOptions(
|
crossPlatform: InAppWebViewOptions(
|
||||||
clearCache: true,
|
clearCache: true,
|
||||||
useShouldInterceptAjaxRequest: true,
|
useShouldInterceptAjaxRequest: true,
|
||||||
)),
|
)),
|
||||||
onWebViewCreated: (controller) {
|
onWebViewCreated: (controller) {
|
||||||
controllerCompleter.complete(controller);
|
controllerCompleter.complete(controller);
|
||||||
},
|
},
|
||||||
|
@ -1780,9 +1781,9 @@ void main() {
|
||||||
|
|
||||||
await shouldInterceptAjaxPostRequestCompleter.future;
|
await shouldInterceptAjaxPostRequestCompleter.future;
|
||||||
final Map<String, dynamic> onAjaxReadyStateChangeValue =
|
final Map<String, dynamic> onAjaxReadyStateChangeValue =
|
||||||
await onAjaxReadyStateChangeCompleter.future;
|
await onAjaxReadyStateChangeCompleter.future;
|
||||||
final Map<String, dynamic> onAjaxProgressValue =
|
final Map<String, dynamic> onAjaxProgressValue =
|
||||||
await onAjaxProgressCompleter.future;
|
await onAjaxProgressCompleter.future;
|
||||||
|
|
||||||
expect(
|
expect(
|
||||||
mapEquals(onAjaxReadyStateChangeValue,
|
mapEquals(onAjaxReadyStateChangeValue,
|
||||||
|
@ -1795,13 +1796,14 @@ void main() {
|
||||||
});
|
});
|
||||||
|
|
||||||
testWidgets('send json data', (WidgetTester tester) async {
|
testWidgets('send json data', (WidgetTester tester) async {
|
||||||
final Completer controllerCompleter = Completer<InAppWebViewController>();
|
final Completer controllerCompleter =
|
||||||
|
Completer<InAppWebViewController>();
|
||||||
final Completer shouldInterceptAjaxPostRequestCompleter =
|
final Completer shouldInterceptAjaxPostRequestCompleter =
|
||||||
Completer<void>();
|
Completer<void>();
|
||||||
final Completer<Map<String, dynamic>> onAjaxReadyStateChangeCompleter =
|
final Completer<Map<String, dynamic>> onAjaxReadyStateChangeCompleter =
|
||||||
Completer<Map<String, dynamic>>();
|
Completer<Map<String, dynamic>>();
|
||||||
final Completer<Map<String, dynamic>> onAjaxProgressCompleter =
|
final Completer<Map<String, dynamic>> onAjaxProgressCompleter =
|
||||||
Completer<Map<String, dynamic>>();
|
Completer<Map<String, dynamic>>();
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
Directionality(
|
Directionality(
|
||||||
textDirection: TextDirection.ltr,
|
textDirection: TextDirection.ltr,
|
||||||
|
@ -1835,15 +1837,16 @@ void main() {
|
||||||
"""),
|
"""),
|
||||||
initialOptions: InAppWebViewGroupOptions(
|
initialOptions: InAppWebViewGroupOptions(
|
||||||
crossPlatform: InAppWebViewOptions(
|
crossPlatform: InAppWebViewOptions(
|
||||||
clearCache: true,
|
clearCache: true,
|
||||||
useShouldInterceptAjaxRequest: true,
|
useShouldInterceptAjaxRequest: true,
|
||||||
)),
|
)),
|
||||||
onWebViewCreated: (controller) {
|
onWebViewCreated: (controller) {
|
||||||
controllerCompleter.complete(controller);
|
controllerCompleter.complete(controller);
|
||||||
},
|
},
|
||||||
shouldInterceptAjaxRequest: (controller, ajaxRequest) async {
|
shouldInterceptAjaxRequest: (controller, ajaxRequest) async {
|
||||||
String data = ajaxRequest.data;
|
String data = ajaxRequest.data;
|
||||||
assert(data.contains('"firstname":"Foo"') && data.contains('"lastname":"Bar"'));
|
assert(data.contains('"firstname":"Foo"') &&
|
||||||
|
data.contains('"lastname":"Bar"'));
|
||||||
|
|
||||||
ajaxRequest.responseType = 'json';
|
ajaxRequest.responseType = 'json';
|
||||||
ajaxRequest.data = '{"firstname": "Foo2", "lastname": "Bar2"}';
|
ajaxRequest.data = '{"firstname": "Foo2", "lastname": "Bar2"}';
|
||||||
|
@ -1871,9 +1874,9 @@ void main() {
|
||||||
|
|
||||||
await shouldInterceptAjaxPostRequestCompleter.future;
|
await shouldInterceptAjaxPostRequestCompleter.future;
|
||||||
final Map<String, dynamic> onAjaxReadyStateChangeValue =
|
final Map<String, dynamic> onAjaxReadyStateChangeValue =
|
||||||
await onAjaxReadyStateChangeCompleter.future;
|
await onAjaxReadyStateChangeCompleter.future;
|
||||||
final Map<String, dynamic> onAjaxProgressValue =
|
final Map<String, dynamic> onAjaxProgressValue =
|
||||||
await onAjaxProgressCompleter.future;
|
await onAjaxProgressCompleter.future;
|
||||||
|
|
||||||
expect(
|
expect(
|
||||||
mapEquals(onAjaxReadyStateChangeValue,
|
mapEquals(onAjaxReadyStateChangeValue,
|
||||||
|
@ -1886,13 +1889,14 @@ void main() {
|
||||||
});
|
});
|
||||||
|
|
||||||
testWidgets('send URLSearchParams data', (WidgetTester tester) async {
|
testWidgets('send URLSearchParams data', (WidgetTester tester) async {
|
||||||
final Completer controllerCompleter = Completer<InAppWebViewController>();
|
final Completer controllerCompleter =
|
||||||
|
Completer<InAppWebViewController>();
|
||||||
final Completer shouldInterceptAjaxPostRequestCompleter =
|
final Completer shouldInterceptAjaxPostRequestCompleter =
|
||||||
Completer<void>();
|
Completer<void>();
|
||||||
final Completer<Map<String, dynamic>> onAjaxReadyStateChangeCompleter =
|
final Completer<Map<String, dynamic>> onAjaxReadyStateChangeCompleter =
|
||||||
Completer<Map<String, dynamic>>();
|
Completer<Map<String, dynamic>>();
|
||||||
final Completer<Map<String, dynamic>> onAjaxProgressCompleter =
|
final Completer<Map<String, dynamic>> onAjaxProgressCompleter =
|
||||||
Completer<Map<String, dynamic>>();
|
Completer<Map<String, dynamic>>();
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
Directionality(
|
Directionality(
|
||||||
textDirection: TextDirection.ltr,
|
textDirection: TextDirection.ltr,
|
||||||
|
@ -1924,9 +1928,9 @@ void main() {
|
||||||
"""),
|
"""),
|
||||||
initialOptions: InAppWebViewGroupOptions(
|
initialOptions: InAppWebViewGroupOptions(
|
||||||
crossPlatform: InAppWebViewOptions(
|
crossPlatform: InAppWebViewOptions(
|
||||||
clearCache: true,
|
clearCache: true,
|
||||||
useShouldInterceptAjaxRequest: true,
|
useShouldInterceptAjaxRequest: true,
|
||||||
)),
|
)),
|
||||||
onWebViewCreated: (controller) {
|
onWebViewCreated: (controller) {
|
||||||
controllerCompleter.complete(controller);
|
controllerCompleter.complete(controller);
|
||||||
},
|
},
|
||||||
|
@ -1959,9 +1963,9 @@ void main() {
|
||||||
|
|
||||||
await shouldInterceptAjaxPostRequestCompleter.future;
|
await shouldInterceptAjaxPostRequestCompleter.future;
|
||||||
final Map<String, dynamic> onAjaxReadyStateChangeValue =
|
final Map<String, dynamic> onAjaxReadyStateChangeValue =
|
||||||
await onAjaxReadyStateChangeCompleter.future;
|
await onAjaxReadyStateChangeCompleter.future;
|
||||||
final Map<String, dynamic> onAjaxProgressValue =
|
final Map<String, dynamic> onAjaxProgressValue =
|
||||||
await onAjaxProgressCompleter.future;
|
await onAjaxProgressCompleter.future;
|
||||||
|
|
||||||
expect(
|
expect(
|
||||||
mapEquals(onAjaxReadyStateChangeValue,
|
mapEquals(onAjaxReadyStateChangeValue,
|
||||||
|
@ -1974,13 +1978,14 @@ void main() {
|
||||||
});
|
});
|
||||||
|
|
||||||
testWidgets('send FormData', (WidgetTester tester) async {
|
testWidgets('send FormData', (WidgetTester tester) async {
|
||||||
final Completer controllerCompleter = Completer<InAppWebViewController>();
|
final Completer controllerCompleter =
|
||||||
|
Completer<InAppWebViewController>();
|
||||||
final Completer shouldInterceptAjaxPostRequestCompleter =
|
final Completer shouldInterceptAjaxPostRequestCompleter =
|
||||||
Completer<void>();
|
Completer<void>();
|
||||||
final Completer<Map<String, dynamic>> onAjaxReadyStateChangeCompleter =
|
final Completer<Map<String, dynamic>> onAjaxReadyStateChangeCompleter =
|
||||||
Completer<Map<String, dynamic>>();
|
Completer<Map<String, dynamic>>();
|
||||||
final Completer<Map<String, dynamic>> onAjaxProgressCompleter =
|
final Completer<Map<String, dynamic>> onAjaxProgressCompleter =
|
||||||
Completer<Map<String, dynamic>>();
|
Completer<Map<String, dynamic>>();
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
Directionality(
|
Directionality(
|
||||||
textDirection: TextDirection.ltr,
|
textDirection: TextDirection.ltr,
|
||||||
|
@ -2012,9 +2017,9 @@ void main() {
|
||||||
"""),
|
"""),
|
||||||
initialOptions: InAppWebViewGroupOptions(
|
initialOptions: InAppWebViewGroupOptions(
|
||||||
crossPlatform: InAppWebViewOptions(
|
crossPlatform: InAppWebViewOptions(
|
||||||
clearCache: true,
|
clearCache: true,
|
||||||
useShouldInterceptAjaxRequest: true,
|
useShouldInterceptAjaxRequest: true,
|
||||||
)),
|
)),
|
||||||
onWebViewCreated: (controller) {
|
onWebViewCreated: (controller) {
|
||||||
controllerCompleter.complete(controller);
|
controllerCompleter.complete(controller);
|
||||||
},
|
},
|
||||||
|
@ -2025,7 +2030,9 @@ void main() {
|
||||||
var bodyString = String.fromCharCodes(body);
|
var bodyString = String.fromCharCodes(body);
|
||||||
assert(bodyString.indexOf("WebKitFormBoundary") >= 0);
|
assert(bodyString.indexOf("WebKitFormBoundary") >= 0);
|
||||||
|
|
||||||
ajaxRequest.data = utf8.encode(bodyString.replaceFirst("Foo", "Foo2").replaceFirst("Bar", "Bar2"));
|
ajaxRequest.data = utf8.encode(bodyString
|
||||||
|
.replaceFirst("Foo", "Foo2")
|
||||||
|
.replaceFirst("Bar", "Bar2"));
|
||||||
ajaxRequest.responseType = 'json';
|
ajaxRequest.responseType = 'json';
|
||||||
shouldInterceptAjaxPostRequestCompleter.complete(controller);
|
shouldInterceptAjaxPostRequestCompleter.complete(controller);
|
||||||
return ajaxRequest;
|
return ajaxRequest;
|
||||||
|
@ -2051,9 +2058,9 @@ void main() {
|
||||||
|
|
||||||
await shouldInterceptAjaxPostRequestCompleter.future;
|
await shouldInterceptAjaxPostRequestCompleter.future;
|
||||||
final Map<String, dynamic> onAjaxReadyStateChangeValue =
|
final Map<String, dynamic> onAjaxReadyStateChangeValue =
|
||||||
await onAjaxReadyStateChangeCompleter.future;
|
await onAjaxReadyStateChangeCompleter.future;
|
||||||
final Map<String, dynamic> onAjaxProgressValue =
|
final Map<String, dynamic> onAjaxProgressValue =
|
||||||
await onAjaxProgressCompleter.future;
|
await onAjaxProgressCompleter.future;
|
||||||
|
|
||||||
expect(
|
expect(
|
||||||
mapEquals(onAjaxReadyStateChangeValue,
|
mapEquals(onAjaxReadyStateChangeValue,
|
||||||
|
@ -2068,11 +2075,12 @@ void main() {
|
||||||
|
|
||||||
group('intercept fetch request', () {
|
group('intercept fetch request', () {
|
||||||
testWidgets('send string data', (WidgetTester tester) async {
|
testWidgets('send string data', (WidgetTester tester) async {
|
||||||
final Completer controllerCompleter = Completer<InAppWebViewController>();
|
final Completer controllerCompleter =
|
||||||
|
Completer<InAppWebViewController>();
|
||||||
final Completer<Map<String, dynamic>> fetchPostCompleter =
|
final Completer<Map<String, dynamic>> fetchPostCompleter =
|
||||||
Completer<Map<String, dynamic>>();
|
Completer<Map<String, dynamic>>();
|
||||||
final Completer<void> shouldInterceptFetchPostRequestCompleter =
|
final Completer<void> shouldInterceptFetchPostRequestCompleter =
|
||||||
Completer<void>();
|
Completer<void>();
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
Directionality(
|
Directionality(
|
||||||
textDirection: TextDirection.ltr,
|
textDirection: TextDirection.ltr,
|
||||||
|
@ -2113,9 +2121,9 @@ void main() {
|
||||||
"""),
|
"""),
|
||||||
initialOptions: InAppWebViewGroupOptions(
|
initialOptions: InAppWebViewGroupOptions(
|
||||||
crossPlatform: InAppWebViewOptions(
|
crossPlatform: InAppWebViewOptions(
|
||||||
clearCache: true,
|
clearCache: true,
|
||||||
useShouldInterceptFetchRequest: true,
|
useShouldInterceptFetchRequest: true,
|
||||||
)),
|
)),
|
||||||
onWebViewCreated: (controller) {
|
onWebViewCreated: (controller) {
|
||||||
controllerCompleter.complete(controller);
|
controllerCompleter.complete(controller);
|
||||||
|
|
||||||
|
@ -2147,11 +2155,12 @@ void main() {
|
||||||
});
|
});
|
||||||
|
|
||||||
testWidgets('send json data', (WidgetTester tester) async {
|
testWidgets('send json data', (WidgetTester tester) async {
|
||||||
final Completer controllerCompleter = Completer<InAppWebViewController>();
|
final Completer controllerCompleter =
|
||||||
|
Completer<InAppWebViewController>();
|
||||||
final Completer<Map<String, dynamic>> fetchPostCompleter =
|
final Completer<Map<String, dynamic>> fetchPostCompleter =
|
||||||
Completer<Map<String, dynamic>>();
|
Completer<Map<String, dynamic>>();
|
||||||
final Completer<void> shouldInterceptFetchPostRequestCompleter =
|
final Completer<void> shouldInterceptFetchPostRequestCompleter =
|
||||||
Completer<void>();
|
Completer<void>();
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
Directionality(
|
Directionality(
|
||||||
textDirection: TextDirection.ltr,
|
textDirection: TextDirection.ltr,
|
||||||
|
@ -2196,9 +2205,9 @@ void main() {
|
||||||
"""),
|
"""),
|
||||||
initialOptions: InAppWebViewGroupOptions(
|
initialOptions: InAppWebViewGroupOptions(
|
||||||
crossPlatform: InAppWebViewOptions(
|
crossPlatform: InAppWebViewOptions(
|
||||||
clearCache: true,
|
clearCache: true,
|
||||||
useShouldInterceptFetchRequest: true,
|
useShouldInterceptFetchRequest: true,
|
||||||
)),
|
)),
|
||||||
onWebViewCreated: (controller) {
|
onWebViewCreated: (controller) {
|
||||||
controllerCompleter.complete(controller);
|
controllerCompleter.complete(controller);
|
||||||
|
|
||||||
|
@ -2211,7 +2220,8 @@ void main() {
|
||||||
},
|
},
|
||||||
shouldInterceptFetchRequest: (controller, fetchRequest) async {
|
shouldInterceptFetchRequest: (controller, fetchRequest) async {
|
||||||
String body = fetchRequest.body;
|
String body = fetchRequest.body;
|
||||||
assert(body.contains('"firstname":"Foo"') && body.contains('"lastname":"Bar"'));
|
assert(body.contains('"firstname":"Foo"') &&
|
||||||
|
body.contains('"lastname":"Bar"'));
|
||||||
|
|
||||||
fetchRequest.body = '{"firstname": "Foo2", "lastname": "Bar2"}';
|
fetchRequest.body = '{"firstname": "Foo2", "lastname": "Bar2"}';
|
||||||
shouldInterceptFetchPostRequestCompleter.complete();
|
shouldInterceptFetchPostRequestCompleter.complete();
|
||||||
|
@ -2231,11 +2241,12 @@ void main() {
|
||||||
});
|
});
|
||||||
|
|
||||||
testWidgets('send URLSearchParams data', (WidgetTester tester) async {
|
testWidgets('send URLSearchParams data', (WidgetTester tester) async {
|
||||||
final Completer controllerCompleter = Completer<InAppWebViewController>();
|
final Completer controllerCompleter =
|
||||||
|
Completer<InAppWebViewController>();
|
||||||
final Completer<Map<String, dynamic>> fetchPostCompleter =
|
final Completer<Map<String, dynamic>> fetchPostCompleter =
|
||||||
Completer<Map<String, dynamic>>();
|
Completer<Map<String, dynamic>>();
|
||||||
final Completer<void> shouldInterceptFetchPostRequestCompleter =
|
final Completer<void> shouldInterceptFetchPostRequestCompleter =
|
||||||
Completer<void>();
|
Completer<void>();
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
Directionality(
|
Directionality(
|
||||||
textDirection: TextDirection.ltr,
|
textDirection: TextDirection.ltr,
|
||||||
|
@ -2278,9 +2289,9 @@ void main() {
|
||||||
"""),
|
"""),
|
||||||
initialOptions: InAppWebViewGroupOptions(
|
initialOptions: InAppWebViewGroupOptions(
|
||||||
crossPlatform: InAppWebViewOptions(
|
crossPlatform: InAppWebViewOptions(
|
||||||
clearCache: true,
|
clearCache: true,
|
||||||
useShouldInterceptFetchRequest: true,
|
useShouldInterceptFetchRequest: true,
|
||||||
)),
|
)),
|
||||||
onWebViewCreated: (controller) {
|
onWebViewCreated: (controller) {
|
||||||
controllerCompleter.complete(controller);
|
controllerCompleter.complete(controller);
|
||||||
|
|
||||||
|
@ -2312,11 +2323,12 @@ void main() {
|
||||||
});
|
});
|
||||||
|
|
||||||
testWidgets('send FormData', (WidgetTester tester) async {
|
testWidgets('send FormData', (WidgetTester tester) async {
|
||||||
final Completer controllerCompleter = Completer<InAppWebViewController>();
|
final Completer controllerCompleter =
|
||||||
|
Completer<InAppWebViewController>();
|
||||||
final Completer<Map<String, dynamic>> fetchPostCompleter =
|
final Completer<Map<String, dynamic>> fetchPostCompleter =
|
||||||
Completer<Map<String, dynamic>>();
|
Completer<Map<String, dynamic>>();
|
||||||
final Completer<void> shouldInterceptFetchPostRequestCompleter =
|
final Completer<void> shouldInterceptFetchPostRequestCompleter =
|
||||||
Completer<void>();
|
Completer<void>();
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
Directionality(
|
Directionality(
|
||||||
textDirection: TextDirection.ltr,
|
textDirection: TextDirection.ltr,
|
||||||
|
@ -2357,9 +2369,9 @@ void main() {
|
||||||
"""),
|
"""),
|
||||||
initialOptions: InAppWebViewGroupOptions(
|
initialOptions: InAppWebViewGroupOptions(
|
||||||
crossPlatform: InAppWebViewOptions(
|
crossPlatform: InAppWebViewOptions(
|
||||||
clearCache: true,
|
clearCache: true,
|
||||||
useShouldInterceptFetchRequest: true,
|
useShouldInterceptFetchRequest: true,
|
||||||
)),
|
)),
|
||||||
onWebViewCreated: (controller) {
|
onWebViewCreated: (controller) {
|
||||||
controllerCompleter.complete(controller);
|
controllerCompleter.complete(controller);
|
||||||
|
|
||||||
|
@ -2377,7 +2389,9 @@ void main() {
|
||||||
var bodyString = String.fromCharCodes(body);
|
var bodyString = String.fromCharCodes(body);
|
||||||
assert(bodyString.indexOf("WebKitFormBoundary") >= 0);
|
assert(bodyString.indexOf("WebKitFormBoundary") >= 0);
|
||||||
|
|
||||||
fetchRequest.body = utf8.encode(bodyString.replaceFirst("Foo", "Foo2").replaceFirst("Bar", "Bar2"));
|
fetchRequest.body = utf8.encode(bodyString
|
||||||
|
.replaceFirst("Foo", "Foo2")
|
||||||
|
.replaceFirst("Bar", "Bar2"));
|
||||||
shouldInterceptFetchPostRequestCompleter.complete();
|
shouldInterceptFetchPostRequestCompleter.complete();
|
||||||
return fetchRequest;
|
return fetchRequest;
|
||||||
},
|
},
|
||||||
|
@ -2404,24 +2418,24 @@ void main() {
|
||||||
child: InAppWebView(
|
child: InAppWebView(
|
||||||
key: GlobalKey(),
|
key: GlobalKey(),
|
||||||
initialUrlRequest:
|
initialUrlRequest:
|
||||||
URLRequest(url: Uri.parse('https://flutter.dev/')),
|
URLRequest(url: Uri.parse('https://flutter.dev/')),
|
||||||
onWebViewCreated: (controller) {
|
onWebViewCreated: (controller) {
|
||||||
controllerCompleter.complete(controller);
|
controllerCompleter.complete(controller);
|
||||||
},
|
},
|
||||||
initialOptions: InAppWebViewGroupOptions(
|
initialOptions: InAppWebViewGroupOptions(
|
||||||
crossPlatform:
|
crossPlatform:
|
||||||
InAppWebViewOptions(clearCache: true, contentBlockers: [
|
InAppWebViewOptions(clearCache: true, contentBlockers: [
|
||||||
ContentBlocker(
|
ContentBlocker(
|
||||||
trigger:
|
trigger:
|
||||||
ContentBlockerTrigger(urlFilter: ".*", resourceType: [
|
ContentBlockerTrigger(urlFilter: ".*", resourceType: [
|
||||||
ContentBlockerTriggerResourceType.IMAGE,
|
ContentBlockerTriggerResourceType.IMAGE,
|
||||||
ContentBlockerTriggerResourceType.STYLE_SHEET
|
ContentBlockerTriggerResourceType.STYLE_SHEET
|
||||||
], ifTopUrl: [
|
], ifTopUrl: [
|
||||||
"https://flutter.dev/"
|
"https://flutter.dev/"
|
||||||
]),
|
]),
|
||||||
action: ContentBlockerAction(
|
action: ContentBlockerAction(
|
||||||
type: ContentBlockerActionType.BLOCK))
|
type: ContentBlockerActionType.BLOCK))
|
||||||
])),
|
])),
|
||||||
onLoadStop: (controller, url) {
|
onLoadStop: (controller, url) {
|
||||||
pageLoaded.complete();
|
pageLoaded.complete();
|
||||||
},
|
},
|
||||||
|
@ -3261,7 +3275,7 @@ setTimeout(function() {
|
||||||
child: InAppWebView(
|
child: InAppWebView(
|
||||||
key: GlobalKey(),
|
key: GlobalKey(),
|
||||||
initialUrlRequest:
|
initialUrlRequest:
|
||||||
URLRequest(url: Uri.parse('https://github.com/flutter')),
|
URLRequest(url: Uri.parse('https://github.com/flutter')),
|
||||||
onWebViewCreated: (controller) {
|
onWebViewCreated: (controller) {
|
||||||
controllerCompleter.complete(controller);
|
controllerCompleter.complete(controller);
|
||||||
},
|
},
|
||||||
|
@ -3278,7 +3292,7 @@ setTimeout(function() {
|
||||||
);
|
);
|
||||||
|
|
||||||
final InAppWebViewController controller =
|
final InAppWebViewController controller =
|
||||||
await controllerCompleter.future;
|
await controllerCompleter.future;
|
||||||
await pageLoaded.future;
|
await pageLoaded.future;
|
||||||
listenForScaleChange = true;
|
listenForScaleChange = true;
|
||||||
|
|
||||||
|
@ -4167,10 +4181,14 @@ setTimeout(function() {
|
||||||
await pageLoaded.future;
|
await pageLoaded.future;
|
||||||
|
|
||||||
await controller.injectJavascriptFileFromUrl(
|
await controller.injectJavascriptFileFromUrl(
|
||||||
urlFile: Uri.parse('https://www.notawebsite..com/jquery-3.3.1.min.js'),
|
urlFile:
|
||||||
scriptHtmlTagAttributes: ScriptHtmlTagAttributes(id: 'jquery-error', onError: () {
|
Uri.parse('https://www.notawebsite..com/jquery-3.3.1.min.js'),
|
||||||
jQueryLoadError.complete();
|
scriptHtmlTagAttributes: ScriptHtmlTagAttributes(
|
||||||
},));
|
id: 'jquery-error',
|
||||||
|
onError: () {
|
||||||
|
jQueryLoadError.complete();
|
||||||
|
},
|
||||||
|
));
|
||||||
await jQueryLoadError.future;
|
await jQueryLoadError.future;
|
||||||
expect(
|
expect(
|
||||||
await controller.evaluateJavascript(
|
await controller.evaluateJavascript(
|
||||||
|
@ -4182,9 +4200,12 @@ setTimeout(function() {
|
||||||
|
|
||||||
await controller.injectJavascriptFileFromUrl(
|
await controller.injectJavascriptFileFromUrl(
|
||||||
urlFile: Uri.parse('https://code.jquery.com/jquery-3.3.1.min.js'),
|
urlFile: Uri.parse('https://code.jquery.com/jquery-3.3.1.min.js'),
|
||||||
scriptHtmlTagAttributes: ScriptHtmlTagAttributes(id: 'jquery', onLoad: () {
|
scriptHtmlTagAttributes: ScriptHtmlTagAttributes(
|
||||||
jQueryLoaded.complete();
|
id: 'jquery',
|
||||||
},));
|
onLoad: () {
|
||||||
|
jQueryLoaded.complete();
|
||||||
|
},
|
||||||
|
));
|
||||||
await jQueryLoaded.future;
|
await jQueryLoaded.future;
|
||||||
expect(
|
expect(
|
||||||
await controller.evaluateJavascript(
|
await controller.evaluateJavascript(
|
||||||
|
@ -4903,21 +4924,19 @@ setTimeout(function() {
|
||||||
expect(await InAppWebViewController.getDefaultUserAgent(), isNotNull);
|
expect(await InAppWebViewController.getDefaultUserAgent(), isNotNull);
|
||||||
});
|
});
|
||||||
|
|
||||||
testWidgets('launches with pull-to-refresh feature', (WidgetTester tester) async {
|
testWidgets('launches with pull-to-refresh feature',
|
||||||
|
(WidgetTester tester) async {
|
||||||
final Completer controllerCompleter = Completer<InAppWebViewController>();
|
final Completer controllerCompleter = Completer<InAppWebViewController>();
|
||||||
final pullToRefreshController = PullToRefreshController(
|
final pullToRefreshController = PullToRefreshController(
|
||||||
options: PullToRefreshOptions(
|
options: PullToRefreshOptions(
|
||||||
color: Colors.blue,
|
color: Colors.blue,
|
||||||
size: AndroidPullToRefreshSize.DEFAULT,
|
size: AndroidPullToRefreshSize.DEFAULT,
|
||||||
backgroundColor: Colors.grey,
|
backgroundColor: Colors.grey,
|
||||||
enabled: true,
|
enabled: true,
|
||||||
slingshotDistance: 150,
|
slingshotDistance: 150,
|
||||||
distanceToTriggerSync: 150,
|
distanceToTriggerSync: 150,
|
||||||
attributedTitle: IOSNSAttributedString(string: "test")
|
attributedTitle: IOSNSAttributedString(string: "test")),
|
||||||
),
|
onRefresh: () {},
|
||||||
onRefresh: () {
|
|
||||||
|
|
||||||
},
|
|
||||||
);
|
);
|
||||||
|
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
|
@ -4925,12 +4944,11 @@ setTimeout(function() {
|
||||||
textDirection: TextDirection.ltr,
|
textDirection: TextDirection.ltr,
|
||||||
child: InAppWebView(
|
child: InAppWebView(
|
||||||
key: GlobalKey(),
|
key: GlobalKey(),
|
||||||
initialUrlRequest: URLRequest(url: Uri.parse('https://github.com/flutter')),
|
initialUrlRequest:
|
||||||
|
URLRequest(url: Uri.parse('https://github.com/flutter')),
|
||||||
initialOptions: InAppWebViewGroupOptions(
|
initialOptions: InAppWebViewGroupOptions(
|
||||||
android: AndroidInAppWebViewOptions(
|
android:
|
||||||
useHybridComposition: true
|
AndroidInAppWebViewOptions(useHybridComposition: true)),
|
||||||
)
|
|
||||||
),
|
|
||||||
pullToRefreshController: pullToRefreshController,
|
pullToRefreshController: pullToRefreshController,
|
||||||
onWebViewCreated: (controller) {
|
onWebViewCreated: (controller) {
|
||||||
controllerCompleter.complete(controller);
|
controllerCompleter.complete(controller);
|
||||||
|
@ -4939,22 +4957,22 @@ setTimeout(function() {
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
final InAppWebViewController controller =
|
final InAppWebViewController controller =
|
||||||
await controllerCompleter.future;
|
await controllerCompleter.future;
|
||||||
final String? currentUrl = (await controller.getUrl())?.toString();
|
final String? currentUrl = (await controller.getUrl())?.toString();
|
||||||
expect(currentUrl, 'https://github.com/flutter');
|
expect(currentUrl, 'https://github.com/flutter');
|
||||||
});
|
});
|
||||||
|
|
||||||
group('WebMessage', () {
|
group('WebMessage', () {
|
||||||
testWidgets('WebMessageChannel', (WidgetTester tester) async {
|
testWidgets('WebMessageChannel', (WidgetTester tester) async {
|
||||||
final Completer controllerCompleter = Completer<InAppWebViewController>();
|
final Completer controllerCompleter =
|
||||||
|
Completer<InAppWebViewController>();
|
||||||
final Completer webMessageCompleter = Completer<String>();
|
final Completer webMessageCompleter = Completer<String>();
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
Directionality(
|
Directionality(
|
||||||
textDirection: TextDirection.ltr,
|
textDirection: TextDirection.ltr,
|
||||||
child: InAppWebView(
|
child: InAppWebView(
|
||||||
key: GlobalKey(),
|
key: GlobalKey(),
|
||||||
initialData: InAppWebViewInitialData(
|
initialData: InAppWebViewInitialData(data: """
|
||||||
data: """
|
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
|
@ -4989,15 +5007,20 @@ setTimeout(function() {
|
||||||
webMessageCompleter.complete(consoleMessage.message);
|
webMessageCompleter.complete(consoleMessage.message);
|
||||||
},
|
},
|
||||||
onLoadStop: (controller, url) async {
|
onLoadStop: (controller, url) async {
|
||||||
var webMessageChannel = await controller.createWebMessageChannel();
|
var webMessageChannel =
|
||||||
|
await controller.createWebMessageChannel();
|
||||||
var port1 = webMessageChannel!.port1;
|
var port1 = webMessageChannel!.port1;
|
||||||
var port2 = webMessageChannel.port2;
|
var port2 = webMessageChannel.port2;
|
||||||
|
|
||||||
await port1.setWebMessageCallback((message) async {
|
await port1.setWebMessageCallback((message) async {
|
||||||
await port1.postMessage(WebMessage(data: message! + " and back"));
|
await port1
|
||||||
|
.postMessage(WebMessage(data: message! + " and back"));
|
||||||
});
|
});
|
||||||
await controller.postWebMessage(message: WebMessage(data: "capturePort", ports: [port2]), targetOrigin: Uri.parse("*"));
|
await controller.postWebMessage(
|
||||||
await controller.evaluateJavascript(source: "document.getElementById('button').click();");
|
message: WebMessage(data: "capturePort", ports: [port2]),
|
||||||
|
targetOrigin: Uri.parse("*"));
|
||||||
|
await controller.evaluateJavascript(
|
||||||
|
source: "document.getElementById('button').click();");
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -5009,7 +5032,8 @@ setTimeout(function() {
|
||||||
});
|
});
|
||||||
|
|
||||||
testWidgets('WebMessageListener', (WidgetTester tester) async {
|
testWidgets('WebMessageListener', (WidgetTester tester) async {
|
||||||
final Completer controllerCompleter = Completer<InAppWebViewController>();
|
final Completer controllerCompleter =
|
||||||
|
Completer<InAppWebViewController>();
|
||||||
final Completer<void> pageLoaded = Completer<void>();
|
final Completer<void> pageLoaded = Completer<void>();
|
||||||
final Completer webMessageCompleter = Completer<String>();
|
final Completer webMessageCompleter = Completer<String>();
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
|
@ -5021,8 +5045,10 @@ setTimeout(function() {
|
||||||
await controller.addWebMessageListener(WebMessageListener(
|
await controller.addWebMessageListener(WebMessageListener(
|
||||||
jsObjectName: "myTestObj",
|
jsObjectName: "myTestObj",
|
||||||
allowedOriginRules: Set.from(["https://*.example.com"]),
|
allowedOriginRules: Set.from(["https://*.example.com"]),
|
||||||
onPostMessage: (message, sourceOrigin, isMainFrame, replyProxy) {
|
onPostMessage:
|
||||||
assert(sourceOrigin.toString() == "https://www.example.com");
|
(message, sourceOrigin, isMainFrame, replyProxy) {
|
||||||
|
assert(
|
||||||
|
sourceOrigin.toString() == "https://www.example.com");
|
||||||
assert(isMainFrame);
|
assert(isMainFrame);
|
||||||
|
|
||||||
replyProxy.postMessage(message! + " and back");
|
replyProxy.postMessage(message! + " and back");
|
||||||
|
@ -5042,7 +5068,8 @@ setTimeout(function() {
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
final controller = await controllerCompleter.future;
|
final controller = await controllerCompleter.future;
|
||||||
await controller.loadUrl(urlRequest: URLRequest(url: Uri.parse("https://www.example.com/")));
|
await controller.loadUrl(
|
||||||
|
urlRequest: URLRequest(url: Uri.parse("https://www.example.com/")));
|
||||||
await pageLoaded.future;
|
await pageLoaded.future;
|
||||||
|
|
||||||
await controller.evaluateJavascript(source: """
|
await controller.evaluateJavascript(source: """
|
||||||
|
@ -5273,7 +5300,9 @@ setTimeout(function() {
|
||||||
}, skip: !Platform.isAndroid);
|
}, skip: !Platform.isAndroid);
|
||||||
|
|
||||||
test('setWebContentsDebuggingEnabled', () async {
|
test('setWebContentsDebuggingEnabled', () async {
|
||||||
expect(AndroidInAppWebViewController.setWebContentsDebuggingEnabled(true), completes);
|
expect(
|
||||||
|
AndroidInAppWebViewController.setWebContentsDebuggingEnabled(true),
|
||||||
|
completes);
|
||||||
}, skip: !Platform.isAndroid);
|
}, skip: !Platform.isAndroid);
|
||||||
}, skip: !Platform.isAndroid);
|
}, skip: !Platform.isAndroid);
|
||||||
|
|
||||||
|
@ -5377,8 +5406,7 @@ setTimeout(function() {
|
||||||
textDirection: TextDirection.ltr,
|
textDirection: TextDirection.ltr,
|
||||||
child: InAppWebView(
|
child: InAppWebView(
|
||||||
key: GlobalKey(),
|
key: GlobalKey(),
|
||||||
initialData: InAppWebViewInitialData(
|
initialData: InAppWebViewInitialData(data: """
|
||||||
data: """
|
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
|
@ -5393,8 +5421,7 @@ setTimeout(function() {
|
||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
"""
|
"""),
|
||||||
),
|
|
||||||
initialOptions: InAppWebViewGroupOptions(
|
initialOptions: InAppWebViewGroupOptions(
|
||||||
ios: IOSInAppWebViewOptions(
|
ios: IOSInAppWebViewOptions(
|
||||||
applePayAPIEnabled: true,
|
applePayAPIEnabled: true,
|
||||||
|
@ -5421,6 +5448,47 @@ setTimeout(function() {
|
||||||
}, skip: !Platform.isIOS);
|
}, skip: !Platform.isIOS);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
group('Service Worker', () {
|
||||||
|
testWidgets('AndroidInAppWebViewController', (WidgetTester tester) async {
|
||||||
|
final Completer completer = Completer();
|
||||||
|
|
||||||
|
var swAvailable = await AndroidWebViewFeature.isFeatureSupported(
|
||||||
|
AndroidWebViewFeature.SERVICE_WORKER_BASIC_USAGE);
|
||||||
|
var swInterceptAvailable = await AndroidWebViewFeature.isFeatureSupported(
|
||||||
|
AndroidWebViewFeature.SERVICE_WORKER_SHOULD_INTERCEPT_REQUEST);
|
||||||
|
|
||||||
|
if (swAvailable && swInterceptAvailable) {
|
||||||
|
AndroidServiceWorkerController serviceWorkerController =
|
||||||
|
AndroidServiceWorkerController.instance();
|
||||||
|
|
||||||
|
await serviceWorkerController
|
||||||
|
.setServiceWorkerClient(AndroidServiceWorkerClient(
|
||||||
|
shouldInterceptRequest: (request) async {
|
||||||
|
if (!completer.isCompleted) {
|
||||||
|
completer.complete();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
},
|
||||||
|
));
|
||||||
|
} else {
|
||||||
|
completer.complete();
|
||||||
|
}
|
||||||
|
|
||||||
|
await tester.pumpWidget(
|
||||||
|
Directionality(
|
||||||
|
textDirection: TextDirection.ltr,
|
||||||
|
child: InAppWebView(
|
||||||
|
key: GlobalKey(),
|
||||||
|
initialUrlRequest:
|
||||||
|
URLRequest(url: Uri.parse('https://mdn.github.io/sw-test/')),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(completer.future, completes);
|
||||||
|
}, skip: !Platform.isAndroid);
|
||||||
|
});
|
||||||
|
|
||||||
group('Cookie Manager', () {
|
group('Cookie Manager', () {
|
||||||
testWidgets('set, get, delete', (WidgetTester tester) async {
|
testWidgets('set, get, delete', (WidgetTester tester) async {
|
||||||
CookieManager cookieManager = CookieManager.instance();
|
CookieManager cookieManager = CookieManager.instance();
|
||||||
|
@ -5474,7 +5542,8 @@ setTimeout(function() {
|
||||||
final Completer<void> pageLoaded = Completer<void>();
|
final Completer<void> pageLoaded = Completer<void>();
|
||||||
|
|
||||||
var headlessWebView = new HeadlessInAppWebView(
|
var headlessWebView = new HeadlessInAppWebView(
|
||||||
initialUrlRequest: URLRequest(url: Uri.parse("https://github.com/flutter")),
|
initialUrlRequest:
|
||||||
|
URLRequest(url: Uri.parse("https://github.com/flutter")),
|
||||||
onWebViewCreated: (controller) {
|
onWebViewCreated: (controller) {
|
||||||
controllerCompleter.complete(controller);
|
controllerCompleter.complete(controller);
|
||||||
},
|
},
|
||||||
|
@ -5503,20 +5572,20 @@ setTimeout(function() {
|
||||||
final Completer<void> pageLoaded = Completer<void>();
|
final Completer<void> pageLoaded = Completer<void>();
|
||||||
|
|
||||||
var headlessWebView = new HeadlessInAppWebView(
|
var headlessWebView = new HeadlessInAppWebView(
|
||||||
initialUrlRequest: URLRequest(url: Uri.parse("https://github.com/flutter")),
|
initialUrlRequest:
|
||||||
onWebViewCreated: (controller) {
|
URLRequest(url: Uri.parse("https://github.com/flutter")),
|
||||||
controllerCompleter.complete(controller);
|
onWebViewCreated: (controller) {
|
||||||
},
|
controllerCompleter.complete(controller);
|
||||||
onLoadStop: (controller, url) async {
|
},
|
||||||
pageLoaded.complete();
|
onLoadStop: (controller, url) async {
|
||||||
}
|
pageLoaded.complete();
|
||||||
);
|
});
|
||||||
|
|
||||||
await headlessWebView.run();
|
await headlessWebView.run();
|
||||||
expect(headlessWebView.isRunning(), true);
|
expect(headlessWebView.isRunning(), true);
|
||||||
|
|
||||||
final InAppWebViewController controller =
|
final InAppWebViewController controller =
|
||||||
await controllerCompleter.future;
|
await controllerCompleter.future;
|
||||||
await pageLoaded.future;
|
await pageLoaded.future;
|
||||||
|
|
||||||
final String? url = (await controller.getUrl())?.toString();
|
final String? url = (await controller.getUrl())?.toString();
|
||||||
|
@ -5537,11 +5606,12 @@ setTimeout(function() {
|
||||||
final Completer controllerCompleter = Completer<InAppWebViewController>();
|
final Completer controllerCompleter = Completer<InAppWebViewController>();
|
||||||
|
|
||||||
var headlessWebView = new HeadlessInAppWebView(
|
var headlessWebView = new HeadlessInAppWebView(
|
||||||
initialUrlRequest: URLRequest(url: Uri.parse("https://github.com/flutter")),
|
initialUrlRequest:
|
||||||
initialSize: Size(600, 800),
|
URLRequest(url: Uri.parse("https://github.com/flutter")),
|
||||||
onWebViewCreated: (controller) {
|
initialSize: Size(600, 800),
|
||||||
controllerCompleter.complete(controller);
|
onWebViewCreated: (controller) {
|
||||||
},
|
controllerCompleter.complete(controller);
|
||||||
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
await headlessWebView.run();
|
await headlessWebView.run();
|
||||||
|
@ -5566,7 +5636,8 @@ setTimeout(function() {
|
||||||
final Completer<void> pageLoaded = Completer<void>();
|
final Completer<void> pageLoaded = Completer<void>();
|
||||||
|
|
||||||
var headlessWebView = new HeadlessInAppWebView(
|
var headlessWebView = new HeadlessInAppWebView(
|
||||||
initialUrlRequest: URLRequest(url: Uri.parse("https://github.com/flutter")),
|
initialUrlRequest:
|
||||||
|
URLRequest(url: Uri.parse("https://github.com/flutter")),
|
||||||
initialOptions: InAppWebViewGroupOptions(
|
initialOptions: InAppWebViewGroupOptions(
|
||||||
crossPlatform: InAppWebViewOptions(javaScriptEnabled: false)),
|
crossPlatform: InAppWebViewOptions(javaScriptEnabled: false)),
|
||||||
onWebViewCreated: (controller) {
|
onWebViewCreated: (controller) {
|
||||||
|
@ -5610,8 +5681,7 @@ setTimeout(function() {
|
||||||
expect(inAppBrowser.isOpened(), true);
|
expect(inAppBrowser.isOpened(), true);
|
||||||
expect(() async {
|
expect(() async {
|
||||||
await inAppBrowser.openUrlRequest(
|
await inAppBrowser.openUrlRequest(
|
||||||
urlRequest:
|
urlRequest: URLRequest(url: Uri.parse("https://flutter.dev")));
|
||||||
URLRequest(url: Uri.parse("https://flutter.dev")));
|
|
||||||
}, throwsA(isInstanceOf<InAppBrowserAlreadyOpenedException>()));
|
}, throwsA(isInstanceOf<InAppBrowserAlreadyOpenedException>()));
|
||||||
|
|
||||||
await inAppBrowser.firstPageLoaded.future;
|
await inAppBrowser.firstPageLoaded.future;
|
||||||
|
@ -5633,13 +5703,14 @@ setTimeout(function() {
|
||||||
await inAppBrowser.show();
|
await inAppBrowser.show();
|
||||||
}, throwsA(isInstanceOf<InAppBrowserNotOpenedException>()));
|
}, throwsA(isInstanceOf<InAppBrowserNotOpenedException>()));
|
||||||
|
|
||||||
await inAppBrowser.openFile(assetFilePath: "test_assets/in_app_webview_initial_file_test.html");
|
await inAppBrowser.openFile(
|
||||||
|
assetFilePath: "test_assets/in_app_webview_initial_file_test.html");
|
||||||
await inAppBrowser.browserCreated.future;
|
await inAppBrowser.browserCreated.future;
|
||||||
expect(inAppBrowser.isOpened(), true);
|
expect(inAppBrowser.isOpened(), true);
|
||||||
expect(() async {
|
expect(() async {
|
||||||
await inAppBrowser.openUrlRequest(
|
await inAppBrowser.openUrlRequest(
|
||||||
urlRequest:
|
urlRequest:
|
||||||
URLRequest(url: Uri.parse("https://github.com/flutter")));
|
URLRequest(url: Uri.parse("https://github.com/flutter")));
|
||||||
}, throwsA(isInstanceOf<InAppBrowserAlreadyOpenedException>()));
|
}, throwsA(isInstanceOf<InAppBrowserAlreadyOpenedException>()));
|
||||||
|
|
||||||
await inAppBrowser.firstPageLoaded.future;
|
await inAppBrowser.firstPageLoaded.future;
|
||||||
|
@ -5661,7 +5732,8 @@ setTimeout(function() {
|
||||||
await inAppBrowser.show();
|
await inAppBrowser.show();
|
||||||
}, throwsA(isInstanceOf<InAppBrowserNotOpenedException>()));
|
}, throwsA(isInstanceOf<InAppBrowserNotOpenedException>()));
|
||||||
|
|
||||||
await inAppBrowser.openData(data: """
|
await inAppBrowser.openData(
|
||||||
|
data: """
|
||||||
<!doctype html>
|
<!doctype html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
|
@ -5685,7 +5757,7 @@ setTimeout(function() {
|
||||||
expect(() async {
|
expect(() async {
|
||||||
await inAppBrowser.openUrlRequest(
|
await inAppBrowser.openUrlRequest(
|
||||||
urlRequest:
|
urlRequest:
|
||||||
URLRequest(url: Uri.parse("https://github.com/flutter")));
|
URLRequest(url: Uri.parse("https://github.com/flutter")));
|
||||||
}, throwsA(isInstanceOf<InAppBrowserAlreadyOpenedException>()));
|
}, throwsA(isInstanceOf<InAppBrowserAlreadyOpenedException>()));
|
||||||
|
|
||||||
await inAppBrowser.firstPageLoaded.future;
|
await inAppBrowser.firstPageLoaded.future;
|
||||||
|
@ -5728,12 +5800,12 @@ setTimeout(function() {
|
||||||
var chromeSafariBrowser = new MyChromeSafariBrowser();
|
var chromeSafariBrowser = new MyChromeSafariBrowser();
|
||||||
expect(chromeSafariBrowser.isOpened(), false);
|
expect(chromeSafariBrowser.isOpened(), false);
|
||||||
|
|
||||||
await chromeSafariBrowser.open(url: Uri.parse("https://github.com/flutter"));
|
await chromeSafariBrowser.open(
|
||||||
|
url: Uri.parse("https://github.com/flutter"));
|
||||||
await chromeSafariBrowser.browserCreated.future;
|
await chromeSafariBrowser.browserCreated.future;
|
||||||
expect(chromeSafariBrowser.isOpened(), true);
|
expect(chromeSafariBrowser.isOpened(), true);
|
||||||
expect(() async {
|
expect(() async {
|
||||||
await chromeSafariBrowser.open(
|
await chromeSafariBrowser.open(url: Uri.parse("https://flutter.dev"));
|
||||||
url: Uri.parse("https://flutter.dev"));
|
|
||||||
}, throwsA(isInstanceOf<ChromeSafariBrowserAlreadyOpenedException>()));
|
}, throwsA(isInstanceOf<ChromeSafariBrowserAlreadyOpenedException>()));
|
||||||
|
|
||||||
await expectLater(chromeSafariBrowser.firstPageLoaded.future, completes);
|
await expectLater(chromeSafariBrowser.firstPageLoaded.future, completes);
|
||||||
|
@ -5748,21 +5820,18 @@ setTimeout(function() {
|
||||||
expect(chromeSafariBrowser.isOpened(), false);
|
expect(chromeSafariBrowser.isOpened(), false);
|
||||||
|
|
||||||
await chromeSafariBrowser.open(
|
await chromeSafariBrowser.open(
|
||||||
url: Uri.parse("https://github.com/flutter"),
|
url: Uri.parse("https://github.com/flutter"),
|
||||||
options: ChromeSafariBrowserClassOptions(
|
options: ChromeSafariBrowserClassOptions(
|
||||||
android: AndroidChromeCustomTabsOptions(
|
android:
|
||||||
isSingleInstance: true
|
AndroidChromeCustomTabsOptions(isSingleInstance: true)));
|
||||||
)
|
|
||||||
)
|
|
||||||
);
|
|
||||||
await chromeSafariBrowser.browserCreated.future;
|
await chromeSafariBrowser.browserCreated.future;
|
||||||
expect(chromeSafariBrowser.isOpened(), true);
|
expect(chromeSafariBrowser.isOpened(), true);
|
||||||
expect(() async {
|
expect(() async {
|
||||||
await chromeSafariBrowser.open(
|
await chromeSafariBrowser.open(url: Uri.parse("https://flutter.dev"));
|
||||||
url: Uri.parse("https://flutter.dev"));
|
|
||||||
}, throwsA(isInstanceOf<ChromeSafariBrowserAlreadyOpenedException>()));
|
}, throwsA(isInstanceOf<ChromeSafariBrowserAlreadyOpenedException>()));
|
||||||
|
|
||||||
await expectLater(chromeSafariBrowser.firstPageLoaded.future, completes);
|
await expectLater(
|
||||||
|
chromeSafariBrowser.firstPageLoaded.future, completes);
|
||||||
await chromeSafariBrowser.close();
|
await chromeSafariBrowser.close();
|
||||||
await chromeSafariBrowser.browserClosed.future;
|
await chromeSafariBrowser.browserClosed.future;
|
||||||
expect(chromeSafariBrowser.isOpened(), false);
|
expect(chromeSafariBrowser.isOpened(), false);
|
||||||
|
@ -5776,18 +5845,15 @@ setTimeout(function() {
|
||||||
url: Uri.parse("https://github.com/flutter"),
|
url: Uri.parse("https://github.com/flutter"),
|
||||||
options: ChromeSafariBrowserClassOptions(
|
options: ChromeSafariBrowserClassOptions(
|
||||||
android: AndroidChromeCustomTabsOptions(
|
android: AndroidChromeCustomTabsOptions(
|
||||||
isTrustedWebActivity: true
|
isTrustedWebActivity: true)));
|
||||||
)
|
|
||||||
)
|
|
||||||
);
|
|
||||||
await chromeSafariBrowser.browserCreated.future;
|
await chromeSafariBrowser.browserCreated.future;
|
||||||
expect(chromeSafariBrowser.isOpened(), true);
|
expect(chromeSafariBrowser.isOpened(), true);
|
||||||
expect(() async {
|
expect(() async {
|
||||||
await chromeSafariBrowser.open(
|
await chromeSafariBrowser.open(url: Uri.parse("https://flutter.dev"));
|
||||||
url: Uri.parse("https://flutter.dev"));
|
|
||||||
}, throwsA(isInstanceOf<ChromeSafariBrowserAlreadyOpenedException>()));
|
}, throwsA(isInstanceOf<ChromeSafariBrowserAlreadyOpenedException>()));
|
||||||
|
|
||||||
await expectLater(chromeSafariBrowser.firstPageLoaded.future, completes);
|
await expectLater(
|
||||||
|
chromeSafariBrowser.firstPageLoaded.future, completes);
|
||||||
await chromeSafariBrowser.close();
|
await chromeSafariBrowser.close();
|
||||||
await chromeSafariBrowser.browserClosed.future;
|
await chromeSafariBrowser.browserClosed.future;
|
||||||
expect(chromeSafariBrowser.isOpened(), false);
|
expect(chromeSafariBrowser.isOpened(), false);
|
||||||
|
@ -5801,19 +5867,15 @@ setTimeout(function() {
|
||||||
url: Uri.parse("https://github.com/flutter"),
|
url: Uri.parse("https://github.com/flutter"),
|
||||||
options: ChromeSafariBrowserClassOptions(
|
options: ChromeSafariBrowserClassOptions(
|
||||||
android: AndroidChromeCustomTabsOptions(
|
android: AndroidChromeCustomTabsOptions(
|
||||||
isTrustedWebActivity: true,
|
isTrustedWebActivity: true, isSingleInstance: true)));
|
||||||
isSingleInstance: true
|
|
||||||
)
|
|
||||||
)
|
|
||||||
);
|
|
||||||
await chromeSafariBrowser.browserCreated.future;
|
await chromeSafariBrowser.browserCreated.future;
|
||||||
expect(chromeSafariBrowser.isOpened(), true);
|
expect(chromeSafariBrowser.isOpened(), true);
|
||||||
expect(() async {
|
expect(() async {
|
||||||
await chromeSafariBrowser.open(
|
await chromeSafariBrowser.open(url: Uri.parse("https://flutter.dev"));
|
||||||
url: Uri.parse("https://flutter.dev"));
|
|
||||||
}, throwsA(isInstanceOf<ChromeSafariBrowserAlreadyOpenedException>()));
|
}, throwsA(isInstanceOf<ChromeSafariBrowserAlreadyOpenedException>()));
|
||||||
|
|
||||||
await expectLater(chromeSafariBrowser.firstPageLoaded.future, completes);
|
await expectLater(
|
||||||
|
chromeSafariBrowser.firstPageLoaded.future, completes);
|
||||||
await chromeSafariBrowser.close();
|
await chromeSafariBrowser.close();
|
||||||
await chromeSafariBrowser.browserClosed.future;
|
await chromeSafariBrowser.browserClosed.future;
|
||||||
expect(chromeSafariBrowser.isOpened(), false);
|
expect(chromeSafariBrowser.isOpened(), false);
|
||||||
|
@ -5837,8 +5899,8 @@ setTimeout(function() {
|
||||||
textDirection: TextDirection.ltr,
|
textDirection: TextDirection.ltr,
|
||||||
child: InAppWebView(
|
child: InAppWebView(
|
||||||
key: GlobalKey(),
|
key: GlobalKey(),
|
||||||
initialUrlRequest:
|
initialUrlRequest: URLRequest(
|
||||||
URLRequest(url: Uri.parse('http://localhost:8080/test_assets/index.html')),
|
url: Uri.parse('http://localhost:8080/test_assets/index.html')),
|
||||||
onWebViewCreated: (controller) {
|
onWebViewCreated: (controller) {
|
||||||
controllerCompleter.complete(controller);
|
controllerCompleter.complete(controller);
|
||||||
},
|
},
|
||||||
|
@ -5846,7 +5908,7 @@ setTimeout(function() {
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
final InAppWebViewController controller =
|
final InAppWebViewController controller =
|
||||||
await controllerCompleter.future;
|
await controllerCompleter.future;
|
||||||
final String? currentUrl = (await controller.getUrl())?.toString();
|
final String? currentUrl = (await controller.getUrl())?.toString();
|
||||||
expect(currentUrl, 'http://localhost:8080/test_assets/index.html');
|
expect(currentUrl, 'http://localhost:8080/test_assets/index.html');
|
||||||
});
|
});
|
||||||
|
|
|
@ -71,8 +71,7 @@ class _ChromeSafariBrowserExampleScreenState
|
||||||
keepAliveEnabled: true,
|
keepAliveEnabled: true,
|
||||||
dismissButtonStyle: DismissButtonStyle.CLOSE,
|
dismissButtonStyle: DismissButtonStyle.CLOSE,
|
||||||
presentationStyle:
|
presentationStyle:
|
||||||
ModalPresentationStyle.OVER_FULL_SCREEN
|
ModalPresentationStyle.OVER_FULL_SCREEN));
|
||||||
));
|
|
||||||
},
|
},
|
||||||
child: Text("Open Chrome Safari Browser")),
|
child: Text("Open Chrome Safari Browser")),
|
||||||
));
|
));
|
||||||
|
|
|
@ -83,7 +83,6 @@ class InAppBrowserExampleScreen extends StatefulWidget {
|
||||||
}
|
}
|
||||||
|
|
||||||
class _InAppBrowserExampleScreenState extends State<InAppBrowserExampleScreen> {
|
class _InAppBrowserExampleScreenState extends State<InAppBrowserExampleScreen> {
|
||||||
|
|
||||||
late PullToRefreshController pullToRefreshController;
|
late PullToRefreshController pullToRefreshController;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -99,7 +98,8 @@ class _InAppBrowserExampleScreenState extends State<InAppBrowserExampleScreen> {
|
||||||
widget.browser.webViewController.reload();
|
widget.browser.webViewController.reload();
|
||||||
} else if (Platform.isIOS) {
|
} else if (Platform.isIOS) {
|
||||||
widget.browser.webViewController.loadUrl(
|
widget.browser.webViewController.loadUrl(
|
||||||
urlRequest: URLRequest(url: await widget.browser.webViewController.getUrl()));
|
urlRequest: URLRequest(
|
||||||
|
url: await widget.browser.webViewController.getUrl()));
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
@ -121,14 +121,14 @@ class _InAppBrowserExampleScreenState extends State<InAppBrowserExampleScreen> {
|
||||||
ElevatedButton(
|
ElevatedButton(
|
||||||
onPressed: () async {
|
onPressed: () async {
|
||||||
await widget.browser.openUrlRequest(
|
await widget.browser.openUrlRequest(
|
||||||
urlRequest:
|
urlRequest:
|
||||||
URLRequest(url: Uri.parse("https://flutter.dev")),
|
URLRequest(url: Uri.parse("https://flutter.dev")),
|
||||||
settings: InAppBrowserClassSettings(
|
settings: InAppBrowserClassSettings(
|
||||||
webViewSettings: InAppWebViewSettings(
|
webViewSettings: InAppWebViewSettings(
|
||||||
useShouldOverrideUrlLoading: true,
|
useShouldOverrideUrlLoading: true,
|
||||||
useOnLoadResource: true,
|
useOnLoadResource: true,
|
||||||
),
|
|
||||||
),
|
),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
child: Text("Open In-App Browser")),
|
child: Text("Open In-App Browser")),
|
||||||
|
|
|
@ -1,12 +1,7 @@
|
||||||
import 'dart:collection';
|
import 'dart:collection';
|
||||||
// import 'dart:convert';
|
|
||||||
import 'dart:io';
|
|
||||||
// import 'dart:typed_data';
|
|
||||||
|
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_inappwebview/flutter_inappwebview.dart';
|
import 'package:flutter_inappwebview/flutter_inappwebview.dart';
|
||||||
// import 'package:path_provider/path_provider.dart';
|
|
||||||
import 'package:url_launcher/url_launcher.dart';
|
import 'package:url_launcher/url_launcher.dart';
|
||||||
|
|
||||||
import 'main.dart';
|
import 'main.dart';
|
||||||
|
@ -43,8 +38,7 @@ class _InAppWebViewExampleScreenState extends State<InAppWebViewExampleScreen> {
|
||||||
contextMenu = ContextMenu(
|
contextMenu = ContextMenu(
|
||||||
menuItems: [
|
menuItems: [
|
||||||
ContextMenuItem(
|
ContextMenuItem(
|
||||||
androidId: 1,
|
id: 1,
|
||||||
iosId: "1",
|
|
||||||
title: "Special",
|
title: "Special",
|
||||||
action: () async {
|
action: () async {
|
||||||
print("Menu item Special clicked!");
|
print("Menu item Special clicked!");
|
||||||
|
@ -62,11 +56,8 @@ class _InAppWebViewExampleScreenState extends State<InAppWebViewExampleScreen> {
|
||||||
print("onHideContextMenu");
|
print("onHideContextMenu");
|
||||||
},
|
},
|
||||||
onContextMenuActionItemClicked: (contextMenuItemClicked) async {
|
onContextMenuActionItemClicked: (contextMenuItemClicked) async {
|
||||||
var id = (Platform.isAndroid)
|
|
||||||
? contextMenuItemClicked.androidId
|
|
||||||
: contextMenuItemClicked.iosId;
|
|
||||||
print("onContextMenuActionItemClicked: " +
|
print("onContextMenuActionItemClicked: " +
|
||||||
id.toString() +
|
contextMenuItemClicked.id.toString() +
|
||||||
" " +
|
" " +
|
||||||
contextMenuItemClicked.title);
|
contextMenuItemClicked.title);
|
||||||
});
|
});
|
||||||
|
@ -76,9 +67,9 @@ class _InAppWebViewExampleScreenState extends State<InAppWebViewExampleScreen> {
|
||||||
color: Colors.blue,
|
color: Colors.blue,
|
||||||
),
|
),
|
||||||
onRefresh: () async {
|
onRefresh: () async {
|
||||||
if (Platform.isAndroid) {
|
if (defaultTargetPlatform == TargetPlatform.android) {
|
||||||
webViewController?.reload();
|
webViewController?.reload();
|
||||||
} else if (Platform.isIOS) {
|
} else if (defaultTargetPlatform == TargetPlatform.iOS) {
|
||||||
webViewController?.loadUrl(
|
webViewController?.loadUrl(
|
||||||
urlRequest: URLRequest(url: await webViewController?.getUrl()));
|
urlRequest: URLRequest(url: await webViewController?.getUrl()));
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,12 +31,12 @@ Future main() async {
|
||||||
ServiceWorkerController serviceWorkerController =
|
ServiceWorkerController serviceWorkerController =
|
||||||
ServiceWorkerController.instance();
|
ServiceWorkerController.instance();
|
||||||
|
|
||||||
serviceWorkerController.serviceWorkerClient = ServiceWorkerClient(
|
await serviceWorkerController.setServiceWorkerClient(ServiceWorkerClient(
|
||||||
shouldInterceptRequest: (request) async {
|
shouldInterceptRequest: (request) async {
|
||||||
print(request);
|
print(request);
|
||||||
return null;
|
return null;
|
||||||
},
|
},
|
||||||
);
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
class ContextMenuOptions: IWebViewSettings<NSObject> {
|
class ContextMenuSettings: ISettings<NSObject> {
|
||||||
|
|
||||||
var hideDefaultSystemContextMenuItems = false;
|
var hideDefaultSystemContextMenuItems = false;
|
||||||
|
|
|
@ -31,7 +31,11 @@ class CredentialDatabase: NSObject, FlutterPlugin {
|
||||||
switch call.method {
|
switch call.method {
|
||||||
case "getAllAuthCredentials":
|
case "getAllAuthCredentials":
|
||||||
var allCredentials: [[String: Any?]] = []
|
var allCredentials: [[String: Any?]] = []
|
||||||
for (protectionSpace, credentials) in CredentialDatabase.credentialStore!.allCredentials {
|
guard let credentialStore = CredentialDatabase.credentialStore else {
|
||||||
|
result(allCredentials)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for (protectionSpace, credentials) in credentialStore.allCredentials {
|
||||||
var crendentials: [[String: Any?]] = []
|
var crendentials: [[String: Any?]] = []
|
||||||
for c in credentials {
|
for c in credentials {
|
||||||
let credential: [String: Any?] = c.value.toMap()
|
let credential: [String: Any?] = c.value.toMap()
|
||||||
|
@ -43,10 +47,17 @@ class CredentialDatabase: NSObject, FlutterPlugin {
|
||||||
"credentials": crendentials
|
"credentials": crendentials
|
||||||
]
|
]
|
||||||
allCredentials.append(dict)
|
allCredentials.append(dict)
|
||||||
} }
|
}
|
||||||
|
}
|
||||||
result(allCredentials)
|
result(allCredentials)
|
||||||
break
|
break
|
||||||
case "getHttpAuthCredentials":
|
case "getHttpAuthCredentials":
|
||||||
|
var crendentials: [[String: Any?]] = []
|
||||||
|
guard let credentialStore = CredentialDatabase.credentialStore else {
|
||||||
|
result(crendentials)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
let host = arguments!["host"] as! String
|
let host = arguments!["host"] as! String
|
||||||
let urlProtocol = arguments!["protocol"] as? String
|
let urlProtocol = arguments!["protocol"] as? String
|
||||||
let urlPort = arguments!["port"] as? Int ?? 0
|
let urlPort = arguments!["port"] as? Int ?? 0
|
||||||
|
@ -54,9 +65,8 @@ class CredentialDatabase: NSObject, FlutterPlugin {
|
||||||
if let r = realm, r.isEmpty {
|
if let r = realm, r.isEmpty {
|
||||||
realm = nil
|
realm = nil
|
||||||
}
|
}
|
||||||
var crendentials: [[String: Any?]] = []
|
|
||||||
|
|
||||||
for (protectionSpace, credentials) in CredentialDatabase.credentialStore!.allCredentials {
|
for (protectionSpace, credentials) in credentialStore.allCredentials {
|
||||||
if protectionSpace.host == host && protectionSpace.realm == realm &&
|
if protectionSpace.host == host && protectionSpace.realm == realm &&
|
||||||
protectionSpace.protocol == urlProtocol && protectionSpace.port == urlPort {
|
protectionSpace.protocol == urlProtocol && protectionSpace.port == urlPort {
|
||||||
for c in credentials {
|
for c in credentials {
|
||||||
|
@ -68,6 +78,11 @@ class CredentialDatabase: NSObject, FlutterPlugin {
|
||||||
result(crendentials)
|
result(crendentials)
|
||||||
break
|
break
|
||||||
case "setHttpAuthCredential":
|
case "setHttpAuthCredential":
|
||||||
|
guard let credentialStore = CredentialDatabase.credentialStore else {
|
||||||
|
result(false)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
let host = arguments!["host"] as! String
|
let host = arguments!["host"] as! String
|
||||||
let urlProtocol = arguments!["protocol"] as? String
|
let urlProtocol = arguments!["protocol"] as? String
|
||||||
let urlPort = arguments!["port"] as? Int ?? 0
|
let urlPort = arguments!["port"] as? Int ?? 0
|
||||||
|
@ -78,11 +93,17 @@ class CredentialDatabase: NSObject, FlutterPlugin {
|
||||||
let username = arguments!["username"] as! String
|
let username = arguments!["username"] as! String
|
||||||
let password = arguments!["password"] as! String
|
let password = arguments!["password"] as! String
|
||||||
let credential = URLCredential(user: username, password: password, persistence: .permanent)
|
let credential = URLCredential(user: username, password: password, persistence: .permanent)
|
||||||
CredentialDatabase.credentialStore!.set(credential,
|
credentialStore.set(credential,
|
||||||
for: URLProtectionSpace(host: host, port: urlPort, protocol: urlProtocol, realm: realm, authenticationMethod: NSURLAuthenticationMethodHTTPBasic))
|
for: URLProtectionSpace(host: host, port: urlPort, protocol: urlProtocol,
|
||||||
|
realm: realm, authenticationMethod: NSURLAuthenticationMethodHTTPBasic))
|
||||||
result(true)
|
result(true)
|
||||||
break
|
break
|
||||||
case "removeHttpAuthCredential":
|
case "removeHttpAuthCredential":
|
||||||
|
guard let credentialStore = CredentialDatabase.credentialStore else {
|
||||||
|
result(false)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
let host = arguments!["host"] as! String
|
let host = arguments!["host"] as! String
|
||||||
let urlProtocol = arguments!["protocol"] as? String
|
let urlProtocol = arguments!["protocol"] as? String
|
||||||
let urlPort = arguments!["port"] as? Int ?? 0
|
let urlPort = arguments!["port"] as? Int ?? 0
|
||||||
|
@ -96,7 +117,7 @@ class CredentialDatabase: NSObject, FlutterPlugin {
|
||||||
var credential: URLCredential? = nil;
|
var credential: URLCredential? = nil;
|
||||||
var protectionSpaceCredential: URLProtectionSpace? = nil
|
var protectionSpaceCredential: URLProtectionSpace? = nil
|
||||||
|
|
||||||
for (protectionSpace, credentials) in CredentialDatabase.credentialStore!.allCredentials {
|
for (protectionSpace, credentials) in credentialStore.allCredentials {
|
||||||
if protectionSpace.host == host && protectionSpace.realm == realm &&
|
if protectionSpace.host == host && protectionSpace.realm == realm &&
|
||||||
protectionSpace.protocol == urlProtocol && protectionSpace.port == urlPort {
|
protectionSpace.protocol == urlProtocol && protectionSpace.port == urlPort {
|
||||||
for c in credentials {
|
for c in credentials {
|
||||||
|
@ -113,12 +134,17 @@ class CredentialDatabase: NSObject, FlutterPlugin {
|
||||||
}
|
}
|
||||||
|
|
||||||
if let c = credential, let protectionSpace = protectionSpaceCredential {
|
if let c = credential, let protectionSpace = protectionSpaceCredential {
|
||||||
CredentialDatabase.credentialStore!.remove(c, for: protectionSpace)
|
credentialStore.remove(c, for: protectionSpace)
|
||||||
}
|
}
|
||||||
|
|
||||||
result(true)
|
result(true)
|
||||||
break
|
break
|
||||||
case "removeHttpAuthCredentials":
|
case "removeHttpAuthCredentials":
|
||||||
|
guard let credentialStore = CredentialDatabase.credentialStore else {
|
||||||
|
result(false)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
let host = arguments!["host"] as! String
|
let host = arguments!["host"] as! String
|
||||||
let urlProtocol = arguments!["protocol"] as? String
|
let urlProtocol = arguments!["protocol"] as? String
|
||||||
let urlPort = arguments!["port"] as? Int ?? 0
|
let urlPort = arguments!["port"] as? Int ?? 0
|
||||||
|
@ -130,7 +156,7 @@ class CredentialDatabase: NSObject, FlutterPlugin {
|
||||||
var credentialsToRemove: [URLCredential] = [];
|
var credentialsToRemove: [URLCredential] = [];
|
||||||
var protectionSpaceCredential: URLProtectionSpace? = nil
|
var protectionSpaceCredential: URLProtectionSpace? = nil
|
||||||
|
|
||||||
for (protectionSpace, credentials) in CredentialDatabase.credentialStore!.allCredentials {
|
for (protectionSpace, credentials) in credentialStore.allCredentials {
|
||||||
if protectionSpace.host == host && protectionSpace.realm == realm &&
|
if protectionSpace.host == host && protectionSpace.realm == realm &&
|
||||||
protectionSpace.protocol == urlProtocol && protectionSpace.port == urlPort {
|
protectionSpace.protocol == urlProtocol && protectionSpace.port == urlPort {
|
||||||
protectionSpaceCredential = protectionSpace
|
protectionSpaceCredential = protectionSpace
|
||||||
|
@ -145,16 +171,21 @@ class CredentialDatabase: NSObject, FlutterPlugin {
|
||||||
|
|
||||||
if let protectionSpace = protectionSpaceCredential {
|
if let protectionSpace = protectionSpaceCredential {
|
||||||
for credential in credentialsToRemove {
|
for credential in credentialsToRemove {
|
||||||
CredentialDatabase.credentialStore!.remove(credential, for: protectionSpace)
|
credentialStore.remove(credential, for: protectionSpace)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
result(true)
|
result(true)
|
||||||
break
|
break
|
||||||
case "clearAllAuthCredentials":
|
case "clearAllAuthCredentials":
|
||||||
for (protectionSpace, credentials) in CredentialDatabase.credentialStore!.allCredentials {
|
guard let credentialStore = CredentialDatabase.credentialStore else {
|
||||||
|
result(false)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for (protectionSpace, credentials) in credentialStore.allCredentials {
|
||||||
for credential in credentials {
|
for credential in credentials {
|
||||||
CredentialDatabase.credentialStore!.remove(credential.value, for: protectionSpace)
|
credentialStore.remove(credential.value, for: protectionSpace)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
result(true)
|
result(true)
|
||||||
|
@ -164,4 +195,11 @@ class CredentialDatabase: NSObject, FlutterPlugin {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public func dispose() {
|
||||||
|
CredentialDatabase.channel?.setMethodCallHandler(nil)
|
||||||
|
CredentialDatabase.channel = nil
|
||||||
|
CredentialDatabase.registrar = nil
|
||||||
|
CredentialDatabase.credentialStore = nil
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -57,4 +57,15 @@ public class HeadlessInAppWebViewManager: NSObject, FlutterPlugin {
|
||||||
headlessInAppWebView.onWebViewCreated()
|
headlessInAppWebView.onWebViewCreated()
|
||||||
flutterWebView.makeInitialLoad(params: params as NSDictionary)
|
flutterWebView.makeInitialLoad(params: params as NSDictionary)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public func dispose() {
|
||||||
|
HeadlessInAppWebViewManager.channel?.setMethodCallHandler(nil)
|
||||||
|
HeadlessInAppWebViewManager.channel = nil
|
||||||
|
HeadlessInAppWebViewManager.registrar = nil
|
||||||
|
let headlessWebViews = HeadlessInAppWebViewManager.webViews.values
|
||||||
|
headlessWebViews.forEach { (headlessWebView: HeadlessInAppWebView) in
|
||||||
|
headlessWebView.dispose()
|
||||||
|
}
|
||||||
|
HeadlessInAppWebViewManager.webViews.removeAll()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,13 +8,13 @@
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
@objcMembers
|
@objcMembers
|
||||||
public class IWebViewSettings<T>: NSObject {
|
public class ISettings<T>: NSObject {
|
||||||
|
|
||||||
override init(){
|
override init(){
|
||||||
super.init()
|
super.init()
|
||||||
}
|
}
|
||||||
|
|
||||||
func parse(settings: [String: Any?]) -> IWebViewSettings {
|
func parse(settings: [String: Any?]) -> ISettings {
|
||||||
for (key, value) in settings {
|
for (key, value) in settings {
|
||||||
if !(value is NSNull), value != nil, self.responds(to: Selector(key)) {
|
if !(value is NSNull), value != nil, self.responds(to: Selector(key)) {
|
||||||
self.setValue(value, forKey: key)
|
self.setValue(value, forKey: key)
|
|
@ -140,4 +140,10 @@ public class InAppBrowserManager: NSObject, FlutterPlugin {
|
||||||
}
|
}
|
||||||
result(true)
|
result(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public func dispose() {
|
||||||
|
InAppBrowserManager.channel?.setMethodCallHandler(nil)
|
||||||
|
InAppBrowserManager.channel = nil
|
||||||
|
InAppBrowserManager.registrar = nil
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
@objcMembers
|
@objcMembers
|
||||||
public class InAppBrowserSettings: IWebViewSettings<InAppBrowserWebViewController> {
|
public class InAppBrowserSettings: ISettings<InAppBrowserWebViewController> {
|
||||||
|
|
||||||
var hidden = false
|
var hidden = false
|
||||||
var hideToolbarTop = true
|
var hideToolbarTop = true
|
||||||
|
|
|
@ -182,6 +182,7 @@ public class InAppBrowserWebViewController: UIViewController, InAppBrowserDelega
|
||||||
|
|
||||||
deinit {
|
deinit {
|
||||||
print("InAppBrowserWebViewController - dealloc")
|
print("InAppBrowserWebViewController - dealloc")
|
||||||
|
dispose()
|
||||||
}
|
}
|
||||||
|
|
||||||
public override func viewDidDisappear(_ animated: Bool) {
|
public override func viewDidDisappear(_ animated: Bool) {
|
||||||
|
@ -550,7 +551,10 @@ public class InAppBrowserWebViewController: UIViewController, InAppBrowserDelega
|
||||||
}
|
}
|
||||||
|
|
||||||
public func dispose() {
|
public func dispose() {
|
||||||
webView.dispose()
|
onExit()
|
||||||
|
channel?.setMethodCallHandler(nil)
|
||||||
|
channel = nil
|
||||||
|
webView?.dispose()
|
||||||
webView = nil
|
webView = nil
|
||||||
view = nil
|
view = nil
|
||||||
if previousStatusBarStyle != -1 {
|
if previousStatusBarStyle != -1 {
|
||||||
|
@ -563,18 +567,15 @@ public class InAppBrowserWebViewController: UIViewController, InAppBrowserDelega
|
||||||
backButton.target = nil
|
backButton.target = nil
|
||||||
reloadButton.target = nil
|
reloadButton.target = nil
|
||||||
shareButton.target = nil
|
shareButton.target = nil
|
||||||
onExit()
|
|
||||||
channel?.setMethodCallHandler(nil)
|
|
||||||
channel = nil
|
|
||||||
methodCallDelegate?.webView = nil
|
methodCallDelegate?.webView = nil
|
||||||
methodCallDelegate = nil
|
methodCallDelegate = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
public func onBrowserCreated() {
|
public func onBrowserCreated() {
|
||||||
channel!.invokeMethod("onBrowserCreated", arguments: [])
|
channel?.invokeMethod("onBrowserCreated", arguments: [])
|
||||||
}
|
}
|
||||||
|
|
||||||
public func onExit() {
|
public func onExit() {
|
||||||
channel!.invokeMethod("onExit", arguments: [])
|
channel?.invokeMethod("onExit", arguments: [])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -164,7 +164,8 @@ public class FlutterWebViewController: NSObject, FlutterPlatformView {
|
||||||
|
|
||||||
func dispose() {
|
func dispose() {
|
||||||
channel?.setMethodCallHandler(nil)
|
channel?.setMethodCallHandler(nil)
|
||||||
methodCallDelegate?.webView = nil
|
channel = nil
|
||||||
|
methodCallDelegate?.dispose()
|
||||||
methodCallDelegate = nil
|
methodCallDelegate = nil
|
||||||
webView?.dispose()
|
webView?.dispose()
|
||||||
webView = nil
|
webView = nil
|
||||||
|
|
|
@ -9,7 +9,10 @@ import Flutter
|
||||||
import Foundation
|
import Foundation
|
||||||
import WebKit
|
import WebKit
|
||||||
|
|
||||||
public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavigationDelegate, WKScriptMessageHandler, UIGestureRecognizerDelegate, PullToRefreshDelegate {
|
public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate,
|
||||||
|
WKNavigationDelegate, WKScriptMessageHandler, UIGestureRecognizerDelegate,
|
||||||
|
WKDownloadDelegate,
|
||||||
|
PullToRefreshDelegate {
|
||||||
|
|
||||||
var windowId: Int64?
|
var windowId: Int64?
|
||||||
var windowCreated = false
|
var windowCreated = false
|
||||||
|
@ -203,13 +206,15 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavi
|
||||||
if let menu = self.contextMenu {
|
if let menu = self.contextMenu {
|
||||||
if let menuItems = menu["menuItems"] as? [[String : Any]] {
|
if let menuItems = menu["menuItems"] as? [[String : Any]] {
|
||||||
for menuItem in menuItems {
|
for menuItem in menuItems {
|
||||||
let id = menuItem["iosId"] as! String
|
let id = menuItem["id"]!
|
||||||
let title = menuItem["title"] as! String
|
let title = menuItem["title"] as! String
|
||||||
let targetMethodName = "onContextMenuActionItemClicked-" + String(self.hash) + "-" + id
|
let targetMethodName = "onContextMenuActionItemClicked-" + String(self.hash) + "-" +
|
||||||
|
(id is Int64 ? String(id as! Int64) : id as! String)
|
||||||
if !self.responds(to: Selector(targetMethodName)) {
|
if !self.responds(to: Selector(targetMethodName)) {
|
||||||
let customAction: () -> Void = {
|
let customAction: () -> Void = {
|
||||||
let arguments: [String: Any?] = [
|
let arguments: [String: Any?] = [
|
||||||
"iosId": id,
|
"id": id,
|
||||||
|
"iosId": id is Int64 ? String(id as! Int64) : id as! String,
|
||||||
"androidId": nil,
|
"androidId": nil,
|
||||||
"title": title
|
"title": title
|
||||||
]
|
]
|
||||||
|
@ -244,10 +249,10 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavi
|
||||||
}
|
}
|
||||||
|
|
||||||
if let menu = contextMenu {
|
if let menu = contextMenu {
|
||||||
let contextMenuOptions = ContextMenuOptions()
|
let contextMenuSettings = ContextMenuSettings()
|
||||||
if let contextMenuOptionsMap = menu["options"] as? [String: Any?] {
|
if let contextMenuSettingsMap = menu["settings"] as? [String: Any?] {
|
||||||
let _ = contextMenuOptions.parse(settings: contextMenuOptionsMap)
|
let _ = contextMenuSettings.parse(settings: contextMenuSettingsMap)
|
||||||
if !action.description.starts(with: "onContextMenuActionItemClicked-") && contextMenuOptions.hideDefaultSystemContextMenuItems {
|
if !action.description.starts(with: "onContextMenuActionItemClicked-") && contextMenuSettings.hideDefaultSystemContextMenuItems {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -256,6 +261,7 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavi
|
||||||
if contextMenuIsShowing, !action.description.starts(with: "onContextMenuActionItemClicked-") {
|
if contextMenuIsShowing, !action.description.starts(with: "onContextMenuActionItemClicked-") {
|
||||||
let id = action.description.compactMap({ $0.asciiValue?.description }).joined()
|
let id = action.description.compactMap({ $0.asciiValue?.description }).joined()
|
||||||
let arguments: [String: Any?] = [
|
let arguments: [String: Any?] = [
|
||||||
|
"id": id,
|
||||||
"iosId": id,
|
"iosId": id,
|
||||||
"androidId": nil,
|
"androidId": nil,
|
||||||
"title": action.description
|
"title": action.description
|
||||||
|
@ -1514,11 +1520,14 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavi
|
||||||
var action = response["action"] as? Int
|
var action = response["action"] as? Int
|
||||||
action = action != nil ? action : 0;
|
action = action != nil ? action : 0;
|
||||||
switch action {
|
switch action {
|
||||||
case 1:
|
case 1:
|
||||||
decisionHandler(.grant)
|
decisionHandler(.grant)
|
||||||
break
|
break
|
||||||
default:
|
case 2:
|
||||||
decisionHandler(.deny)
|
decisionHandler(.prompt)
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
decisionHandler(.deny)
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -1552,11 +1561,14 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavi
|
||||||
var action = response["action"] as? Int
|
var action = response["action"] as? Int
|
||||||
action = action != nil ? action : 0;
|
action = action != nil ? action : 0;
|
||||||
switch action {
|
switch action {
|
||||||
case 1:
|
case 1:
|
||||||
decisionHandler(.grant)
|
decisionHandler(.grant)
|
||||||
break
|
break
|
||||||
default:
|
case 2:
|
||||||
decisionHandler(.deny)
|
decisionHandler(.prompt)
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
decisionHandler(.deny)
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -1575,6 +1587,39 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavi
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@available(iOS 14.5, *)
|
||||||
|
public func download(_ download: WKDownload, decideDestinationUsing response: URLResponse, suggestedFilename: String, completionHandler: @escaping (URL?) -> Void) {
|
||||||
|
if let url = response.url, let useOnDownloadStart = settings?.useOnDownloadStart, useOnDownloadStart {
|
||||||
|
let downloadStartRequest = DownloadStartRequest(url: url.absoluteString,
|
||||||
|
userAgent: nil,
|
||||||
|
contentDisposition: nil,
|
||||||
|
mimeType: response.mimeType,
|
||||||
|
contentLength: response.expectedContentLength,
|
||||||
|
suggestedFilename: suggestedFilename,
|
||||||
|
textEncodingName: response.textEncodingName)
|
||||||
|
onDownloadStartRequest(request: downloadStartRequest)
|
||||||
|
}
|
||||||
|
download.delegate = nil
|
||||||
|
// cancel the download
|
||||||
|
completionHandler(nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
@available(iOS 14.5, *)
|
||||||
|
public func webView(_ webView: WKWebView, navigationResponse: WKNavigationResponse, didBecome download: WKDownload) {
|
||||||
|
let response = navigationResponse.response
|
||||||
|
if let url = response.url, let useOnDownloadStart = settings?.useOnDownloadStart, useOnDownloadStart {
|
||||||
|
let downloadStartRequest = DownloadStartRequest(url: url.absoluteString,
|
||||||
|
userAgent: nil,
|
||||||
|
contentDisposition: nil,
|
||||||
|
mimeType: response.mimeType,
|
||||||
|
contentLength: response.expectedContentLength,
|
||||||
|
suggestedFilename: response.suggestedFilename,
|
||||||
|
textEncodingName: response.textEncodingName)
|
||||||
|
onDownloadStartRequest(request: downloadStartRequest)
|
||||||
|
}
|
||||||
|
download.delegate = nil
|
||||||
|
}
|
||||||
|
|
||||||
public func webView(_ webView: WKWebView,
|
public func webView(_ webView: WKWebView,
|
||||||
decidePolicyFor navigationAction: WKNavigationAction,
|
decidePolicyFor navigationAction: WKNavigationAction,
|
||||||
decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) {
|
decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) {
|
||||||
|
@ -1602,7 +1647,8 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavi
|
||||||
if let r = result {
|
if let r = result {
|
||||||
response = r as! [String: Any]
|
response = r as! [String: Any]
|
||||||
let action = response["action"] as? Int
|
let action = response["action"] as? Int
|
||||||
let navigationActionPolicy = WKNavigationActionPolicy.init(rawValue: action ?? WKNavigationActionPolicy.cancel.rawValue) ??
|
let navigationActionPolicy = WKNavigationActionPolicy
|
||||||
|
.init(rawValue: action ?? WKNavigationActionPolicy.cancel.rawValue) ??
|
||||||
WKNavigationActionPolicy.cancel
|
WKNavigationActionPolicy.cancel
|
||||||
decisionHandler(navigationActionPolicy)
|
decisionHandler(navigationActionPolicy)
|
||||||
return;
|
return;
|
||||||
|
@ -1644,15 +1690,11 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavi
|
||||||
var response: [String: Any]
|
var response: [String: Any]
|
||||||
if let r = result {
|
if let r = result {
|
||||||
response = r as! [String: Any]
|
response = r as! [String: Any]
|
||||||
var action = response["action"] as? Int
|
let action = response["action"] as? Int
|
||||||
action = action != nil ? action : 0;
|
let navigationActionPolicy = WKNavigationResponsePolicy
|
||||||
switch action {
|
.init(rawValue: action ?? WKNavigationResponsePolicy.cancel.rawValue) ??
|
||||||
case 1:
|
WKNavigationResponsePolicy.cancel
|
||||||
decisionHandler(.allow)
|
decisionHandler(navigationActionPolicy)
|
||||||
break
|
|
||||||
default:
|
|
||||||
decisionHandler(.cancel)
|
|
||||||
}
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
decisionHandler(.allow)
|
decisionHandler(.allow)
|
||||||
|
@ -1661,21 +1703,26 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavi
|
||||||
}
|
}
|
||||||
|
|
||||||
if let useOnDownloadStart = settings?.useOnDownloadStart, useOnDownloadStart {
|
if let useOnDownloadStart = settings?.useOnDownloadStart, useOnDownloadStart {
|
||||||
let mimeType = navigationResponse.response.mimeType
|
if #available(iOS 14.5, *), !navigationResponse.canShowMIMEType {
|
||||||
if let url = navigationResponse.response.url, navigationResponse.isForMainFrame {
|
decisionHandler(.download)
|
||||||
if url.scheme != "file", mimeType != nil, !mimeType!.starts(with: "text/") {
|
return
|
||||||
let downloadStartRequest = DownloadStartRequest(url: url.absoluteString,
|
} else {
|
||||||
userAgent: nil,
|
let mimeType = navigationResponse.response.mimeType
|
||||||
contentDisposition: nil,
|
if let url = navigationResponse.response.url, navigationResponse.isForMainFrame {
|
||||||
mimeType: mimeType,
|
if url.scheme != "file", mimeType != nil, !mimeType!.starts(with: "text/") {
|
||||||
contentLength: navigationResponse.response.expectedContentLength,
|
let downloadStartRequest = DownloadStartRequest(url: url.absoluteString,
|
||||||
suggestedFilename: navigationResponse.response.suggestedFilename,
|
userAgent: nil,
|
||||||
textEncodingName: navigationResponse.response.textEncodingName)
|
contentDisposition: nil,
|
||||||
onDownloadStartRequest(request: downloadStartRequest)
|
mimeType: mimeType,
|
||||||
if useOnNavigationResponse == nil || !useOnNavigationResponse! {
|
contentLength: navigationResponse.response.expectedContentLength,
|
||||||
decisionHandler(.cancel)
|
suggestedFilename: navigationResponse.response.suggestedFilename,
|
||||||
|
textEncodingName: navigationResponse.response.textEncodingName)
|
||||||
|
onDownloadStartRequest(request: downloadStartRequest)
|
||||||
|
if useOnNavigationResponse == nil || !useOnNavigationResponse! {
|
||||||
|
decisionHandler(.cancel)
|
||||||
|
}
|
||||||
|
return
|
||||||
}
|
}
|
||||||
return
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1791,8 +1838,8 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavi
|
||||||
completionHandler(.useCredential, credential)
|
completionHandler(.useCredential, credential)
|
||||||
break
|
break
|
||||||
case 2:
|
case 2:
|
||||||
if InAppWebView.credentialsProposed.count == 0 {
|
if InAppWebView.credentialsProposed.count == 0, let credentialStore = CredentialDatabase.credentialStore {
|
||||||
for (protectionSpace, credentials) in CredentialDatabase.credentialStore!.allCredentials {
|
for (protectionSpace, credentials) in credentialStore.allCredentials {
|
||||||
if protectionSpace.host == host && protectionSpace.realm == realm &&
|
if protectionSpace.host == host && protectionSpace.realm == realm &&
|
||||||
protectionSpace.protocol == prot && protectionSpace.port == port {
|
protectionSpace.protocol == prot && protectionSpace.port == port {
|
||||||
for credential in credentials {
|
for credential in credentials {
|
||||||
|
@ -3023,6 +3070,12 @@ if(window.\(JAVASCRIPT_BRIDGE_NAME)[\(_callHandlerID)] != null) {
|
||||||
}
|
}
|
||||||
|
|
||||||
public func dispose() {
|
public func dispose() {
|
||||||
|
channel = nil
|
||||||
|
removeObserver(self, forKeyPath: #keyPath(WKWebView.estimatedProgress))
|
||||||
|
removeObserver(self, forKeyPath: #keyPath(WKWebView.url))
|
||||||
|
removeObserver(self, forKeyPath: #keyPath(WKWebView.title))
|
||||||
|
scrollView.removeObserver(self, forKeyPath: #keyPath(UIScrollView.contentOffset))
|
||||||
|
scrollView.removeObserver(self, forKeyPath: #keyPath(UIScrollView.zoomScale))
|
||||||
resumeTimers()
|
resumeTimers()
|
||||||
stopLoading()
|
stopLoading()
|
||||||
disposeWebMessageChannels()
|
disposeWebMessageChannels()
|
||||||
|
@ -3043,15 +3096,10 @@ if(window.\(JAVASCRIPT_BRIDGE_NAME)[\(_callHandlerID)] != null) {
|
||||||
InAppWebView.windowWebViews.removeValue(forKey: wId)
|
InAppWebView.windowWebViews.removeValue(forKey: wId)
|
||||||
}
|
}
|
||||||
configuration.userContentController.dispose(windowId: windowId)
|
configuration.userContentController.dispose(windowId: windowId)
|
||||||
removeObserver(self, forKeyPath: #keyPath(WKWebView.estimatedProgress))
|
|
||||||
removeObserver(self, forKeyPath: #keyPath(WKWebView.url))
|
|
||||||
removeObserver(self, forKeyPath: #keyPath(WKWebView.title))
|
|
||||||
NotificationCenter.default.removeObserver(self)
|
NotificationCenter.default.removeObserver(self)
|
||||||
for imp in customIMPs {
|
for imp in customIMPs {
|
||||||
imp_removeBlock(imp)
|
imp_removeBlock(imp)
|
||||||
}
|
}
|
||||||
scrollView.removeObserver(self, forKeyPath: #keyPath(UIScrollView.contentOffset))
|
|
||||||
scrollView.removeObserver(self, forKeyPath: #keyPath(UIScrollView.zoomScale))
|
|
||||||
longPressRecognizer.removeTarget(self, action: #selector(longPressGestureDetected))
|
longPressRecognizer.removeTarget(self, action: #selector(longPressGestureDetected))
|
||||||
longPressRecognizer.delegate = nil
|
longPressRecognizer.delegate = nil
|
||||||
scrollView.removeGestureRecognizer(longPressRecognizer)
|
scrollView.removeGestureRecognizer(longPressRecognizer)
|
||||||
|
@ -3068,7 +3116,6 @@ if(window.\(JAVASCRIPT_BRIDGE_NAME)[\(_callHandlerID)] != null) {
|
||||||
navigationDelegate = nil
|
navigationDelegate = nil
|
||||||
scrollView.delegate = nil
|
scrollView.delegate = nil
|
||||||
isPausedTimersCompletionHandler = nil
|
isPausedTimersCompletionHandler = nil
|
||||||
channel = nil
|
|
||||||
SharedLastTouchPointTimestamp.removeValue(forKey: self)
|
SharedLastTouchPointTimestamp.removeValue(forKey: self)
|
||||||
callAsyncJavaScriptBelowIOS14Results.removeAll()
|
callAsyncJavaScriptBelowIOS14Results.removeAll()
|
||||||
super.removeFromSuperview()
|
super.removeFromSuperview()
|
||||||
|
|
|
@ -9,7 +9,7 @@ import Foundation
|
||||||
import WebKit
|
import WebKit
|
||||||
|
|
||||||
@objcMembers
|
@objcMembers
|
||||||
public class InAppWebViewSettings: IWebViewSettings<InAppWebView> {
|
public class InAppWebViewSettings: ISettings<InAppWebView> {
|
||||||
|
|
||||||
var useShouldOverrideUrlLoading = false
|
var useShouldOverrideUrlLoading = false
|
||||||
var useOnLoadResource = false
|
var useOnLoadResource = false
|
||||||
|
|
|
@ -598,8 +598,12 @@ public class InAppWebViewMethodHandler: FlutterMethodCallDelegate {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
deinit {
|
public func dispose() {
|
||||||
print("InAppWebViewMethodHandler - dealloc")
|
|
||||||
webView = nil
|
webView = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
deinit {
|
||||||
|
print("InAppWebViewMethodHandler - dealloc")
|
||||||
|
dispose()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -72,4 +72,12 @@ class InAppWebViewStatic: NSObject, FlutterPlugin {
|
||||||
completionHandler(defaultUserAgent)
|
completionHandler(defaultUserAgent)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public func dispose() {
|
||||||
|
InAppWebViewStatic.channel?.setMethodCallHandler(nil)
|
||||||
|
InAppWebViewStatic.channel = nil
|
||||||
|
InAppWebViewStatic.registrar = nil
|
||||||
|
InAppWebViewStatic.webViewForUserAgent = nil
|
||||||
|
InAppWebViewStatic.defaultUserAgent = nil
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -100,6 +100,11 @@ class MyCookieManager: NSObject, FlutterPlugin {
|
||||||
isHttpOnly: Bool?,
|
isHttpOnly: Bool?,
|
||||||
sameSite: String?,
|
sameSite: String?,
|
||||||
result: @escaping FlutterResult) {
|
result: @escaping FlutterResult) {
|
||||||
|
guard let httpCookieStore = MyCookieManager.httpCookieStore else {
|
||||||
|
result(false)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
var properties: [HTTPCookiePropertyKey: Any] = [:]
|
var properties: [HTTPCookiePropertyKey: Any] = [:]
|
||||||
properties[.originURL] = url
|
properties[.originURL] = url
|
||||||
properties[.name] = name
|
properties[.name] = name
|
||||||
|
@ -138,7 +143,7 @@ class MyCookieManager: NSObject, FlutterPlugin {
|
||||||
|
|
||||||
let cookie = HTTPCookie(properties: properties)!
|
let cookie = HTTPCookie(properties: properties)!
|
||||||
|
|
||||||
MyCookieManager.httpCookieStore!.setCookie(cookie, completionHandler: {() in
|
httpCookieStore.setCookie(cookie, completionHandler: {() in
|
||||||
result(true)
|
result(true)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -146,8 +151,13 @@ class MyCookieManager: NSObject, FlutterPlugin {
|
||||||
public static func getCookies(url: String, result: @escaping FlutterResult) {
|
public static func getCookies(url: String, result: @escaping FlutterResult) {
|
||||||
var cookieList: [[String: Any?]] = []
|
var cookieList: [[String: Any?]] = []
|
||||||
|
|
||||||
|
guard let httpCookieStore = MyCookieManager.httpCookieStore else {
|
||||||
|
result(cookieList)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
if let urlHost = URL(string: url)?.host {
|
if let urlHost = URL(string: url)?.host {
|
||||||
MyCookieManager.httpCookieStore!.getAllCookies { (cookies) in
|
httpCookieStore.getAllCookies { (cookies) in
|
||||||
for cookie in cookies {
|
for cookie in cookies {
|
||||||
if urlHost.hasSuffix(cookie.domain) || ".\(urlHost)".hasSuffix(cookie.domain) {
|
if urlHost.hasSuffix(cookie.domain) || ".\(urlHost)".hasSuffix(cookie.domain) {
|
||||||
var sameSite: String? = nil
|
var sameSite: String? = nil
|
||||||
|
@ -189,7 +199,12 @@ class MyCookieManager: NSObject, FlutterPlugin {
|
||||||
public static func getAllCookies(result: @escaping FlutterResult) {
|
public static func getAllCookies(result: @escaping FlutterResult) {
|
||||||
var cookieList: [[String: Any?]] = []
|
var cookieList: [[String: Any?]] = []
|
||||||
|
|
||||||
MyCookieManager.httpCookieStore!.getAllCookies { (cookies) in
|
guard let httpCookieStore = MyCookieManager.httpCookieStore else {
|
||||||
|
result(cookieList)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
httpCookieStore.getAllCookies { (cookies) in
|
||||||
for cookie in cookies {
|
for cookie in cookies {
|
||||||
var sameSite: String? = nil
|
var sameSite: String? = nil
|
||||||
if #available(iOS 13.0, *) {
|
if #available(iOS 13.0, *) {
|
||||||
|
@ -221,7 +236,12 @@ class MyCookieManager: NSObject, FlutterPlugin {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static func deleteCookie(url: String, name: String, domain: String, path: String, result: @escaping FlutterResult) {
|
public static func deleteCookie(url: String, name: String, domain: String, path: String, result: @escaping FlutterResult) {
|
||||||
MyCookieManager.httpCookieStore!.getAllCookies { (cookies) in
|
guard let httpCookieStore = MyCookieManager.httpCookieStore else {
|
||||||
|
result(false)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
httpCookieStore.getAllCookies { (cookies) in
|
||||||
for cookie in cookies {
|
for cookie in cookies {
|
||||||
var originURL = ""
|
var originURL = ""
|
||||||
if cookie.properties![.originURL] is String {
|
if cookie.properties![.originURL] is String {
|
||||||
|
@ -234,7 +254,7 @@ class MyCookieManager: NSObject, FlutterPlugin {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if (cookie.domain == domain || cookie.domain == ".\(domain)" || ".\(cookie.domain)" == domain) && cookie.name == name && cookie.path == path {
|
if (cookie.domain == domain || cookie.domain == ".\(domain)" || ".\(cookie.domain)" == domain) && cookie.name == name && cookie.path == path {
|
||||||
MyCookieManager.httpCookieStore!.delete(cookie, completionHandler: {
|
httpCookieStore.delete(cookie, completionHandler: {
|
||||||
result(true)
|
result(true)
|
||||||
})
|
})
|
||||||
return
|
return
|
||||||
|
@ -245,7 +265,12 @@ class MyCookieManager: NSObject, FlutterPlugin {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static func deleteCookies(url: String, domain: String, path: String, result: @escaping FlutterResult) {
|
public static func deleteCookies(url: String, domain: String, path: String, result: @escaping FlutterResult) {
|
||||||
MyCookieManager.httpCookieStore!.getAllCookies { (cookies) in
|
guard let httpCookieStore = MyCookieManager.httpCookieStore else {
|
||||||
|
result(false)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
httpCookieStore.getAllCookies { (cookies) in
|
||||||
for cookie in cookies {
|
for cookie in cookies {
|
||||||
var originURL = ""
|
var originURL = ""
|
||||||
if cookie.properties![.originURL] is String {
|
if cookie.properties![.originURL] is String {
|
||||||
|
@ -258,7 +283,7 @@ class MyCookieManager: NSObject, FlutterPlugin {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if (cookie.domain == domain || cookie.domain == ".\(domain)" || ".\(cookie.domain)" == domain) && cookie.path == path {
|
if (cookie.domain == domain || cookie.domain == ".\(domain)" || ".\(cookie.domain)" == domain) && cookie.path == path {
|
||||||
MyCookieManager.httpCookieStore!.delete(cookie, completionHandler: nil)
|
httpCookieStore.delete(cookie, completionHandler: nil)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
result(true)
|
result(true)
|
||||||
|
@ -272,4 +297,11 @@ class MyCookieManager: NSObject, FlutterPlugin {
|
||||||
result(true)
|
result(true)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public func dispose() {
|
||||||
|
MyCookieManager.channel?.setMethodCallHandler(nil)
|
||||||
|
MyCookieManager.channel = nil
|
||||||
|
MyCookieManager.registrar = nil
|
||||||
|
MyCookieManager.httpCookieStore = nil
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -53,7 +53,13 @@ class MyWebStorageManager: NSObject, FlutterPlugin {
|
||||||
|
|
||||||
public static func fetchDataRecords(dataTypes: Set<String>, result: @escaping FlutterResult) {
|
public static func fetchDataRecords(dataTypes: Set<String>, result: @escaping FlutterResult) {
|
||||||
var recordList: [[String: Any?]] = []
|
var recordList: [[String: Any?]] = []
|
||||||
MyWebStorageManager.websiteDataStore!.fetchDataRecords(ofTypes: dataTypes) { (data) in
|
|
||||||
|
guard let websiteDataStore = MyWebStorageManager.websiteDataStore else {
|
||||||
|
result(recordList)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
websiteDataStore.fetchDataRecords(ofTypes: dataTypes) { (data) in
|
||||||
for record in data {
|
for record in data {
|
||||||
recordList.append([
|
recordList.append([
|
||||||
"displayName": record.displayName,
|
"displayName": record.displayName,
|
||||||
|
@ -68,7 +74,13 @@ class MyWebStorageManager: NSObject, FlutterPlugin {
|
||||||
|
|
||||||
public static func removeDataFor(dataTypes: Set<String>, recordList: [[String: Any?]], result: @escaping FlutterResult) {
|
public static func removeDataFor(dataTypes: Set<String>, recordList: [[String: Any?]], result: @escaping FlutterResult) {
|
||||||
var records: [WKWebsiteDataRecord] = []
|
var records: [WKWebsiteDataRecord] = []
|
||||||
MyWebStorageManager.websiteDataStore!.fetchDataRecords(ofTypes: dataTypes) { (data) in
|
|
||||||
|
guard let websiteDataStore = MyWebStorageManager.websiteDataStore else {
|
||||||
|
result(false)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
websiteDataStore.fetchDataRecords(ofTypes: dataTypes) { (data) in
|
||||||
for record in data {
|
for record in data {
|
||||||
for r in recordList {
|
for r in recordList {
|
||||||
let displayName = r["displayName"] as! String
|
let displayName = r["displayName"] as! String
|
||||||
|
@ -78,16 +90,28 @@ class MyWebStorageManager: NSObject, FlutterPlugin {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
MyWebStorageManager.websiteDataStore!.removeData(ofTypes: dataTypes, for: records) {
|
websiteDataStore.removeData(ofTypes: dataTypes, for: records) {
|
||||||
result(true)
|
result(true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static func removeDataModifiedSince(dataTypes: Set<String>, timestamp: Int64, result: @escaping FlutterResult) {
|
public static func removeDataModifiedSince(dataTypes: Set<String>, timestamp: Int64, result: @escaping FlutterResult) {
|
||||||
|
guard let websiteDataStore = MyWebStorageManager.websiteDataStore else {
|
||||||
|
result(false)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
let date = NSDate(timeIntervalSince1970: TimeInterval(timestamp))
|
let date = NSDate(timeIntervalSince1970: TimeInterval(timestamp))
|
||||||
MyWebStorageManager.websiteDataStore!.removeData(ofTypes: dataTypes, modifiedSince: date as Date) {
|
websiteDataStore.removeData(ofTypes: dataTypes, modifiedSince: date as Date) {
|
||||||
result(true)
|
result(true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public func dispose() {
|
||||||
|
MyWebStorageManager.channel?.setMethodCallHandler(nil)
|
||||||
|
MyWebStorageManager.channel = nil
|
||||||
|
MyWebStorageManager.registrar = nil
|
||||||
|
MyWebStorageManager.websiteDataStore = nil
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -60,4 +60,10 @@ class PlatformUtil: NSObject, FlutterPlugin {
|
||||||
formatter.timeZone = timezone
|
formatter.timeZone = timezone
|
||||||
return formatter.string(from: PlatformUtil.getDateFromMilliseconds(date: date))
|
return formatter.string(from: PlatformUtil.getDateFromMilliseconds(date: date))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public func dispose() {
|
||||||
|
PlatformUtil.channel?.setMethodCallHandler(nil)
|
||||||
|
PlatformUtil.channel = nil
|
||||||
|
PlatformUtil.registrar = nil
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -109,5 +109,6 @@ public class PullToRefreshControl : UIRefreshControl, FlutterPlugin {
|
||||||
|
|
||||||
deinit {
|
deinit {
|
||||||
print("PullToRefreshControl - dealloc")
|
print("PullToRefreshControl - dealloc")
|
||||||
|
dispose()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
public class PullToRefreshSettings : IWebViewSettings<PullToRefreshControl> {
|
public class PullToRefreshSettings : ISettings<PullToRefreshControl> {
|
||||||
|
|
||||||
var enabled = true
|
var enabled = true
|
||||||
var color: String?
|
var color: String?
|
||||||
|
|
|
@ -91,4 +91,10 @@ public class ChromeSafariBrowserManager: NSObject, FlutterPlugin {
|
||||||
|
|
||||||
result(FlutterError.init(code: "ChromeSafariBrowserManager", message: "SafariViewController is not available!", details: nil))
|
result(FlutterError.init(code: "ChromeSafariBrowserManager", message: "SafariViewController is not available!", details: nil))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public func dispose() {
|
||||||
|
ChromeSafariBrowserManager.channel?.setMethodCallHandler(nil)
|
||||||
|
ChromeSafariBrowserManager.channel = nil
|
||||||
|
ChromeSafariBrowserManager.registrar = nil
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,7 +9,7 @@ import Foundation
|
||||||
|
|
||||||
@available(iOS 9.0, *)
|
@available(iOS 9.0, *)
|
||||||
@objcMembers
|
@objcMembers
|
||||||
public class SafariBrowserSettings: IWebViewSettings<SafariViewController> {
|
public class SafariBrowserSettings: ISettings<SafariViewController> {
|
||||||
|
|
||||||
var entersReaderIfAvailable = false
|
var entersReaderIfAvailable = false
|
||||||
var barCollapsingEnabled = false
|
var barCollapsingEnabled = false
|
||||||
|
|
|
@ -22,6 +22,7 @@ public class SafariViewController: SFSafariViewController, FlutterPlugin, SFSafa
|
||||||
|
|
||||||
deinit {
|
deinit {
|
||||||
print("SafariViewController - dealloc")
|
print("SafariViewController - dealloc")
|
||||||
|
dispose()
|
||||||
}
|
}
|
||||||
|
|
||||||
public func prepareMethodChannel() {
|
public func prepareMethodChannel() {
|
||||||
|
@ -123,20 +124,21 @@ public class SafariViewController: SFSafariViewController, FlutterPlugin, SFSafa
|
||||||
// }
|
// }
|
||||||
|
|
||||||
public func onChromeSafariBrowserOpened() {
|
public func onChromeSafariBrowserOpened() {
|
||||||
channel!.invokeMethod("onChromeSafariBrowserOpened", arguments: [])
|
channel?.invokeMethod("onChromeSafariBrowserOpened", arguments: [])
|
||||||
}
|
}
|
||||||
|
|
||||||
public func onChromeSafariBrowserCompletedInitialLoad() {
|
public func onChromeSafariBrowserCompletedInitialLoad() {
|
||||||
channel!.invokeMethod("onChromeSafariBrowserCompletedInitialLoad", arguments: [])
|
channel?.invokeMethod("onChromeSafariBrowserCompletedInitialLoad", arguments: [])
|
||||||
}
|
}
|
||||||
|
|
||||||
public func onChromeSafariBrowserClosed() {
|
public func onChromeSafariBrowserClosed() {
|
||||||
channel!.invokeMethod("onChromeSafariBrowserClosed", arguments: [])
|
channel?.invokeMethod("onChromeSafariBrowserClosed", arguments: [])
|
||||||
}
|
}
|
||||||
|
|
||||||
public func dispose() {
|
public func dispose() {
|
||||||
|
channel?.setMethodCallHandler(nil)
|
||||||
|
channel = nil
|
||||||
delegate = nil
|
delegate = nil
|
||||||
channel!.setMethodCallHandler(nil)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -180,7 +182,12 @@ class CustomUIActivity : UIActivity {
|
||||||
}
|
}
|
||||||
|
|
||||||
override func perform() {
|
override func perform() {
|
||||||
let channel = FlutterMethodChannel(name: "com.pichillilorenzo/flutter_chromesafaribrowser_" + viewId, binaryMessenger: SwiftFlutterPlugin.instance!.registrar!.messenger())
|
guard let registrar = SwiftFlutterPlugin.instance?.registrar else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
let channel = FlutterMethodChannel(name: "com.pichillilorenzo/flutter_chromesafaribrowser_" + viewId,
|
||||||
|
binaryMessenger: registrar.messenger())
|
||||||
|
|
||||||
let arguments: [String: Any?] = [
|
let arguments: [String: Any?] = [
|
||||||
"url": url.absoluteString,
|
"url": url.absoluteString,
|
||||||
|
|
|
@ -49,16 +49,39 @@ public class SwiftFlutterPlugin: NSObject, FlutterPlugin {
|
||||||
headlessInAppWebViewManager = HeadlessInAppWebViewManager(registrar: registrar)
|
headlessInAppWebViewManager = HeadlessInAppWebViewManager(registrar: registrar)
|
||||||
chromeSafariBrowserManager = ChromeSafariBrowserManager(registrar: registrar)
|
chromeSafariBrowserManager = ChromeSafariBrowserManager(registrar: registrar)
|
||||||
inAppWebViewStatic = InAppWebViewStatic(registrar: registrar)
|
inAppWebViewStatic = InAppWebViewStatic(registrar: registrar)
|
||||||
|
credentialDatabase = CredentialDatabase(registrar: registrar)
|
||||||
if #available(iOS 11.0, *) {
|
if #available(iOS 11.0, *) {
|
||||||
myCookieManager = MyCookieManager(registrar: registrar)
|
myCookieManager = MyCookieManager(registrar: registrar)
|
||||||
}
|
}
|
||||||
if #available(iOS 9.0, *) {
|
if #available(iOS 9.0, *) {
|
||||||
myWebStorageManager = MyWebStorageManager(registrar: registrar)
|
myWebStorageManager = MyWebStorageManager(registrar: registrar)
|
||||||
}
|
}
|
||||||
credentialDatabase = CredentialDatabase(registrar: registrar)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static func register(with registrar: FlutterPluginRegistrar) {
|
public static func register(with registrar: FlutterPluginRegistrar) {
|
||||||
SwiftFlutterPlugin.instance = SwiftFlutterPlugin(with: registrar)
|
SwiftFlutterPlugin.instance = SwiftFlutterPlugin(with: registrar)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public func detachFromEngine(for registrar: FlutterPluginRegistrar) {
|
||||||
|
platformUtil?.dispose()
|
||||||
|
platformUtil = nil
|
||||||
|
inAppBrowserManager?.dispose()
|
||||||
|
inAppBrowserManager = nil
|
||||||
|
headlessInAppWebViewManager?.dispose()
|
||||||
|
headlessInAppWebViewManager = nil
|
||||||
|
chromeSafariBrowserManager?.dispose()
|
||||||
|
chromeSafariBrowserManager = nil
|
||||||
|
inAppWebViewStatic?.dispose()
|
||||||
|
inAppWebViewStatic = nil
|
||||||
|
credentialDatabase?.dispose()
|
||||||
|
credentialDatabase = nil
|
||||||
|
if #available(iOS 11.0, *) {
|
||||||
|
(myCookieManager as! MyCookieManager?)?.dispose()
|
||||||
|
myCookieManager = nil
|
||||||
|
}
|
||||||
|
if #available(iOS 9.0, *) {
|
||||||
|
(myWebStorageManager as! MyWebStorageManager?)?.dispose()
|
||||||
|
myWebStorageManager = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,6 +10,10 @@ import WebKit
|
||||||
|
|
||||||
extension WKNavigationAction {
|
extension WKNavigationAction {
|
||||||
public func toMap () -> [String:Any?] {
|
public func toMap () -> [String:Any?] {
|
||||||
|
var shouldPerformDownload: Bool? = nil
|
||||||
|
if #available(iOS 14.5, *) {
|
||||||
|
shouldPerformDownload = self.shouldPerformDownload
|
||||||
|
}
|
||||||
return [
|
return [
|
||||||
"request": request.toMap(),
|
"request": request.toMap(),
|
||||||
"isForMainFrame": targetFrame?.isMainFrame ?? false,
|
"isForMainFrame": targetFrame?.isMainFrame ?? false,
|
||||||
|
@ -17,7 +21,8 @@ extension WKNavigationAction {
|
||||||
"isRedirect": nil,
|
"isRedirect": nil,
|
||||||
"navigationType": navigationType.rawValue,
|
"navigationType": navigationType.rawValue,
|
||||||
"sourceFrame": sourceFrame.toMap(),
|
"sourceFrame": sourceFrame.toMap(),
|
||||||
"targetFrame": targetFrame?.toMap()
|
"targetFrame": targetFrame?.toMap(),
|
||||||
|
"shouldPerformDownload": shouldPerformDownload
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,7 +21,7 @@ extension WKUserContentController {
|
||||||
var contentWorlds: Set<WKContentWorld> {
|
var contentWorlds: Set<WKContentWorld> {
|
||||||
get {
|
get {
|
||||||
let tmpAddress = String(format: "%p", unsafeBitCast(self, to: Int.self))
|
let tmpAddress = String(format: "%p", unsafeBitCast(self, to: Int.self))
|
||||||
return WKUserContentController._contentWorlds[tmpAddress]!
|
return WKUserContentController._contentWorlds[tmpAddress] ?? []
|
||||||
}
|
}
|
||||||
set(newValue) {
|
set(newValue) {
|
||||||
let tmpAddress = String(format: "%p", unsafeBitCast(self, to: Int.self))
|
let tmpAddress = String(format: "%p", unsafeBitCast(self, to: Int.self))
|
||||||
|
@ -33,7 +33,7 @@ extension WKUserContentController {
|
||||||
var userOnlyScripts: [WKUserScriptInjectionTime:OrderedSet<UserScript>] {
|
var userOnlyScripts: [WKUserScriptInjectionTime:OrderedSet<UserScript>] {
|
||||||
get {
|
get {
|
||||||
let tmpAddress = String(format: "%p", unsafeBitCast(self, to: Int.self))
|
let tmpAddress = String(format: "%p", unsafeBitCast(self, to: Int.self))
|
||||||
return WKUserContentController._userOnlyScripts[tmpAddress]!
|
return WKUserContentController._userOnlyScripts[tmpAddress] ?? [:]
|
||||||
}
|
}
|
||||||
set(newValue) {
|
set(newValue) {
|
||||||
let tmpAddress = String(format: "%p", unsafeBitCast(self, to: Int.self))
|
let tmpAddress = String(format: "%p", unsafeBitCast(self, to: Int.self))
|
||||||
|
@ -45,7 +45,7 @@ extension WKUserContentController {
|
||||||
var pluginScripts: [WKUserScriptInjectionTime:OrderedSet<PluginScript>] {
|
var pluginScripts: [WKUserScriptInjectionTime:OrderedSet<PluginScript>] {
|
||||||
get {
|
get {
|
||||||
let tmpAddress = String(format: "%p", unsafeBitCast(self, to: Int.self))
|
let tmpAddress = String(format: "%p", unsafeBitCast(self, to: Int.self))
|
||||||
return WKUserContentController._pluginScripts[tmpAddress]!
|
return WKUserContentController._pluginScripts[tmpAddress] ?? [:]
|
||||||
}
|
}
|
||||||
set(newValue) {
|
set(newValue) {
|
||||||
let tmpAddress = String(format: "%p", unsafeBitCast(self, to: Int.self))
|
let tmpAddress = String(format: "%p", unsafeBitCast(self, to: Int.self))
|
||||||
|
|
|
@ -23,5 +23,6 @@ public class WebMessage : NSObject {
|
||||||
|
|
||||||
deinit {
|
deinit {
|
||||||
print("WebMessage - dealloc")
|
print("WebMessage - dealloc")
|
||||||
|
dispose()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -126,6 +126,7 @@ public class WebMessageChannel : FlutterMethodCallDelegate {
|
||||||
|
|
||||||
public func dispose() {
|
public func dispose() {
|
||||||
channel?.setMethodCallHandler(nil)
|
channel?.setMethodCallHandler(nil)
|
||||||
|
channel = nil
|
||||||
for port in ports {
|
for port in ports {
|
||||||
port.dispose()
|
port.dispose()
|
||||||
}
|
}
|
||||||
|
@ -140,11 +141,11 @@ public class WebMessageChannel : FlutterMethodCallDelegate {
|
||||||
}
|
}
|
||||||
})();
|
})();
|
||||||
""")
|
""")
|
||||||
channel = nil
|
|
||||||
webView = nil
|
webView = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
deinit {
|
deinit {
|
||||||
print("WebMessageChannel - dealloc")
|
print("WebMessageChannel - dealloc")
|
||||||
|
dispose()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -222,5 +222,6 @@ public class WebMessageListener : FlutterMethodCallDelegate {
|
||||||
|
|
||||||
deinit {
|
deinit {
|
||||||
print("WebMessageListener - dealloc")
|
print("WebMessageListener - dealloc")
|
||||||
|
dispose()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -118,5 +118,6 @@ public class WebMessagePort : NSObject {
|
||||||
|
|
||||||
deinit {
|
deinit {
|
||||||
print("WebMessagePort - dealloc")
|
print("WebMessagePort - dealloc")
|
||||||
|
dispose()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,7 +12,7 @@ var SharedLastTouchPointTimestamp: [InAppWebView: Int64] = [:]
|
||||||
|
|
||||||
public class Util {
|
public class Util {
|
||||||
public static func getUrlAsset(assetFilePath: String) throws -> URL {
|
public static func getUrlAsset(assetFilePath: String) throws -> URL {
|
||||||
let key = SwiftFlutterPlugin.instance!.registrar!.lookupKey(forAsset: assetFilePath)
|
let key = SwiftFlutterPlugin.instance?.registrar?.lookupKey(forAsset: assetFilePath)
|
||||||
guard let assetURL = Bundle.main.url(forResource: key, withExtension: nil) else {
|
guard let assetURL = Bundle.main.url(forResource: key, withExtension: nil) else {
|
||||||
throw NSError(domain: assetFilePath + " asset file cannot be found!", code: 0)
|
throw NSError(domain: assetFilePath + " asset file cannot be found!", code: 0)
|
||||||
}
|
}
|
||||||
|
@ -20,7 +20,7 @@ public class Util {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static func getAbsPathAsset(assetFilePath: String) throws -> String {
|
public static func getAbsPathAsset(assetFilePath: String) throws -> String {
|
||||||
let key = SwiftFlutterPlugin.instance!.registrar!.lookupKey(forAsset: assetFilePath)
|
let key = SwiftFlutterPlugin.instance?.registrar?.lookupKey(forAsset: assetFilePath)
|
||||||
guard let assetAbsPath = Bundle.main.path(forResource: key, ofType: nil) else {
|
guard let assetAbsPath = Bundle.main.path(forResource: key, ofType: nil) else {
|
||||||
throw NSError(domain: assetFilePath + " asset file cannot be found!", code: 0)
|
throw NSError(domain: assetFilePath + " asset file cannot be found!", code: 0)
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,13 +14,23 @@ class ServiceWorkerController {
|
||||||
static const MethodChannel _channel = const MethodChannel(
|
static const MethodChannel _channel = const MethodChannel(
|
||||||
'com.pichillilorenzo/flutter_inappwebview_serviceworkercontroller');
|
'com.pichillilorenzo/flutter_inappwebview_serviceworkercontroller');
|
||||||
|
|
||||||
ServiceWorkerClient? serviceWorkerClient;
|
|
||||||
|
|
||||||
///Gets the [ServiceWorkerController] shared instance.
|
///Gets the [ServiceWorkerController] shared instance.
|
||||||
static ServiceWorkerController instance() {
|
static ServiceWorkerController instance() {
|
||||||
return (_instance != null) ? _instance! : _init();
|
return (_instance != null) ? _instance! : _init();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ServiceWorkerClient? _serviceWorkerClient;
|
||||||
|
|
||||||
|
ServiceWorkerClient? get serviceWorkerClient => _serviceWorkerClient;
|
||||||
|
|
||||||
|
///Sets the service worker client
|
||||||
|
setServiceWorkerClient(ServiceWorkerClient? value) async {
|
||||||
|
Map<String, dynamic> args = <String, dynamic>{};
|
||||||
|
args.putIfAbsent('isNull', () => value == null);
|
||||||
|
await _channel.invokeMethod("setServiceWorkerClient", args);
|
||||||
|
_serviceWorkerClient = value;
|
||||||
|
}
|
||||||
|
|
||||||
static ServiceWorkerController _init() {
|
static ServiceWorkerController _init() {
|
||||||
_channel.setMethodCallHandler(_handleMethod);
|
_channel.setMethodCallHandler(_handleMethod);
|
||||||
_instance = ServiceWorkerController();
|
_instance = ServiceWorkerController();
|
||||||
|
@ -180,7 +190,25 @@ class AndroidServiceWorkerController {
|
||||||
static const MethodChannel _channel = const MethodChannel(
|
static const MethodChannel _channel = const MethodChannel(
|
||||||
'com.pichillilorenzo/flutter_inappwebview_serviceworkercontroller');
|
'com.pichillilorenzo/flutter_inappwebview_serviceworkercontroller');
|
||||||
|
|
||||||
AndroidServiceWorkerClient? serviceWorkerClient;
|
AndroidServiceWorkerClient? _serviceWorkerClient;
|
||||||
|
|
||||||
|
AndroidServiceWorkerClient? get serviceWorkerClient => _serviceWorkerClient;
|
||||||
|
|
||||||
|
@Deprecated("Use setServiceWorkerClient instead")
|
||||||
|
set serviceWorkerClient(AndroidServiceWorkerClient? value) {
|
||||||
|
Map<String, dynamic> args = <String, dynamic>{};
|
||||||
|
args.putIfAbsent('isNull', () => value == null);
|
||||||
|
_channel.invokeMethod("setServiceWorkerClient", args);
|
||||||
|
_serviceWorkerClient = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
///Sets the service worker client
|
||||||
|
setServiceWorkerClient(AndroidServiceWorkerClient? value) async {
|
||||||
|
Map<String, dynamic> args = <String, dynamic>{};
|
||||||
|
args.putIfAbsent('isNull', () => value == null);
|
||||||
|
await _channel.invokeMethod("setServiceWorkerClient", args);
|
||||||
|
_serviceWorkerClient = value;
|
||||||
|
}
|
||||||
|
|
||||||
///Gets the [AndroidServiceWorkerController] shared instance.
|
///Gets the [AndroidServiceWorkerController] shared instance.
|
||||||
static AndroidServiceWorkerController instance() {
|
static AndroidServiceWorkerController instance() {
|
||||||
|
|
|
@ -241,8 +241,8 @@ class ChromeSafariBrowserSettings implements ChromeSafariBrowserOptions {
|
||||||
settings.additionalTrustedOrigins = map["additionalTrustedOrigins"];
|
settings.additionalTrustedOrigins = map["additionalTrustedOrigins"];
|
||||||
switch (map["displayMode"]["type"]) {
|
switch (map["displayMode"]["type"]) {
|
||||||
case "IMMERSIVE_MODE":
|
case "IMMERSIVE_MODE":
|
||||||
settings.displayMode =
|
settings.displayMode = TrustedWebActivityImmersiveDisplayMode.fromMap(
|
||||||
TrustedWebActivityImmersiveDisplayMode.fromMap(map["displayMode"]);
|
map["displayMode"]);
|
||||||
break;
|
break;
|
||||||
case "DEFAULT_MODE":
|
case "DEFAULT_MODE":
|
||||||
default:
|
default:
|
||||||
|
@ -255,15 +255,15 @@ class ChromeSafariBrowserSettings implements ChromeSafariBrowserOptions {
|
||||||
settings.entersReaderIfAvailable = map["entersReaderIfAvailable"];
|
settings.entersReaderIfAvailable = map["entersReaderIfAvailable"];
|
||||||
settings.barCollapsingEnabled = map["barCollapsingEnabled"];
|
settings.barCollapsingEnabled = map["barCollapsingEnabled"];
|
||||||
settings.dismissButtonStyle =
|
settings.dismissButtonStyle =
|
||||||
DismissButtonStyle.fromValue(map["dismissButtonStyle"])!;
|
DismissButtonStyle.fromValue(map["dismissButtonStyle"])!;
|
||||||
settings.preferredBarTintColor =
|
settings.preferredBarTintColor =
|
||||||
UtilColor.fromHex(map["preferredBarTintColor"]);
|
UtilColor.fromHex(map["preferredBarTintColor"]);
|
||||||
settings.preferredControlTintColor =
|
settings.preferredControlTintColor =
|
||||||
UtilColor.fromHex(map["preferredControlTintColor"]);
|
UtilColor.fromHex(map["preferredControlTintColor"]);
|
||||||
settings.presentationStyle =
|
settings.presentationStyle =
|
||||||
ModalPresentationStyle.fromValue(map["presentationStyle"])!;
|
ModalPresentationStyle.fromValue(map["presentationStyle"])!;
|
||||||
settings.transitionStyle =
|
settings.transitionStyle =
|
||||||
ModalTransitionStyle.fromValue(map["transitionStyle"])!;
|
ModalTransitionStyle.fromValue(map["transitionStyle"])!;
|
||||||
}
|
}
|
||||||
return settings;
|
return settings;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
import 'package:flutter/foundation.dart';
|
||||||
|
|
||||||
import 'in_app_webview/webview.dart';
|
import 'in_app_webview/webview.dart';
|
||||||
import 'types.dart';
|
import 'types.dart';
|
||||||
|
|
||||||
|
@ -20,9 +22,13 @@ class ContextMenu {
|
||||||
final void Function(ContextMenuItem contextMenuItemClicked)?
|
final void Function(ContextMenuItem contextMenuItemClicked)?
|
||||||
onContextMenuActionItemClicked;
|
onContextMenuActionItemClicked;
|
||||||
|
|
||||||
///Context menu options.
|
///Use [settings] instead
|
||||||
|
@Deprecated("Use settings instead")
|
||||||
final ContextMenuOptions? options;
|
final ContextMenuOptions? options;
|
||||||
|
|
||||||
|
///Context menu settings.
|
||||||
|
final ContextMenuSettings? settings;
|
||||||
|
|
||||||
///List of the custom [ContextMenuItem].
|
///List of the custom [ContextMenuItem].
|
||||||
final List<ContextMenuItem> menuItems;
|
final List<ContextMenuItem> menuItems;
|
||||||
|
|
||||||
|
@ -31,12 +37,14 @@ class ContextMenu {
|
||||||
this.onCreateContextMenu,
|
this.onCreateContextMenu,
|
||||||
this.onHideContextMenu,
|
this.onHideContextMenu,
|
||||||
this.options,
|
this.options,
|
||||||
|
this.settings,
|
||||||
this.onContextMenuActionItemClicked});
|
this.onContextMenuActionItemClicked});
|
||||||
|
|
||||||
Map<String, dynamic> toMap() {
|
Map<String, dynamic> toMap() {
|
||||||
return {
|
return {
|
||||||
"menuItems": menuItems.map((menuItem) => menuItem.toMap()).toList(),
|
"menuItems": menuItems.map((menuItem) => menuItem.toMap()).toList(),
|
||||||
"options": options?.toMap()
|
// ignore: deprecated_member_use_from_same_package
|
||||||
|
"settings": settings?.toMap() ?? options?.toMap()
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -52,12 +60,19 @@ class ContextMenu {
|
||||||
|
|
||||||
///Class that represent an item of the [ContextMenu].
|
///Class that represent an item of the [ContextMenu].
|
||||||
class ContextMenuItem {
|
class ContextMenuItem {
|
||||||
///Android menu item ID.
|
///Use [id] instead.
|
||||||
|
@Deprecated("Use id instead")
|
||||||
int? androidId;
|
int? androidId;
|
||||||
|
|
||||||
///iOS menu item ID.
|
///Use [id] instead.
|
||||||
|
@Deprecated("Use id instead")
|
||||||
String? iosId;
|
String? iosId;
|
||||||
|
|
||||||
|
///Menu item ID. It cannot be `null` and it can be a [String] or an [int].
|
||||||
|
///
|
||||||
|
///**NOTE for Android**: it must be an [int] value.
|
||||||
|
dynamic id;
|
||||||
|
|
||||||
///Menu item title.
|
///Menu item title.
|
||||||
String title;
|
String title;
|
||||||
|
|
||||||
|
@ -65,10 +80,31 @@ class ContextMenuItem {
|
||||||
Function()? action;
|
Function()? action;
|
||||||
|
|
||||||
ContextMenuItem(
|
ContextMenuItem(
|
||||||
{this.androidId, this.iosId, required this.title, this.action});
|
{this.id,
|
||||||
|
this.androidId,
|
||||||
|
this.iosId,
|
||||||
|
required this.title,
|
||||||
|
this.action}) {
|
||||||
|
if (defaultTargetPlatform == TargetPlatform.android) {
|
||||||
|
// ignore: deprecated_member_use_from_same_package
|
||||||
|
this.id = this.id ?? this.androidId;
|
||||||
|
assert(this.id is int);
|
||||||
|
} else if (defaultTargetPlatform == TargetPlatform.iOS) {
|
||||||
|
// ignore: deprecated_member_use_from_same_package
|
||||||
|
this.id = this.id ?? this.iosId;
|
||||||
|
}
|
||||||
|
assert(this.id != null && (this.id is int || this.id is String));
|
||||||
|
}
|
||||||
|
|
||||||
Map<String, dynamic> toMap() {
|
Map<String, dynamic> toMap() {
|
||||||
return {"androidId": androidId, "iosId": iosId, "title": title};
|
return {
|
||||||
|
"id": id,
|
||||||
|
// ignore: deprecated_member_use_from_same_package
|
||||||
|
"androidId": androidId,
|
||||||
|
// ignore: deprecated_member_use_from_same_package
|
||||||
|
"iosId": iosId,
|
||||||
|
"title": title
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
Map<String, dynamic> toJson() {
|
Map<String, dynamic> toJson() {
|
||||||
|
@ -81,7 +117,31 @@ class ContextMenuItem {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
///Class that represents available options used by [ContextMenu].
|
///Class that represents available settings used by [ContextMenu].
|
||||||
|
class ContextMenuSettings {
|
||||||
|
///Whether all the default system context menu items should be hidden or not. The default value is `false`.
|
||||||
|
bool hideDefaultSystemContextMenuItems;
|
||||||
|
|
||||||
|
ContextMenuSettings({this.hideDefaultSystemContextMenuItems = false});
|
||||||
|
|
||||||
|
Map<String, dynamic> toMap() {
|
||||||
|
return {
|
||||||
|
"hideDefaultSystemContextMenuItems": hideDefaultSystemContextMenuItems
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, dynamic> toJson() {
|
||||||
|
return this.toMap();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() {
|
||||||
|
return toMap().toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
///Use [ContextMenuSettings] instead.
|
||||||
|
@Deprecated("Use ContextMenuSettings instead")
|
||||||
class ContextMenuOptions {
|
class ContextMenuOptions {
|
||||||
///Whether all the default system context menu items should be hidden or not. The default value is `false`.
|
///Whether all the default system context menu items should be hidden or not. The default value is `false`.
|
||||||
bool hideDefaultSystemContextMenuItems;
|
bool hideDefaultSystemContextMenuItems;
|
||||||
|
|
|
@ -286,7 +286,7 @@ class InAppBrowserSettings
|
||||||
settings.closeOnCannotGoBack = map["closeOnCannotGoBack"];
|
settings.closeOnCannotGoBack = map["closeOnCannotGoBack"];
|
||||||
settings.allowGoBackWithBackButton = map["allowGoBackWithBackButton"];
|
settings.allowGoBackWithBackButton = map["allowGoBackWithBackButton"];
|
||||||
settings.shouldCloseOnBackButtonPressed =
|
settings.shouldCloseOnBackButtonPressed =
|
||||||
map["shouldCloseOnBackButtonPressed"];
|
map["shouldCloseOnBackButtonPressed"];
|
||||||
}
|
}
|
||||||
if (defaultTargetPlatform == TargetPlatform.iOS || defaultTargetPlatform == TargetPlatform.macOS) {
|
if (defaultTargetPlatform == TargetPlatform.iOS || defaultTargetPlatform == TargetPlatform.macOS) {
|
||||||
settings.toolbarTopTranslucent = map["toolbarTopTranslucent"];
|
settings.toolbarTopTranslucent = map["toolbarTopTranslucent"];
|
||||||
|
@ -301,9 +301,9 @@ class InAppBrowserSettings
|
||||||
settings.closeButtonCaption = map["closeButtonCaption"];
|
settings.closeButtonCaption = map["closeButtonCaption"];
|
||||||
settings.closeButtonColor = UtilColor.fromHex(map["closeButtonColor"]);
|
settings.closeButtonColor = UtilColor.fromHex(map["closeButtonColor"]);
|
||||||
settings.presentationStyle =
|
settings.presentationStyle =
|
||||||
ModalPresentationStyle.fromValue(map["presentationStyle"])!;
|
ModalPresentationStyle.fromValue(map["presentationStyle"])!;
|
||||||
settings.transitionStyle =
|
settings.transitionStyle =
|
||||||
ModalTransitionStyle.fromValue(map["transitionStyle"])!;
|
ModalTransitionStyle.fromValue(map["transitionStyle"])!;
|
||||||
}
|
}
|
||||||
return settings;
|
return settings;
|
||||||
}
|
}
|
||||||
|
|
|
@ -107,7 +107,8 @@ abstract class AppleInAppWebViewControllerMixin {
|
||||||
///- iOS ([Official API - WKWebView.requestMediaPlaybackState](https://developer.apple.com/documentation/webkit/wkwebview/3752241-requestmediaplaybackstate)).
|
///- iOS ([Official API - WKWebView.requestMediaPlaybackState](https://developer.apple.com/documentation/webkit/wkwebview/3752241-requestmediaplaybackstate)).
|
||||||
Future<MediaPlaybackState?> requestMediaPlaybackState() async {
|
Future<MediaPlaybackState?> requestMediaPlaybackState() async {
|
||||||
Map<String, dynamic> args = <String, dynamic>{};
|
Map<String, dynamic> args = <String, dynamic>{};
|
||||||
return MediaPlaybackState.fromValue(await _channel.invokeMethod('requestMediaPlaybackState', args));
|
return MediaPlaybackState.fromValue(
|
||||||
|
await _channel.invokeMethod('requestMediaPlaybackState', args));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -472,8 +472,7 @@ class InAppWebView extends StatefulWidget implements WebView {
|
||||||
NavigationResponse navigationResponse)? onNavigationResponse;
|
NavigationResponse navigationResponse)? onNavigationResponse;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
final Future<PermissionResponse?> Function(
|
final Future<PermissionResponse?> Function(InAppWebViewController controller,
|
||||||
InAppWebViewController controller,
|
|
||||||
PermissionRequest permissionRequest)? onPermissionRequest;
|
PermissionRequest permissionRequest)? onPermissionRequest;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
|
|
@ -731,9 +731,9 @@ class InAppWebViewController
|
||||||
List<String> resources = call.arguments["resources"].cast<String>();
|
List<String> resources = call.arguments["resources"].cast<String>();
|
||||||
|
|
||||||
Map<String, dynamic> arguments =
|
Map<String, dynamic> arguments =
|
||||||
call.arguments.cast<String, dynamic>();
|
call.arguments.cast<String, dynamic>();
|
||||||
PermissionRequest permissionRequest =
|
PermissionRequest permissionRequest =
|
||||||
PermissionRequest.fromMap(arguments)!;
|
PermissionRequest.fromMap(arguments)!;
|
||||||
|
|
||||||
if (_webview != null) {
|
if (_webview != null) {
|
||||||
if (_webview!.onPermissionRequest != null)
|
if (_webview!.onPermissionRequest != null)
|
||||||
|
@ -747,11 +747,12 @@ class InAppWebViewController
|
||||||
?.toMap();
|
?.toMap();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return (await _inAppBrowser!
|
return (await _inAppBrowser!.onPermissionRequest(permissionRequest))
|
||||||
.onPermissionRequest(permissionRequest))?.toMap() ??
|
?.toMap() ??
|
||||||
(await _inAppBrowser!
|
(await _inAppBrowser!
|
||||||
// ignore: deprecated_member_use_from_same_package
|
// ignore: deprecated_member_use_from_same_package
|
||||||
.androidOnPermissionRequest(origin, resources))?.toMap();
|
.androidOnPermissionRequest(origin, resources))
|
||||||
|
?.toMap();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
@ -944,16 +945,18 @@ class InAppWebViewController
|
||||||
if (contextMenu != null) {
|
if (contextMenu != null) {
|
||||||
int? androidId = call.arguments["androidId"];
|
int? androidId = call.arguments["androidId"];
|
||||||
String? iosId = call.arguments["iosId"];
|
String? iosId = call.arguments["iosId"];
|
||||||
|
dynamic id = call.arguments["id"];
|
||||||
String title = call.arguments["title"];
|
String title = call.arguments["title"];
|
||||||
|
|
||||||
ContextMenuItem menuItemClicked = ContextMenuItem(
|
ContextMenuItem menuItemClicked = ContextMenuItem(
|
||||||
androidId: androidId, iosId: iosId, title: title, action: null);
|
id: id,
|
||||||
|
androidId: androidId,
|
||||||
|
iosId: iosId,
|
||||||
|
title: title,
|
||||||
|
action: null);
|
||||||
|
|
||||||
for (var menuItem in contextMenu.menuItems) {
|
for (var menuItem in contextMenu.menuItems) {
|
||||||
if ((defaultTargetPlatform == TargetPlatform.android &&
|
if (menuItem.id == id) {
|
||||||
menuItem.androidId == androidId) ||
|
|
||||||
(defaultTargetPlatform == TargetPlatform.iOS &&
|
|
||||||
menuItem.iosId == iosId)) {
|
|
||||||
menuItemClicked = menuItem;
|
menuItemClicked = menuItem;
|
||||||
if (menuItem.action != null) {
|
if (menuItem.action != null) {
|
||||||
menuItem.action!();
|
menuItem.action!();
|
||||||
|
@ -1864,7 +1867,8 @@ class InAppWebViewController
|
||||||
///Use [setSettings] instead.
|
///Use [setSettings] instead.
|
||||||
@Deprecated('Use setSettings instead')
|
@Deprecated('Use setSettings instead')
|
||||||
Future<void> setOptions({required InAppWebViewGroupOptions options}) async {
|
Future<void> setOptions({required InAppWebViewGroupOptions options}) async {
|
||||||
InAppWebViewSettings settings = InAppWebViewSettings.fromMap(options.toMap());
|
InAppWebViewSettings settings =
|
||||||
|
InAppWebViewSettings.fromMap(options.toMap());
|
||||||
await setSettings(settings: settings);
|
await setSettings(settings: settings);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2325,7 +2329,8 @@ class InAppWebViewController
|
||||||
|
|
||||||
try {
|
try {
|
||||||
Map<String, dynamic> args = <String, dynamic>{};
|
Map<String, dynamic> args = <String, dynamic>{};
|
||||||
themeColor = UtilColor.fromStringRepresentation(await _channel.invokeMethod('getMetaThemeColor', args));
|
themeColor = UtilColor.fromStringRepresentation(
|
||||||
|
await _channel.invokeMethod('getMetaThemeColor', args));
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
// not implemented
|
// not implemented
|
||||||
}
|
}
|
||||||
|
|
|
@ -1414,7 +1414,7 @@ class InAppWebViewSettings
|
||||||
settings.hardwareAcceleration = map["hardwareAcceleration"];
|
settings.hardwareAcceleration = map["hardwareAcceleration"];
|
||||||
settings.supportMultipleWindows = map["supportMultipleWindows"];
|
settings.supportMultipleWindows = map["supportMultipleWindows"];
|
||||||
settings.regexToCancelSubFramesLoading =
|
settings.regexToCancelSubFramesLoading =
|
||||||
map["regexToCancelSubFramesLoading"];
|
map["regexToCancelSubFramesLoading"];
|
||||||
settings.useHybridComposition = map["useHybridComposition"];
|
settings.useHybridComposition = map["useHybridComposition"];
|
||||||
settings.useShouldInterceptRequest = map["useShouldInterceptRequest"];
|
settings.useShouldInterceptRequest = map["useShouldInterceptRequest"];
|
||||||
settings.useOnRenderProcessGone = map["useOnRenderProcessGone"];
|
settings.useOnRenderProcessGone = map["useOnRenderProcessGone"];
|
||||||
|
@ -1424,7 +1424,7 @@ class InAppWebViewSettings
|
||||||
settings.verticalScrollbarPosition =
|
settings.verticalScrollbarPosition =
|
||||||
VerticalScrollbarPosition.fromValue(map["verticalScrollbarPosition"]);
|
VerticalScrollbarPosition.fromValue(map["verticalScrollbarPosition"]);
|
||||||
settings.scrollBarDefaultDelayBeforeFade =
|
settings.scrollBarDefaultDelayBeforeFade =
|
||||||
map["scrollBarDefaultDelayBeforeFade"];
|
map["scrollBarDefaultDelayBeforeFade"];
|
||||||
settings.scrollbarFadingEnabled = map["scrollbarFadingEnabled"];
|
settings.scrollbarFadingEnabled = map["scrollbarFadingEnabled"];
|
||||||
settings.scrollBarFadeDuration = map["scrollBarFadeDuration"];
|
settings.scrollBarFadeDuration = map["scrollBarFadeDuration"];
|
||||||
settings.rendererPriorityPolicy = RendererPriorityPolicy.fromMap(
|
settings.rendererPriorityPolicy = RendererPriorityPolicy.fromMap(
|
||||||
|
@ -1443,28 +1443,28 @@ class InAppWebViewSettings
|
||||||
settings.disallowOverScroll = map["disallowOverScroll"];
|
settings.disallowOverScroll = map["disallowOverScroll"];
|
||||||
settings.enableViewportScale = map["enableViewportScale"];
|
settings.enableViewportScale = map["enableViewportScale"];
|
||||||
settings.suppressesIncrementalRendering =
|
settings.suppressesIncrementalRendering =
|
||||||
map["suppressesIncrementalRendering"];
|
map["suppressesIncrementalRendering"];
|
||||||
settings.allowsAirPlayForMediaPlayback =
|
settings.allowsAirPlayForMediaPlayback =
|
||||||
map["allowsAirPlayForMediaPlayback"];
|
map["allowsAirPlayForMediaPlayback"];
|
||||||
settings.allowsBackForwardNavigationGestures =
|
settings.allowsBackForwardNavigationGestures =
|
||||||
map["allowsBackForwardNavigationGestures"];
|
map["allowsBackForwardNavigationGestures"];
|
||||||
settings.allowsLinkPreview = map["allowsLinkPreview"];
|
settings.allowsLinkPreview = map["allowsLinkPreview"];
|
||||||
settings.ignoresViewportScaleLimits = map["ignoresViewportScaleLimits"];
|
settings.ignoresViewportScaleLimits = map["ignoresViewportScaleLimits"];
|
||||||
settings.allowsInlineMediaPlayback = map["allowsInlineMediaPlayback"];
|
settings.allowsInlineMediaPlayback = map["allowsInlineMediaPlayback"];
|
||||||
settings.allowsPictureInPictureMediaPlayback =
|
settings.allowsPictureInPictureMediaPlayback =
|
||||||
map["allowsPictureInPictureMediaPlayback"];
|
map["allowsPictureInPictureMediaPlayback"];
|
||||||
settings.isFraudulentWebsiteWarningEnabled =
|
settings.isFraudulentWebsiteWarningEnabled =
|
||||||
map["isFraudulentWebsiteWarningEnabled"];
|
map["isFraudulentWebsiteWarningEnabled"];
|
||||||
settings.selectionGranularity =
|
settings.selectionGranularity =
|
||||||
SelectionGranularity.fromValue(map["selectionGranularity"])!;
|
SelectionGranularity.fromValue(map["selectionGranularity"])!;
|
||||||
settings.dataDetectorTypes = dataDetectorTypes;
|
settings.dataDetectorTypes = dataDetectorTypes;
|
||||||
settings.sharedCookiesEnabled = map["sharedCookiesEnabled"];
|
settings.sharedCookiesEnabled = map["sharedCookiesEnabled"];
|
||||||
settings.automaticallyAdjustsScrollIndicatorInsets =
|
settings.automaticallyAdjustsScrollIndicatorInsets =
|
||||||
map["automaticallyAdjustsScrollIndicatorInsets"];
|
map["automaticallyAdjustsScrollIndicatorInsets"];
|
||||||
settings.accessibilityIgnoresInvertColors =
|
settings.accessibilityIgnoresInvertColors =
|
||||||
map["accessibilityIgnoresInvertColors"];
|
map["accessibilityIgnoresInvertColors"];
|
||||||
settings.decelerationRate =
|
settings.decelerationRate =
|
||||||
ScrollViewDecelerationRate.fromValue(map["decelerationRate"])!;
|
ScrollViewDecelerationRate.fromValue(map["decelerationRate"])!;
|
||||||
settings.alwaysBounceVertical = map["alwaysBounceVertical"];
|
settings.alwaysBounceVertical = map["alwaysBounceVertical"];
|
||||||
settings.alwaysBounceHorizontal = map["alwaysBounceHorizontal"];
|
settings.alwaysBounceHorizontal = map["alwaysBounceHorizontal"];
|
||||||
settings.scrollsToTop = map["scrollsToTop"];
|
settings.scrollsToTop = map["scrollsToTop"];
|
||||||
|
@ -1472,24 +1472,26 @@ class InAppWebViewSettings
|
||||||
settings.maximumZoomScale = map["maximumZoomScale"];
|
settings.maximumZoomScale = map["maximumZoomScale"];
|
||||||
settings.minimumZoomScale = map["minimumZoomScale"];
|
settings.minimumZoomScale = map["minimumZoomScale"];
|
||||||
settings.contentInsetAdjustmentBehavior =
|
settings.contentInsetAdjustmentBehavior =
|
||||||
ScrollViewContentInsetAdjustmentBehavior.fromValue(
|
ScrollViewContentInsetAdjustmentBehavior.fromValue(
|
||||||
map["contentInsetAdjustmentBehavior"])!;
|
map["contentInsetAdjustmentBehavior"])!;
|
||||||
settings.isDirectionalLockEnabled = map["isDirectionalLockEnabled"];
|
settings.isDirectionalLockEnabled = map["isDirectionalLockEnabled"];
|
||||||
settings.mediaType = map["mediaType"];
|
settings.mediaType = map["mediaType"];
|
||||||
settings.pageZoom = map["pageZoom"];
|
settings.pageZoom = map["pageZoom"];
|
||||||
settings.limitsNavigationsToAppBoundDomains =
|
settings.limitsNavigationsToAppBoundDomains =
|
||||||
map["limitsNavigationsToAppBoundDomains"];
|
map["limitsNavigationsToAppBoundDomains"];
|
||||||
settings.useOnNavigationResponse = map["useOnNavigationResponse"];
|
settings.useOnNavigationResponse = map["useOnNavigationResponse"];
|
||||||
settings.applePayAPIEnabled = map["applePayAPIEnabled"];
|
settings.applePayAPIEnabled = map["applePayAPIEnabled"];
|
||||||
settings.allowingReadAccessTo = map["allowingReadAccessTo"] != null
|
settings.allowingReadAccessTo = map["allowingReadAccessTo"] != null
|
||||||
? Uri.parse(map["allowingReadAccessTo"])
|
? Uri.parse(map["allowingReadAccessTo"])
|
||||||
: null;
|
: null;
|
||||||
settings.disableLongPressContextMenuOnLinks =
|
settings.disableLongPressContextMenuOnLinks =
|
||||||
map["disableLongPressContextMenuOnLinks"];
|
map["disableLongPressContextMenuOnLinks"];
|
||||||
settings.disableInputAccessoryView = map["disableInputAccessoryView"];
|
settings.disableInputAccessoryView = map["disableInputAccessoryView"];
|
||||||
settings.underPageBackgroundColor = UtilColor.fromHex(map["underPageBackgroundColor"]);
|
settings.underPageBackgroundColor =
|
||||||
|
UtilColor.fromHex(map["underPageBackgroundColor"]);
|
||||||
settings.isTextInteractionEnabled = map["isTextInteractionEnabled"];
|
settings.isTextInteractionEnabled = map["isTextInteractionEnabled"];
|
||||||
settings.isSiteSpecificQuirksModeEnabled = map["isSiteSpecificQuirksModeEnabled"];
|
settings.isSiteSpecificQuirksModeEnabled =
|
||||||
|
map["isSiteSpecificQuirksModeEnabled"];
|
||||||
settings.upgradeKnownHostsToHTTPS = map["upgradeKnownHostsToHTTPS"];
|
settings.upgradeKnownHostsToHTTPS = map["upgradeKnownHostsToHTTPS"];
|
||||||
}
|
}
|
||||||
return settings;
|
return settings;
|
||||||
|
|
|
@ -525,8 +525,7 @@ abstract class WebView {
|
||||||
///
|
///
|
||||||
///**Supported Platforms/Implementations**:
|
///**Supported Platforms/Implementations**:
|
||||||
///- Android native WebView ([Official API - WebChromeClient.onPermissionRequest](https://developer.android.com/reference/android/webkit/WebChromeClient#onPermissionRequest(android.webkit.PermissionRequest)))
|
///- Android native WebView ([Official API - WebChromeClient.onPermissionRequest](https://developer.android.com/reference/android/webkit/WebChromeClient#onPermissionRequest(android.webkit.PermissionRequest)))
|
||||||
final Future<PermissionResponse?> Function(
|
final Future<PermissionResponse?> Function(InAppWebViewController controller,
|
||||||
InAppWebViewController controller,
|
|
||||||
PermissionRequest permissionRequest)? onPermissionRequest;
|
PermissionRequest permissionRequest)? onPermissionRequest;
|
||||||
|
|
||||||
///Use [onGeolocationPermissionsShowPrompt] instead.
|
///Use [onGeolocationPermissionsShowPrompt] instead.
|
||||||
|
|
|
@ -4716,6 +4716,11 @@ class PermissionResponseAction {
|
||||||
///Grants origin the permission to access the given resources.
|
///Grants origin the permission to access the given resources.
|
||||||
static const GRANT = const PermissionResponseAction._internal(1);
|
static const GRANT = const PermissionResponseAction._internal(1);
|
||||||
|
|
||||||
|
///Prompt the user for permission for the requested resource.
|
||||||
|
///
|
||||||
|
///**NOTE**: available only on iOS 15.0+. It will fallback to [DENY].
|
||||||
|
static const PROMPT = const PermissionResponseAction._internal(2);
|
||||||
|
|
||||||
bool operator ==(value) => value == _value;
|
bool operator ==(value) => value == _value;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -4768,7 +4773,8 @@ class PermissionResourceType {
|
||||||
PermissionResourceType.RESOURCE_VIDEO_CAPTURE,
|
PermissionResourceType.RESOURCE_VIDEO_CAPTURE,
|
||||||
].toSet();
|
].toSet();
|
||||||
|
|
||||||
static final Set<PermissionResourceType> _appleValues = <PermissionResourceType>[
|
static final Set<PermissionResourceType> _appleValues =
|
||||||
|
<PermissionResourceType>[
|
||||||
PermissionResourceType.CAMERA,
|
PermissionResourceType.CAMERA,
|
||||||
PermissionResourceType.MICROPHONE,
|
PermissionResourceType.MICROPHONE,
|
||||||
PermissionResourceType.CAMERA_AND_MICROPHONE,
|
PermissionResourceType.CAMERA_AND_MICROPHONE,
|
||||||
|
@ -4814,52 +4820,51 @@ class PermissionResourceType {
|
||||||
///Resource belongs to audio capture device, like microphone.
|
///Resource belongs to audio capture device, like microphone.
|
||||||
///
|
///
|
||||||
///**NOTE**: available only on Android.
|
///**NOTE**: available only on Android.
|
||||||
static const RESOURCE_AUDIO_CAPTURE = const PermissionResourceType._internal('android.webkit.resource.AUDIO_CAPTURE');
|
static const RESOURCE_AUDIO_CAPTURE = const PermissionResourceType._internal(
|
||||||
|
'android.webkit.resource.AUDIO_CAPTURE');
|
||||||
|
|
||||||
///Resource will allow sysex messages to be sent to or received from MIDI devices.
|
///Resource will allow sysex messages to be sent to or received from MIDI devices.
|
||||||
///These messages are privileged operations, e.g. modifying sound libraries and sampling data, or even updating the MIDI device's firmware.
|
///These messages are privileged operations, e.g. modifying sound libraries and sampling data, or even updating the MIDI device's firmware.
|
||||||
///Permission may be requested for this resource in API levels 21 and above, if the Android device has been updated to WebView 45 or above.
|
///Permission may be requested for this resource in API levels 21 and above, if the Android device has been updated to WebView 45 or above.
|
||||||
///
|
///
|
||||||
///**NOTE**: available only on Android.
|
///**NOTE**: available only on Android.
|
||||||
static const RESOURCE_MIDI_SYSEX =
|
static const RESOURCE_MIDI_SYSEX = const PermissionResourceType._internal(
|
||||||
const PermissionResourceType._internal('android.webkit.resource.MIDI_SYSEX');
|
'android.webkit.resource.MIDI_SYSEX');
|
||||||
|
|
||||||
///Resource belongs to protected media identifier. After the user grants this resource, the origin can use EME APIs to generate the license requests.
|
///Resource belongs to protected media identifier. After the user grants this resource, the origin can use EME APIs to generate the license requests.
|
||||||
///
|
///
|
||||||
///**NOTE**: available only on Android.
|
///**NOTE**: available only on Android.
|
||||||
static const RESOURCE_PROTECTED_MEDIA_ID =
|
static const RESOURCE_PROTECTED_MEDIA_ID =
|
||||||
const PermissionResourceType._internal('android.webkit.resource.PROTECTED_MEDIA_ID');
|
const PermissionResourceType._internal(
|
||||||
|
'android.webkit.resource.PROTECTED_MEDIA_ID');
|
||||||
|
|
||||||
///Resource belongs to video capture device, like camera.
|
///Resource belongs to video capture device, like camera.
|
||||||
///
|
///
|
||||||
///**NOTE**: available only on Android.
|
///**NOTE**: available only on Android.
|
||||||
static const RESOURCE_VIDEO_CAPTURE =
|
static const RESOURCE_VIDEO_CAPTURE = const PermissionResourceType._internal(
|
||||||
const PermissionResourceType._internal('android.webkit.resource.VIDEO_CAPTURE');
|
'android.webkit.resource.VIDEO_CAPTURE');
|
||||||
|
|
||||||
///A media device that can capture video.
|
///A media device that can capture video.
|
||||||
///
|
///
|
||||||
///**NOTE**: available only on iOS.
|
///**NOTE**: available only on iOS.
|
||||||
static const CAMERA =
|
static const CAMERA = const PermissionResourceType._internal(0);
|
||||||
const PermissionResourceType._internal(0);
|
|
||||||
|
|
||||||
///A media device that can capture audio.
|
///A media device that can capture audio.
|
||||||
///
|
///
|
||||||
///**NOTE**: available only on iOS.
|
///**NOTE**: available only on iOS.
|
||||||
static const MICROPHONE =
|
static const MICROPHONE = const PermissionResourceType._internal(1);
|
||||||
const PermissionResourceType._internal(1);
|
|
||||||
|
|
||||||
///A media device or devices that can capture audio and video.
|
///A media device or devices that can capture audio and video.
|
||||||
///
|
///
|
||||||
///**NOTE**: available only on iOS.
|
///**NOTE**: available only on iOS.
|
||||||
static const CAMERA_AND_MICROPHONE =
|
static const CAMERA_AND_MICROPHONE =
|
||||||
const PermissionResourceType._internal(2);
|
const PermissionResourceType._internal(2);
|
||||||
|
|
||||||
///Resource belongs to the device’s orientation and motion.
|
///Resource belongs to the device’s orientation and motion.
|
||||||
///
|
///
|
||||||
///**NOTE**: available only on iOS.
|
///**NOTE**: available only on iOS.
|
||||||
static const DEVICE_ORIENTATION_AND_MOTION =
|
static const DEVICE_ORIENTATION_AND_MOTION =
|
||||||
const PermissionResourceType._internal('deviceOrientationAndMotion');
|
const PermissionResourceType._internal('deviceOrientationAndMotion');
|
||||||
|
|
||||||
bool operator ==(value) => value == _value;
|
bool operator ==(value) => value == _value;
|
||||||
|
|
||||||
|
@ -4873,15 +4878,16 @@ class PermissionRequest {
|
||||||
Uri origin;
|
Uri origin;
|
||||||
|
|
||||||
///List of resources the web content wants to access.
|
///List of resources the web content wants to access.
|
||||||
|
///
|
||||||
|
///**NOTE for iOS**: this list will have only 1 element and will be used by the [PermissionResponse.action]
|
||||||
|
///as the resource to consider when applying the corresponding action.
|
||||||
List<PermissionResourceType> resources;
|
List<PermissionResourceType> resources;
|
||||||
|
|
||||||
///The frame that initiates the request in the web view.
|
///The frame that initiates the request in the web view.
|
||||||
FrameInfo? frame;
|
FrameInfo? frame;
|
||||||
|
|
||||||
PermissionRequest(
|
PermissionRequest(
|
||||||
{required this.origin,
|
{required this.origin, this.resources = const [], this.frame});
|
||||||
this.resources = const [],
|
|
||||||
this.frame});
|
|
||||||
|
|
||||||
static PermissionRequest? fromMap(Map<String, dynamic>? map) {
|
static PermissionRequest? fromMap(Map<String, dynamic>? map) {
|
||||||
if (map == null) {
|
if (map == null) {
|
||||||
|
@ -4890,8 +4896,7 @@ class PermissionRequest {
|
||||||
|
|
||||||
List<PermissionResourceType> resources = [];
|
List<PermissionResourceType> resources = [];
|
||||||
if (map["resources"] != null) {
|
if (map["resources"] != null) {
|
||||||
(map["resources"].cast<dynamic>() as List<dynamic>)
|
(map["resources"].cast<dynamic>() as List<dynamic>).forEach((element) {
|
||||||
.forEach((element) {
|
|
||||||
var resource = PermissionResourceType.fromValue(element);
|
var resource = PermissionResourceType.fromValue(element);
|
||||||
if (resource != null) {
|
if (resource != null) {
|
||||||
resources.add(resource);
|
resources.add(resource);
|
||||||
|
@ -4926,14 +4931,15 @@ class PermissionRequest {
|
||||||
///Class that represents the response used by the [WebView.onPermissionRequest] event.
|
///Class that represents the response used by the [WebView.onPermissionRequest] event.
|
||||||
class PermissionResponse {
|
class PermissionResponse {
|
||||||
///Resources granted to be accessed by origin.
|
///Resources granted to be accessed by origin.
|
||||||
|
///
|
||||||
|
///**NOTE for iOS**: not used. The [action] taken is based on the [PermissionRequest.resources].
|
||||||
List<PermissionResourceType> resources;
|
List<PermissionResourceType> resources;
|
||||||
|
|
||||||
///Indicate the [PermissionResponseAction] to take in response of a permission request.
|
///Indicate the [PermissionResponseAction] to take in response of a permission request.
|
||||||
PermissionResponseAction? action;
|
PermissionResponseAction? action;
|
||||||
|
|
||||||
PermissionResponse(
|
PermissionResponse(
|
||||||
{this.resources = const [],
|
{this.resources = const [], this.action = PermissionResponseAction.DENY});
|
||||||
this.action = PermissionResponseAction.DENY});
|
|
||||||
|
|
||||||
Map<String, dynamic> toMap() {
|
Map<String, dynamic> toMap() {
|
||||||
return {
|
return {
|
||||||
|
@ -4995,6 +5001,11 @@ class NavigationActionPolicy {
|
||||||
///Allow the navigation to continue.
|
///Allow the navigation to continue.
|
||||||
static const ALLOW = const NavigationActionPolicy._internal(1);
|
static const ALLOW = const NavigationActionPolicy._internal(1);
|
||||||
|
|
||||||
|
///Turn the navigation into a download.
|
||||||
|
///
|
||||||
|
///**NOTE**: available only on iOS 14.5+. It will fallback to [CANCEL].
|
||||||
|
static const DOWNLOAD = const NavigationActionPolicy._internal(2);
|
||||||
|
|
||||||
bool operator ==(value) => value == _value;
|
bool operator ==(value) => value == _value;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -5763,6 +5774,11 @@ class NavigationAction {
|
||||||
///**NOTE**: available only on iOS.
|
///**NOTE**: available only on iOS.
|
||||||
FrameInfo? targetFrame;
|
FrameInfo? targetFrame;
|
||||||
|
|
||||||
|
///A value indicating whether the web content used a download attribute to indicate that this should be downloaded.
|
||||||
|
///
|
||||||
|
///**NOTE**: available only on iOS.
|
||||||
|
bool? shouldPerformDownload;
|
||||||
|
|
||||||
NavigationAction(
|
NavigationAction(
|
||||||
{required this.request,
|
{required this.request,
|
||||||
required this.isForMainFrame,
|
required this.isForMainFrame,
|
||||||
|
@ -5775,7 +5791,8 @@ class NavigationAction {
|
||||||
@Deprecated("Use sourceFrame instead") this.iosSourceFrame,
|
@Deprecated("Use sourceFrame instead") this.iosSourceFrame,
|
||||||
this.sourceFrame,
|
this.sourceFrame,
|
||||||
@Deprecated("Use targetFrame instead") this.iosTargetFrame,
|
@Deprecated("Use targetFrame instead") this.iosTargetFrame,
|
||||||
this.targetFrame}) {
|
this.targetFrame,
|
||||||
|
this.shouldPerformDownload}) {
|
||||||
// ignore: deprecated_member_use_from_same_package
|
// ignore: deprecated_member_use_from_same_package
|
||||||
this.hasGesture = this.hasGesture ?? this.androidHasGesture;
|
this.hasGesture = this.hasGesture ?? this.androidHasGesture;
|
||||||
// ignore: deprecated_member_use_from_same_package
|
// ignore: deprecated_member_use_from_same_package
|
||||||
|
@ -5820,7 +5837,8 @@ class NavigationAction {
|
||||||
// ignore: deprecated_member_use_from_same_package
|
// ignore: deprecated_member_use_from_same_package
|
||||||
IOSWKFrameInfo.fromMap(map["targetFrame"]?.cast<String, dynamic>()),
|
IOSWKFrameInfo.fromMap(map["targetFrame"]?.cast<String, dynamic>()),
|
||||||
targetFrame:
|
targetFrame:
|
||||||
FrameInfo.fromMap(map["targetFrame"]?.cast<String, dynamic>()));
|
FrameInfo.fromMap(map["targetFrame"]?.cast<String, dynamic>()),
|
||||||
|
shouldPerformDownload: map["shouldPerformDownload"]);
|
||||||
}
|
}
|
||||||
|
|
||||||
Map<String, dynamic> toMap() {
|
Map<String, dynamic> toMap() {
|
||||||
|
@ -5849,6 +5867,7 @@ class NavigationAction {
|
||||||
"iosTargetFrame": targetFrame?.toMap() ?? iosTargetFrame?.toMap(),
|
"iosTargetFrame": targetFrame?.toMap() ?? iosTargetFrame?.toMap(),
|
||||||
// ignore: deprecated_member_use_from_same_package
|
// ignore: deprecated_member_use_from_same_package
|
||||||
"targetFrame": targetFrame?.toMap() ?? iosTargetFrame?.toMap(),
|
"targetFrame": targetFrame?.toMap() ?? iosTargetFrame?.toMap(),
|
||||||
|
"shouldPerformDownload": shouldPerformDownload
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -8955,6 +8974,11 @@ class NavigationResponseAction {
|
||||||
///Allow the navigation to continue.
|
///Allow the navigation to continue.
|
||||||
static const ALLOW = const NavigationResponseAction._internal(1);
|
static const ALLOW = const NavigationResponseAction._internal(1);
|
||||||
|
|
||||||
|
///Turn the navigation into a download.
|
||||||
|
///
|
||||||
|
///**NOTE**: available only on iOS 14.5+. It will fallback to [CANCEL].
|
||||||
|
static const DOWNLOAD = const NavigationResponseAction._internal(2);
|
||||||
|
|
||||||
bool operator ==(value) => value == _value;
|
bool operator ==(value) => value == _value;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -10687,16 +10711,13 @@ class MediaPlaybackState {
|
||||||
static const NONE = const MediaPlaybackState._internal(0);
|
static const NONE = const MediaPlaybackState._internal(0);
|
||||||
|
|
||||||
///The media is playing.
|
///The media is playing.
|
||||||
static const PLAYING =
|
static const PLAYING = const MediaPlaybackState._internal(1);
|
||||||
const MediaPlaybackState._internal(1);
|
|
||||||
|
|
||||||
///The media playback is paused.
|
///The media playback is paused.
|
||||||
static const PAUSED =
|
static const PAUSED = const MediaPlaybackState._internal(2);
|
||||||
const MediaPlaybackState._internal(2);
|
|
||||||
|
|
||||||
///The media is not playing, and cannot be resumed until the user revokes the suspension.
|
///The media is not playing, and cannot be resumed until the user revokes the suspension.
|
||||||
static const SUSPENDED =
|
static const SUSPENDED = const MediaPlaybackState._internal(3);
|
||||||
const MediaPlaybackState._internal(3);
|
|
||||||
|
|
||||||
bool operator ==(value) => value == _value;
|
bool operator ==(value) => value == _value;
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue