From 9f9232e1f31005d8a57fa96609db40354d4badbf Mon Sep 17 00:00:00 2001 From: Lorenzo Pichilli Date: Sat, 30 May 2020 20:23:33 +0200 Subject: [PATCH] added clearHistory webview methods on Android, setContextMenu and clearFocus webview methods, added ContextMenuOptions class --- CHANGELOG.md | 5 +- README.md | 25 +++--- .../InAppBrowser/InAppBrowserActivity.java | 21 +++++ .../InAppBrowser/InAppBrowserOptions.java | 12 +-- .../InAppWebView/ContextMenuOptions.java | 43 ++++++++++ .../InAppWebView/FlutterWebView.java | 21 ++++- .../InAppWebView/InAppWebView.java | 83 ++++++++++--------- .../flutter_inappwebview/Options.java | 3 +- example/.flutter-plugins-dependencies | 2 +- example/lib/in_app_webiew_example.screen.dart | 24 +++--- ios/Classes/ContextMenuOptions.swift | 17 ++++ ios/Classes/FlutterWebViewController.swift | 17 ++++ .../InAppBrowserWebViewController.swift | 18 ++++ ios/Classes/InAppWebView.swift | 14 ++++ lib/src/context_menu.dart | 49 +++++++++-- lib/src/in_app_browser.dart | 4 +- lib/src/in_app_webview_controller.dart | 25 ++++++ 17 files changed, 305 insertions(+), 78 deletions(-) create mode 100644 android/src/main/java/com/pichillilorenzo/flutter_inappwebview/InAppWebView/ContextMenuOptions.java create mode 100644 ios/Classes/ContextMenuOptions.swift diff --git a/CHANGELOG.md b/CHANGELOG.md index 6871bb0e..176cf0c2 100755 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,12 +3,14 @@ - Updated API docs - Updated Android context menu workaround - Calling `onCreateContextMenu` event on iOS also when the context menu is disabled in order to have the same effect as Android +- Added `options` attribute to `ContextMenu` class and created `ContextMenuOptions` class - Added Android keyboard workaround to hide the keyboard when clicking other HTML elements, losing the focus on the previous input - Added `onEnterFullscreen`, `onExitFullscreen` webview events [#275](https://github.com/pichillilorenzo/flutter_inappwebview/issues/275) - Added Android support to use camera on HTML inputs that requires it, such as `` [#353](https://github.com/pichillilorenzo/flutter_inappwebview/issues/353) - Added `overScrollMode`, `networkAvailable`, `scrollBarStyle`, `verticalScrollbarPosition`, `scrollBarDefaultDelayBeforeFade`, `scrollbarFadingEnabled`, `scrollBarFadeDuration`, `rendererPriorityPolicy`, `useShouldInterceptRequest`, `useOnRenderProcessGone` webview options on Android -- Added `pageDown`, `pageUp`, `saveWebArchive`, `zoomIn`, `zoomOut` webview methods on Android +- Added `pageDown`, `pageUp`, `saveWebArchive`, `zoomIn`, `zoomOut`, `clearHistory` webview methods on Android - Added `getCurrentWebViewPackage` static webview method on Android +- Added `setContextMenu`, `clearFocus` methods to webview controller - Added `onPageCommitVisible` webview event - Added `androidShouldInterceptRequest`, `androidOnRenderProcessUnresponsive`, `androidOnRenderProcessResponsive`, `androidOnRenderProcessGone`, `androidOnFormResubmission`, `androidOnScaleChanged` Android events - Added `toString()` method to various classes in order to have a better output instead of simply `Instance of ...` @@ -23,6 +25,7 @@ - Android `clearClientCertPreferences`, `getSafeBrowsingPrivacyPolicyUrl`, `setSafeBrowsingWhitelist` webview methods are static now - Removed iOS event `onDidCommit`; it has been renamed to `onPageCommitVisible` and made cross-platform +- `contextMenu` is `final` now ## 3.2.0 diff --git a/README.md b/README.md index b8e1fbf0..c5af3b26 100755 --- a/README.md +++ b/README.md @@ -400,6 +400,8 @@ Screenshots: * `getScale`: Gets the current scale of this WebView. * `getSelectedText`: Gets the selected text. * `getHitTestResult`: Gets the hit result for hitting an HTML elements. +* `clearFocus`: Clears the current focus. It will clear also, for example, the current text selection. +* `setContextMenu(ContextMenu contextMenu)`: Sets or updates the WebView context menu to be used next time it will appear. * `static getDefaultUserAgent`: Gets the default user agent. ##### `InAppWebViewController` Android-specific methods @@ -416,6 +418,7 @@ Android-specific methods can be called using the `InAppWebViewController.android * `saveWebArchive({@required String basename, @required bool autoname})`: Saves the current view as a web archive. * `zoomIn`: Performs zoom in in this WebView. * `zoomOut`: Performs zoom out in this WebView. +* `clearHistory`: Clears the internal back/forward list. * `static clearClientCertPreferences`: Clears the client certificate preferences stored in response to proceeding/cancelling client cert requests. * `static getSafeBrowsingPrivacyPolicyUrl`: Returns a URL pointing to the privacy policy for Safe Browsing reporting. This value will never be `null`. * `static setSafeBrowsingWhitelist({@required List hosts})`: Sets the list of hosts (domain names/IP addresses) that are exempt from SafeBrowsing checks. The list is global for all the WebViews. @@ -665,6 +668,11 @@ class _MyAppState extends State { super.initState(); contextMenu = ContextMenu( + menuItems: [ + ContextMenuItem(androidId: 1, iosId: "1", title: "Special", action: () async { + print("Menu item Special clicked!"); + }) + ], onCreateContextMenu: (hitTestResult) async { print("onCreateContextMenu"); print(hitTestResult.extra); @@ -679,12 +687,6 @@ class _MyAppState extends State { } ); - contextMenu.menuItems = [ - ContextMenuItem(androidId: 1, iosId: "1", title: "Special", action: () async { - print("Menu item Special clicked!"); - }) - ]; - } @override @@ -782,6 +784,10 @@ class _MyAppState extends State { } ``` +### `ContextMenu` options + +* `hideDefaultSystemContextMenuItems`: Whether all the default system context menu items should be hidden or not. The default value is `false`. + ### `ContextMenu` Events * `onCreateContextMenu`: Event fired when the context menu for this WebView is being built. @@ -1118,7 +1124,7 @@ class MyInAppBrowser extends InAppBrowser { } class MyChromeSafariBrowser extends ChromeSafariBrowser { - + MyChromeSafariBrowser(browserFallback) : super(bFallback: browserFallback); @override @@ -1146,7 +1152,7 @@ void main() { class MyApp extends StatefulWidget { final ChromeSafariBrowser browser = new MyChromeSafariBrowser(new MyInAppBrowser()); - + @override _MyAppState createState() => new _MyAppState(); } @@ -1182,7 +1188,7 @@ class _MyAppState extends State { url: "https://flutter.dev/", options: ChromeSafariBrowserClassOptions( android: AndroidChromeCustomTabsOptions(addDefaultShareMenuItem: false), - ios: IosSafariOptions(barCollapsingEnabled: true))); + ios: IOSSafariOptions(barCollapsingEnabled: true))); }, child: Text("Open Chrome Safari Browser")), ), @@ -1190,7 +1196,6 @@ class _MyAppState extends State { ); } } - ``` Screenshots: diff --git a/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/InAppBrowser/InAppBrowserActivity.java b/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/InAppBrowser/InAppBrowserActivity.java index 62ed8ee9..9f08b3f5 100755 --- a/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/InAppBrowser/InAppBrowserActivity.java +++ b/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/InAppBrowser/InAppBrowserActivity.java @@ -365,6 +365,17 @@ public class InAppBrowserActivity extends AppCompatActivity implements MethodCha case "zoomOut": result.success(zoomOut()); break; + case "clearFocus": + clearFocus(); + result.success(true); + break; + case "setContextMenu": + { + Map contextMenu = (Map) call.argument("contextMenu"); + setContextMenu(contextMenu); + } + result.success(true); + break; default: result.notImplemented(); } @@ -909,6 +920,16 @@ public class InAppBrowserActivity extends AppCompatActivity implements MethodCha return false; } + public void clearFocus() { + if (webView != null) + webView.clearFocus(); + } + + public void setContextMenu(Map contextMenu) { + if (webView != null) + webView.contextMenu = contextMenu; + } + public void dispose() { channel.setMethodCallHandler(null); activityResultListeners.clear(); diff --git a/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/InAppBrowser/InAppBrowserOptions.java b/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/InAppBrowser/InAppBrowserOptions.java index 9398a5e9..0c09dd26 100755 --- a/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/InAppBrowser/InAppBrowserOptions.java +++ b/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/InAppBrowser/InAppBrowserOptions.java @@ -30,10 +30,10 @@ public class InAppBrowserOptions implements Options { switch (key) { case "hidden": - hidden = (boolean) value; + hidden = (Boolean) value; break; case "toolbarTop": - toolbarTop = (boolean) value; + toolbarTop = (Boolean) value; break; case "toolbarTopBackgroundColor": toolbarTopBackgroundColor = (String) value; @@ -42,16 +42,16 @@ public class InAppBrowserOptions implements Options { toolbarTopFixedTitle = (String) value; break; case "hideUrlBar": - hideUrlBar = (boolean) value; + hideUrlBar = (Boolean) value; break; case "hideTitleBar": - hideTitleBar = (boolean) value; + hideTitleBar = (Boolean) value; break; case "closeOnCannotGoBack": - closeOnCannotGoBack = (boolean) value; + closeOnCannotGoBack = (Boolean) value; break; case "progressBar": - progressBar = (boolean) value; + progressBar = (Boolean) value; break; } } diff --git a/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/InAppWebView/ContextMenuOptions.java b/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/InAppWebView/ContextMenuOptions.java new file mode 100644 index 00000000..cb42253c --- /dev/null +++ b/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/InAppWebView/ContextMenuOptions.java @@ -0,0 +1,43 @@ +package com.pichillilorenzo.flutter_inappwebview.InAppWebView; + +import com.pichillilorenzo.flutter_inappwebview.Options; + +import java.util.HashMap; +import java.util.Map; + +public class ContextMenuOptions implements Options { + public static final String LOG_TAG = "ContextMenuOptions"; + + public Boolean hideDefaultSystemContextMenuItems = false; + + public ContextMenuOptions parse(Map options) { + for (Map.Entry pair : options.entrySet()) { + String key = pair.getKey(); + Object value = pair.getValue(); + if (value == null) { + continue; + } + + switch (key) { + case "hideDefaultSystemContextMenuItems": + hideDefaultSystemContextMenuItems = (Boolean) value; + break; + } + } + + return this; + } + + public Map toMap() { + Map options = new HashMap<>(); + options.put("hideDefaultSystemContextMenuItems", hideDefaultSystemContextMenuItems); + return options; + } + + @Override + public Map getRealOptions(Object webView) { + Map realOptions = toMap(); + return realOptions; + } + +} diff --git a/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/InAppWebView/FlutterWebView.java b/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/InAppWebView/FlutterWebView.java index 978bd12a..e9be1ade 100755 --- a/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/InAppWebView/FlutterWebView.java +++ b/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/InAppWebView/FlutterWebView.java @@ -52,8 +52,8 @@ public class FlutterWebView implements PlatformView, MethodCallHandler { final String initialFile = (String) params.get("initialFile"); final Map initialData = (Map) params.get("initialData"); final Map initialHeaders = (Map) params.get("initialHeaders"); - HashMap initialOptions = (HashMap) params.get("initialOptions"); - HashMap contextMenu = (HashMap) params.get("contextMenu"); + Map initialOptions = (Map) params.get("initialOptions"); + Map contextMenu = (Map) params.get("contextMenu"); InAppWebViewOptions options = new InAppWebViewOptions(); options.parse(initialOptions); @@ -440,6 +440,23 @@ public class FlutterWebView implements PlatformView, MethodCallHandler { result.success(false); } break; + case "clearFocus": + if (webView != null) { + webView.clearFocus(); + result.success(true); + } else { + result.success(false); + } + break; + case "setContextMenu": + if (webView != null) { + Map contextMenu = (Map) call.argument("contextMenu"); + webView.contextMenu = contextMenu; + result.success(true); + } else { + result.success(false); + } + break; default: result.notImplemented(); } diff --git a/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/InAppWebView/InAppWebView.java b/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/InAppWebView/InAppWebView.java index 7cbbf785..d00e1141 100755 --- a/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/InAppWebView/InAppWebView.java +++ b/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/InAppWebView/InAppWebView.java @@ -82,7 +82,7 @@ final public class InAppWebView extends InputAwareWebView { public Pattern regexToCancelSubFramesLoadingCompiled; public GestureDetector gestureDetector = null; public LinearLayout floatingContextMenu = null; - public HashMap contextMenu = null; + public Map contextMenu = null; public Handler headlessHandler = new Handler(Looper.getMainLooper()); public Runnable checkScrollStoppedTask; @@ -608,7 +608,7 @@ final public class InAppWebView extends InputAwareWebView { super(context, attrs, defaultStyle); } - public InAppWebView(Context context, Object obj, Object id, InAppWebViewOptions options, HashMap contextMenu, View containerView) { + public InAppWebView(Context context, Object obj, Object id, InAppWebViewOptions options, Map contextMenu, View containerView) { super(context, containerView); if (obj instanceof InAppBrowserActivity) this.inAppBrowserActivity = (InAppBrowserActivity) obj; @@ -1597,10 +1597,49 @@ final public class InAppWebView extends InputAwareWebView { HorizontalScrollView horizontalScrollView = (HorizontalScrollView) floatingContextMenu.getChildAt(0); LinearLayout menuItemListLayout = (LinearLayout) horizontalScrollView.getChildAt(0); - for (int i = 0; i < actionMenu.size(); i++) { - final MenuItem menuItem = actionMenu.getItem(i); - final int itemId = menuItem.getItemId(); - final String itemTitle = menuItem.getTitle().toString(); + List> customMenuItems = new ArrayList<>(); + ContextMenuOptions contextMenuOptions = new ContextMenuOptions(); + if (contextMenu != null) { + customMenuItems = (List>) contextMenu.get("menuItems"); + Map contextMenuOptionsMap = (Map) contextMenu.get("options"); + if (contextMenuOptionsMap != null) { + contextMenuOptions.parse(contextMenuOptionsMap); + } + } + customMenuItems = customMenuItems == null ? new ArrayList>() : customMenuItems; + + if (contextMenuOptions.hideDefaultSystemContextMenuItems == null || !contextMenuOptions.hideDefaultSystemContextMenuItems) { + for (int i = 0; i < actionMenu.size(); i++) { + final MenuItem menuItem = actionMenu.getItem(i); + final int itemId = menuItem.getItemId(); + final String itemTitle = menuItem.getTitle().toString(); + TextView text = (TextView) LayoutInflater.from(this.getContext()) + .inflate(R.layout.floating_action_mode_item, this, false); + text.setText(itemTitle); + text.setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + hideContextMenu(); + callback.onActionItemClicked(actionMode, menuItem); + + Map obj = new HashMap<>(); + if (inAppBrowserActivity != null) + obj.put("uuid", inAppBrowserActivity.uuid); + obj.put("androidId", itemId); + obj.put("iosId", null); + obj.put("title", itemTitle); + channel.invokeMethod("onContextMenuActionItemClicked", obj); + } + }); + if (floatingContextMenu != null) { + menuItemListLayout.addView(text); + } + } + } + + for (final Map menuItem : customMenuItems) { + final int itemId = (int) menuItem.get("androidId"); + final String itemTitle = (String) menuItem.get("title"); TextView text = (TextView) LayoutInflater.from(this.getContext()) .inflate(R.layout.floating_action_mode_item, this, false); text.setText(itemTitle); @@ -1608,7 +1647,6 @@ final public class InAppWebView extends InputAwareWebView { @Override public void onClick(View v) { hideContextMenu(); - callback.onActionItemClicked(actionMode, menuItem); Map obj = new HashMap<>(); if (inAppBrowserActivity != null) @@ -1621,38 +1659,7 @@ final public class InAppWebView extends InputAwareWebView { }); if (floatingContextMenu != null) { menuItemListLayout.addView(text); - } - } - if (contextMenu != null) { - List> customMenuItems = (List>) contextMenu.get("menuItems"); - if (customMenuItems != null) { - for (final HashMap menuItem : customMenuItems) { - final int itemId = (int) menuItem.get("androidId"); - final String itemTitle = (String) menuItem.get("title"); - TextView text = (TextView) LayoutInflater.from(this.getContext()) - .inflate(R.layout.floating_action_mode_item, this, false); - text.setText(itemTitle); - text.setOnClickListener(new OnClickListener() { - @Override - public void onClick(View v) { - // clearFocus(); - hideContextMenu(); - - Map obj = new HashMap<>(); - if (inAppBrowserActivity != null) - obj.put("uuid", inAppBrowserActivity.uuid); - obj.put("androidId", itemId); - obj.put("iosId", null); - obj.put("title", itemTitle); - channel.invokeMethod("onContextMenuActionItemClicked", obj); - } - }); - if (floatingContextMenu != null) { - menuItemListLayout.addView(text); - - } - } } } diff --git a/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/Options.java b/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/Options.java index bd5440ac..bc3c06e3 100755 --- a/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/Options.java +++ b/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/Options.java @@ -3,8 +3,7 @@ package com.pichillilorenzo.flutter_inappwebview; import java.util.Map; public interface Options { - static String LOG_TAG = "Options"; public Options parse(Map options); public Map toMap(); - public Map getRealOptions(T webView); + public Map getRealOptions(T obj); } diff --git a/example/.flutter-plugins-dependencies b/example/.flutter-plugins-dependencies index 5891ba58..8ec33c1e 100755 --- a/example/.flutter-plugins-dependencies +++ b/example/.flutter-plugins-dependencies @@ -1 +1 @@ -{"info":"This is a generated file; do not edit or check into version control.","plugins":{"ios":[{"name":"e2e","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/e2e-0.2.4+4/","dependencies":[]},{"name":"flutter_inappwebview","path":"/Users/lorenzopichilli/Desktop/flutter_inappwebview/","dependencies":[]},{"name":"permission_handler","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/permission_handler-5.0.0+hotfix.6/","dependencies":[]}],"android":[{"name":"e2e","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/e2e-0.2.4+4/","dependencies":[]},{"name":"flutter_inappwebview","path":"/Users/lorenzopichilli/Desktop/flutter_inappwebview/","dependencies":[]},{"name":"permission_handler","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/permission_handler-5.0.0+hotfix.6/","dependencies":[]}],"macos":[],"linux":[],"windows":[],"web":[]},"dependencyGraph":[{"name":"e2e","dependencies":[]},{"name":"flutter_inappwebview","dependencies":[]},{"name":"permission_handler","dependencies":[]}],"date_created":"2020-05-29 19:53:44.213613","version":"1.17.1"} \ No newline at end of file +{"info":"This is a generated file; do not edit or check into version control.","plugins":{"ios":[{"name":"e2e","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/e2e-0.2.4+4/","dependencies":[]},{"name":"flutter_inappwebview","path":"/Users/lorenzopichilli/Desktop/flutter_inappwebview/","dependencies":[]},{"name":"permission_handler","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/permission_handler-5.0.0+hotfix.6/","dependencies":[]}],"android":[{"name":"e2e","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/e2e-0.2.4+4/","dependencies":[]},{"name":"flutter_inappwebview","path":"/Users/lorenzopichilli/Desktop/flutter_inappwebview/","dependencies":[]},{"name":"permission_handler","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/permission_handler-5.0.0+hotfix.6/","dependencies":[]}],"macos":[],"linux":[],"windows":[],"web":[]},"dependencyGraph":[{"name":"e2e","dependencies":[]},{"name":"flutter_inappwebview","dependencies":[]},{"name":"permission_handler","dependencies":[]}],"date_created":"2020-05-30 19:55:39.840707","version":"1.17.1"} \ No newline at end of file diff --git a/example/lib/in_app_webiew_example.screen.dart b/example/lib/in_app_webiew_example.screen.dart index 7c4f6c2e..cfba732d 100755 --- a/example/lib/in_app_webiew_example.screen.dart +++ b/example/lib/in_app_webiew_example.screen.dart @@ -23,6 +23,16 @@ class _InAppWebViewExampleScreenState extends State { super.initState(); contextMenu = ContextMenu( + menuItems: [ + ContextMenuItem(androidId: 1, iosId: "1", title: "Special", action: () async { + print("Menu item Special clicked!"); + print(await webView.getSelectedText()); + await webView.clearFocus(); + }) + ], + options: ContextMenuOptions( + hideDefaultSystemContextMenuItems: true + ), onCreateContextMenu: (hitTestResult) async { print("onCreateContextMenu"); print(hitTestResult.extra); @@ -31,16 +41,11 @@ class _InAppWebViewExampleScreenState extends State { onHideContextMenu: () { print("onHideContextMenu"); }, - onContextMenuActionItemClicked: (contextMenuItemClicked) { + onContextMenuActionItemClicked: (contextMenuItemClicked) async { var id = (Platform.isAndroid) ? contextMenuItemClicked.androidId : contextMenuItemClicked.iosId; print("onContextMenuActionItemClicked: " + id.toString() + " " + contextMenuItemClicked.title); } ); - contextMenu.menuItems = [ - ContextMenuItem(androidId: 1, iosId: "1", title: "Special", action: () async { - print("Menu item Special clicked!"); - }) - ]; } @override @@ -74,17 +79,14 @@ class _InAppWebViewExampleScreenState extends State { BoxDecoration(border: Border.all(color: Colors.blueAccent)), child: InAppWebView( contextMenu: contextMenu, - // initialUrl: "https://github.com/flutter", - initialFile: "assets/index.html", + initialUrl: "https://github.com/flutter", + // initialFile: "assets/index.html", initialHeaders: {}, initialOptions: InAppWebViewGroupOptions( crossPlatform: InAppWebViewOptions( debuggingEnabled: true, useShouldOverrideUrlLoading: true ), - android: AndroidInAppWebViewOptions( - supportMultipleWindows: true - ) ), onWebViewCreated: (InAppWebViewController controller) { webView = controller; diff --git a/ios/Classes/ContextMenuOptions.swift b/ios/Classes/ContextMenuOptions.swift new file mode 100644 index 00000000..39a3a69c --- /dev/null +++ b/ios/Classes/ContextMenuOptions.swift @@ -0,0 +1,17 @@ +// +// ContextMenuOptions.swift +// flutter_inappwebview +// +// Created by Lorenzo Pichilli on 30/05/2020. +// + +import Foundation + +class ContextMenuOptions: Options { + + var hideDefaultSystemContextMenuItems = false; + + override init(){ + super.init() + } +} diff --git a/ios/Classes/FlutterWebViewController.swift b/ios/Classes/FlutterWebViewController.swift index 41440b9e..4bf95f31 100755 --- a/ios/Classes/FlutterWebViewController.swift +++ b/ios/Classes/FlutterWebViewController.swift @@ -426,6 +426,23 @@ public class FlutterWebViewController: FlutterMethodCallDelegate, FlutterPlatfor result(nil) } break + case "clearFocus": + if webView != nil { + webView!.clearFocus() + result(true) + } else { + result(false) + } + break + case "setContextMenu": + if webView != nil { + let contextMenu = arguments!["contextMenu"] as? [String: Any] + webView!.contextMenu = contextMenu + result(true) + } else { + result(false) + } + break default: result(FlutterMethodNotImplemented) break diff --git a/ios/Classes/InAppBrowserWebViewController.swift b/ios/Classes/InAppBrowserWebViewController.swift index 2d71f313..75e351b3 100755 --- a/ios/Classes/InAppBrowserWebViewController.swift +++ b/ios/Classes/InAppBrowserWebViewController.swift @@ -308,6 +308,24 @@ public class InAppBrowserWebViewController: UIViewController, FlutterPlugin, UIS result(nil) } break + case "clearFocus": + if webView != nil { + webView!.clearFocus() + result(true) + } else { + result(false) + } + + break + case "setContextMenu": + if webView != nil { + let contextMenu = arguments!["contextMenu"] as? [String: Any] + webView!.contextMenu = contextMenu + result(true) + } else { + result(false) + } + break default: result(FlutterMethodNotImplemented) break diff --git a/ios/Classes/InAppWebView.swift b/ios/Classes/InAppWebView.swift index 7a346b68..e9beed31 100755 --- a/ios/Classes/InAppWebView.swift +++ b/ios/Classes/InAppWebView.swift @@ -905,6 +905,16 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavi return false } + if let menu = contextMenu { + let contextMenuOptions = ContextMenuOptions() + if let contextMenuOptionsMap = menu["options"] as? [String: Any?] { + let _ = contextMenuOptions.parse(options: contextMenuOptionsMap) + if !action.description.starts(with: "onContextMenuActionItemClicked-") && contextMenuOptions.hideDefaultSystemContextMenuItems { + return false + } + } + } + if contextMenuIsShowing, !action.description.starts(with: "onContextMenuActionItemClicked-") { let id = action.description.compactMap({ $0.asciiValue?.description }).joined() let arguments: [String: Any?] = [ @@ -2729,6 +2739,10 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavi } } + public func clearFocus() { + self.scrollView.subviews.first?.resignFirstResponder() + } + public func dispose() { stopLoading() configuration.userContentController.removeScriptMessageHandler(forName: "consoleLog") diff --git a/lib/src/context_menu.dart b/lib/src/context_menu.dart index fccb8304..a42ba0e7 100644 --- a/lib/src/context_menu.dart +++ b/lib/src/context_menu.dart @@ -1,6 +1,6 @@ import 'package:flutter/foundation.dart'; -import 'package:flutter_inappwebview/src/webview.dart'; +import 'webview.dart'; import 'types.dart'; ///Class that represents the WebView context menu. It used by [WebView.contextMenu]. @@ -22,24 +22,35 @@ class ContextMenu { final void Function(ContextMenuItem contextMenuItemClicked) onContextMenuActionItemClicked; + ///Context menu options. + final ContextMenuOptions options; + ///List of the custom [ContextMenuItem]. - List menuItems = List(); + final List menuItems; ContextMenu( - {this.menuItems, + {this.menuItems = const [], this.onCreateContextMenu, this.onHideContextMenu, - this.onContextMenuActionItemClicked}); + this.options, + this.onContextMenuActionItemClicked}) + : assert(menuItems != null); Map toMap() { return { - "menuItems": menuItems.map((menuItem) => menuItem?.toMap()).toList() + "menuItems": menuItems.map((menuItem) => menuItem?.toMap()).toList(), + "options": options?.toMap() }; } Map toJson() { return this.toMap(); } + + @override + String toString() { + return toMap().toString(); + } } ///Class that represent an item of the [ContextMenu]. @@ -69,4 +80,32 @@ class ContextMenuItem { Map toJson() { return this.toMap(); } + + @override + String toString() { + return toMap().toString(); + } +} + +///Class that represents available options used by [ContextMenu]. +class ContextMenuOptions { + ///Whether all the default system context menu items should be hidden or not. The default value is `false`. + bool hideDefaultSystemContextMenuItems; + + ContextMenuOptions({this.hideDefaultSystemContextMenuItems = false}); + + Map toMap() { + return { + "hideDefaultSystemContextMenuItems": hideDefaultSystemContextMenuItems + }; + } + + Map toJson() { + return this.toMap(); + } + + @override + String toString() { + return toMap().toString(); + } } diff --git a/lib/src/in_app_browser.dart b/lib/src/in_app_browser.dart index 67912f4e..925c1617 100755 --- a/lib/src/in_app_browser.dart +++ b/lib/src/in_app_browser.dart @@ -13,10 +13,10 @@ import 'types.dart'; ///This class uses the native WebView of the platform. ///The [webViewController] field can be used to access the [InAppWebViewController] API. class InAppBrowser { - ///Browser's UUID + ///Browser's UUID. String uuid; - ///Context menu used by the browser + ///Context menu used by the browser. It should be set before opening the browser. ContextMenu contextMenu; Map javaScriptHandlersMap = diff --git a/lib/src/in_app_webview_controller.dart b/lib/src/in_app_webview_controller.dart index 2b93031d..69568709 100644 --- a/lib/src/in_app_webview_controller.dart +++ b/lib/src/in_app_webview_controller.dart @@ -1487,6 +1487,23 @@ class InAppWebViewController { return InAppWebViewHitTestResult(type: type, extra: extra); } + ///Clears the current focus. It will clear also, for example, the current text selection. + /// + ///**Official Android API**: https://developer.android.com/reference/android/view/ViewGroup#clearFocus() + ///**Official iOS API**: https://developer.apple.com/documentation/uikit/uiresponder/1621097-resignfirstresponder + Future clearFocus() async { + Map args = {}; + return await _channel.invokeMethod('clearFocus', args); + } + + ///Sets or updates the WebView context menu to be used next time it will appear. + Future setContextMenu(ContextMenu contextMenu) async { + Map args = {}; + args.putIfAbsent("contextMenu", () => contextMenu?.toMap()); + await _channel.invokeMethod('setContextMenu', args); + _inAppBrowser?.contextMenu = contextMenu; + } + ///Gets the default user agent. /// ///**Official Android API**: https://developer.android.com/reference/android/webkit/WebSettings#getDefaultUserAgent(android.content.Context) @@ -1617,6 +1634,14 @@ class AndroidInAppWebViewController { return await _controller._channel.invokeMethod('zoomOut', args); } + ///Clears the internal back/forward list. + /// + ///**Official Android API**: https://developer.android.com/reference/android/webkit/WebView#clearHistory() + Future clearHistory() async { + Map args = {}; + return await _controller._channel.invokeMethod('clearHistory', args); + } + ///Clears the client certificate preferences stored in response to proceeding/cancelling client cert requests. ///Note that WebView automatically clears these preferences when the system keychain is updated. ///The preferences are shared by all the WebViews that are created by the embedder application.