updated context menu item and options to settings
This commit is contained in:
parent
68305a365b
commit
b881909957
|
@ -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);
|
||||||
}
|
}
|
|
@ -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";
|
||||||
|
|
||||||
|
|
|
@ -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();
|
|
@ -55,6 +55,7 @@ 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.DownloadStartRequest;
|
||||||
|
import com.pichillilorenzo.flutter_inappwebview.types.HitTestResult;
|
||||||
import com.pichillilorenzo.flutter_inappwebview.types.InAppWebViewInterface;
|
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;
|
||||||
|
@ -1367,17 +1368,17 @@ 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> contextMenuOptionsMap = (Map<String, Object>) contextMenu.get("settings");
|
||||||
if (contextMenuOptionsMap != null) {
|
if (contextMenuOptionsMap != null) {
|
||||||
contextMenuOptions.parse(contextMenuOptionsMap);
|
contextMenuSettings.parse(contextMenuOptionsMap);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
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 (int i = 0; i < actionMenu.size(); i++) {
|
||||||
final MenuItem menuItem = actionMenu.getItem(i);
|
final MenuItem menuItem = actionMenu.getItem(i);
|
||||||
final int itemId = menuItem.getItemId();
|
final int itemId = menuItem.getItemId();
|
||||||
|
@ -1393,6 +1394,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 +1408,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 +1419,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);
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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")),
|
||||||
));
|
));
|
||||||
|
|
|
@ -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,11 +1,7 @@
|
||||||
import 'dart:collection';
|
import 'dart:collection';
|
||||||
// import 'dart:convert';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'dart:io';
|
|
||||||
// import 'dart:typed_data';
|
|
||||||
|
|
||||||
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';
|
||||||
|
@ -17,7 +13,6 @@ class InAppWebViewExampleScreen extends StatefulWidget {
|
||||||
}
|
}
|
||||||
|
|
||||||
class _InAppWebViewExampleScreenState extends State<InAppWebViewExampleScreen> {
|
class _InAppWebViewExampleScreenState extends State<InAppWebViewExampleScreen> {
|
||||||
|
|
||||||
final GlobalKey webViewKey = GlobalKey();
|
final GlobalKey webViewKey = GlobalKey();
|
||||||
|
|
||||||
InAppWebViewController? webViewController;
|
InAppWebViewController? webViewController;
|
||||||
|
@ -25,8 +20,7 @@ class _InAppWebViewExampleScreenState extends State<InAppWebViewExampleScreen> {
|
||||||
useShouldOverrideUrlLoading: true,
|
useShouldOverrideUrlLoading: true,
|
||||||
mediaPlaybackRequiresUserGesture: false,
|
mediaPlaybackRequiresUserGesture: false,
|
||||||
useHybridComposition: true,
|
useHybridComposition: true,
|
||||||
allowsInlineMediaPlayback: true
|
allowsInlineMediaPlayback: true);
|
||||||
);
|
|
||||||
|
|
||||||
late PullToRefreshController pullToRefreshController;
|
late PullToRefreshController pullToRefreshController;
|
||||||
late ContextMenu contextMenu;
|
late ContextMenu contextMenu;
|
||||||
|
@ -41,8 +35,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!");
|
||||||
|
@ -50,7 +43,7 @@ class _InAppWebViewExampleScreenState extends State<InAppWebViewExampleScreen> {
|
||||||
await webViewController?.clearFocus();
|
await webViewController?.clearFocus();
|
||||||
})
|
})
|
||||||
],
|
],
|
||||||
options: ContextMenuOptions(hideDefaultSystemContextMenuItems: false),
|
settings: ContextMenuSettings(hideDefaultSystemContextMenuItems: false),
|
||||||
onCreateContextMenu: (hitTestResult) async {
|
onCreateContextMenu: (hitTestResult) async {
|
||||||
print("onCreateContextMenu");
|
print("onCreateContextMenu");
|
||||||
print(hitTestResult.extra);
|
print(hitTestResult.extra);
|
||||||
|
@ -60,9 +53,7 @@ class _InAppWebViewExampleScreenState extends State<InAppWebViewExampleScreen> {
|
||||||
print("onHideContextMenu");
|
print("onHideContextMenu");
|
||||||
},
|
},
|
||||||
onContextMenuActionItemClicked: (contextMenuItemClicked) async {
|
onContextMenuActionItemClicked: (contextMenuItemClicked) async {
|
||||||
var id = (Platform.isAndroid)
|
var id = contextMenuItemClicked.id;
|
||||||
? contextMenuItemClicked.androidId
|
|
||||||
: contextMenuItemClicked.iosId;
|
|
||||||
print("onContextMenuActionItemClicked: " +
|
print("onContextMenuActionItemClicked: " +
|
||||||
id.toString() +
|
id.toString() +
|
||||||
" " +
|
" " +
|
||||||
|
@ -74,9 +65,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 || defaultTargetPlatform == TargetPlatform.macOS) {
|
||||||
webViewController?.loadUrl(
|
webViewController?.loadUrl(
|
||||||
urlRequest: URLRequest(url: await webViewController?.getUrl()));
|
urlRequest: URLRequest(url: await webViewController?.getUrl()));
|
||||||
}
|
}
|
||||||
|
@ -97,9 +88,7 @@ class _InAppWebViewExampleScreenState extends State<InAppWebViewExampleScreen> {
|
||||||
body: SafeArea(
|
body: SafeArea(
|
||||||
child: Column(children: <Widget>[
|
child: Column(children: <Widget>[
|
||||||
TextField(
|
TextField(
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(prefixIcon: Icon(Icons.search)),
|
||||||
prefixIcon: Icon(Icons.search)
|
|
||||||
),
|
|
||||||
controller: urlController,
|
controller: urlController,
|
||||||
keyboardType: TextInputType.url,
|
keyboardType: TextInputType.url,
|
||||||
onSubmitted: (value) {
|
onSubmitted: (value) {
|
||||||
|
@ -107,94 +96,95 @@ class _InAppWebViewExampleScreenState extends State<InAppWebViewExampleScreen> {
|
||||||
if (url.scheme.isEmpty) {
|
if (url.scheme.isEmpty) {
|
||||||
url = Uri.parse("https://www.google.com/search?q=" + value);
|
url = Uri.parse("https://www.google.com/search?q=" + value);
|
||||||
}
|
}
|
||||||
webViewController?.loadUrl(
|
webViewController?.loadUrl(urlRequest: URLRequest(url: url));
|
||||||
urlRequest: URLRequest(url: url));
|
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Stack(
|
child: Stack(
|
||||||
children: [
|
children: [
|
||||||
InAppWebView(
|
InAppWebView(
|
||||||
key: webViewKey,
|
key: webViewKey,
|
||||||
// contextMenu: contextMenu,
|
// contextMenu: contextMenu,
|
||||||
initialUrlRequest:
|
initialUrlRequest:
|
||||||
URLRequest(url: Uri.parse("http://github.com/flutter/")),
|
URLRequest(url: Uri.parse("http://github.com/flutter/")),
|
||||||
// initialFile: "assets/index.html",
|
// initialFile: "assets/index.html",
|
||||||
initialUserScripts: UnmodifiableListView<UserScript>([]),
|
initialUserScripts: UnmodifiableListView<UserScript>([]),
|
||||||
initialSettings: settings,
|
initialSettings: settings,
|
||||||
pullToRefreshController: pullToRefreshController,
|
// contextMenu: contextMenu,
|
||||||
onWebViewCreated: (controller) {
|
pullToRefreshController: pullToRefreshController,
|
||||||
webViewController = controller;
|
onWebViewCreated: (controller) {
|
||||||
},
|
webViewController = controller;
|
||||||
onLoadStart: (controller, url) async {
|
},
|
||||||
setState(() {
|
onLoadStart: (controller, url) async {
|
||||||
this.url = url.toString();
|
setState(() {
|
||||||
urlController.text = this.url;
|
this.url = url.toString();
|
||||||
});
|
urlController.text = this.url;
|
||||||
},
|
});
|
||||||
onPermissionRequest: (controller, request) async {
|
},
|
||||||
return PermissionResponse(
|
onPermissionRequest: (controller, request) async {
|
||||||
resources: request.resources,
|
return PermissionResponse(
|
||||||
action: PermissionResponseAction.GRANT);
|
resources: request.resources,
|
||||||
},
|
action: PermissionResponseAction.GRANT);
|
||||||
shouldOverrideUrlLoading: (controller, navigationAction) async {
|
},
|
||||||
var uri = navigationAction.request.url!;
|
shouldOverrideUrlLoading:
|
||||||
|
(controller, navigationAction) async {
|
||||||
|
var uri = navigationAction.request.url!;
|
||||||
|
|
||||||
if (![
|
if (![
|
||||||
"http",
|
"http",
|
||||||
"https",
|
"https",
|
||||||
"file",
|
"file",
|
||||||
"chrome",
|
"chrome",
|
||||||
"data",
|
"data",
|
||||||
"javascript",
|
"javascript",
|
||||||
"about"
|
"about"
|
||||||
].contains(uri.scheme)) {
|
].contains(uri.scheme)) {
|
||||||
if (await canLaunch(url)) {
|
if (await canLaunch(url)) {
|
||||||
// Launch the App
|
// Launch the App
|
||||||
await launch(
|
await launch(
|
||||||
url,
|
url,
|
||||||
);
|
);
|
||||||
// and cancel the request
|
// and cancel the request
|
||||||
return NavigationActionPolicy.CANCEL;
|
return NavigationActionPolicy.CANCEL;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return NavigationActionPolicy.ALLOW;
|
return NavigationActionPolicy.ALLOW;
|
||||||
},
|
},
|
||||||
onLoadStop: (controller, url) async {
|
onLoadStop: (controller, url) async {
|
||||||
|
pullToRefreshController.endRefreshing();
|
||||||
|
setState(() {
|
||||||
|
this.url = url.toString();
|
||||||
|
urlController.text = this.url;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
onLoadError: (controller, url, code, message) {
|
||||||
|
pullToRefreshController.endRefreshing();
|
||||||
|
},
|
||||||
|
onProgressChanged: (controller, progress) {
|
||||||
|
if (progress == 100) {
|
||||||
pullToRefreshController.endRefreshing();
|
pullToRefreshController.endRefreshing();
|
||||||
setState(() {
|
}
|
||||||
this.url = url.toString();
|
setState(() {
|
||||||
urlController.text = this.url;
|
this.progress = progress / 100;
|
||||||
});
|
urlController.text = this.url;
|
||||||
},
|
});
|
||||||
onLoadError: (controller, url, code, message) {
|
},
|
||||||
pullToRefreshController.endRefreshing();
|
onUpdateVisitedHistory: (controller, url, androidIsReload) {
|
||||||
},
|
setState(() {
|
||||||
onProgressChanged: (controller, progress) {
|
this.url = url.toString();
|
||||||
if (progress == 100) {
|
urlController.text = this.url;
|
||||||
pullToRefreshController.endRefreshing();
|
});
|
||||||
}
|
},
|
||||||
setState(() {
|
onConsoleMessage: (controller, consoleMessage) {
|
||||||
this.progress = progress / 100;
|
print(consoleMessage);
|
||||||
urlController.text = this.url;
|
},
|
||||||
});
|
),
|
||||||
},
|
progress < 1.0
|
||||||
onUpdateVisitedHistory: (controller, url, androidIsReload) {
|
? LinearProgressIndicator(value: progress)
|
||||||
setState(() {
|
: Container(),
|
||||||
this.url = url.toString();
|
],
|
||||||
urlController.text = this.url;
|
),
|
||||||
});
|
|
||||||
},
|
|
||||||
onConsoleMessage: (controller, consoleMessage) {
|
|
||||||
print(consoleMessage);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
progress < 1.0
|
|
||||||
? LinearProgressIndicator(value: progress)
|
|
||||||
: Container(),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
ButtonBar(
|
ButtonBar(
|
||||||
alignment: MainAxisAlignment.center,
|
alignment: MainAxisAlignment.center,
|
||||||
|
|
|
@ -31,8 +31,7 @@ Future main() async {
|
||||||
ServiceWorkerController serviceWorkerController =
|
ServiceWorkerController serviceWorkerController =
|
||||||
ServiceWorkerController.instance();
|
ServiceWorkerController.instance();
|
||||||
|
|
||||||
await serviceWorkerController
|
await serviceWorkerController.setServiceWorkerClient(ServiceWorkerClient(
|
||||||
.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;
|
||||||
|
|
|
@ -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)
|
|
@ -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
|
||||||
|
|
|
@ -203,13 +203,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 +246,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 +258,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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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?
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -242,8 +242,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:
|
||||||
|
@ -256,15 +256,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;
|
||||||
|
|
|
@ -287,7 +287,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 (Platform.isIOS || Platform.isMacOS) {
|
if (Platform.isIOS || Platform.isMacOS) {
|
||||||
settings.toolbarTopTranslucent = map["toolbarTopTranslucent"];
|
settings.toolbarTopTranslucent = map["toolbarTopTranslucent"];
|
||||||
|
@ -302,9 +302,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));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -469,8 +469,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
|
||||||
|
|
|
@ -732,9 +732,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)
|
||||||
|
@ -748,11 +748,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;
|
||||||
|
@ -945,16 +946,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!();
|
||||||
|
@ -1847,7 +1850,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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2306,7 +2310,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
|
||||||
}
|
}
|
||||||
|
|
|
@ -1357,7 +1357,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"];
|
||||||
|
@ -1367,7 +1367,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(
|
||||||
|
@ -1386,28 +1386,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"];
|
||||||
|
@ -1415,24 +1415,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;
|
||||||
|
|
|
@ -510,8 +510,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.
|
||||||
|
|
|
@ -4769,7 +4769,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,
|
||||||
|
@ -4779,7 +4780,8 @@ class PermissionResourceType {
|
||||||
static PermissionResourceType? fromValue(dynamic? value) {
|
static PermissionResourceType? fromValue(dynamic? value) {
|
||||||
if (value != null) {
|
if (value != null) {
|
||||||
try {
|
try {
|
||||||
Set<PermissionResourceType> valueList = <PermissionResourceType>[].toSet();
|
Set<PermissionResourceType> valueList =
|
||||||
|
<PermissionResourceType>[].toSet();
|
||||||
if (Platform.isAndroid) {
|
if (Platform.isAndroid) {
|
||||||
valueList = PermissionResourceType._androidValues;
|
valueList = PermissionResourceType._androidValues;
|
||||||
} else if (Platform.isIOS || Platform.isMacOS) {
|
} else if (Platform.isIOS || Platform.isMacOS) {
|
||||||
|
@ -4815,52 +4817,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;
|
||||||
|
|
||||||
|
@ -4880,9 +4881,7 @@ class PermissionRequest {
|
||||||
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) {
|
||||||
|
@ -4891,8 +4890,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);
|
||||||
|
@ -4908,7 +4906,7 @@ class PermissionRequest {
|
||||||
|
|
||||||
Map<String, dynamic> toMap() {
|
Map<String, dynamic> toMap() {
|
||||||
return {
|
return {
|
||||||
"origin": origin.toString(),
|
"origin": origin.toString(),
|
||||||
"resources": resources.map((e) => e.toValue()).toList(),
|
"resources": resources.map((e) => e.toValue()).toList(),
|
||||||
"frame": frame?.toMap()
|
"frame": frame?.toMap()
|
||||||
};
|
};
|
||||||
|
@ -4933,8 +4931,7 @@ class PermissionResponse {
|
||||||
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 {
|
||||||
|
@ -10688,19 +10685,16 @@ 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;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
int get hashCode => _value.hashCode;
|
int get hashCode => _value.hashCode;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue