diff --git a/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/webview/InAppWebViewInterface.java b/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/webview/InAppWebViewInterface.java index 60bf917d6..f5cc6c6ab 100644 --- a/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/webview/InAppWebViewInterface.java +++ b/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/webview/InAppWebViewInterface.java @@ -81,6 +81,7 @@ void loadDataWithBaseURL(String baseUrl, String data, boolean zoomIn(); boolean zoomOut(); void clearFocus(); + void requestFocusView(); // For requestFocus Map requestFocusNodeHref(); Map requestImageRef(); int getScrollX(); diff --git a/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/webview/WebViewChannelDelegate.java b/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/webview/WebViewChannelDelegate.java index 6e480a19a..3bdc7111d 100644 --- a/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/webview/WebViewChannelDelegate.java +++ b/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/webview/WebViewChannelDelegate.java @@ -483,6 +483,12 @@ public void onReceiveValue(String value) { } result.success(true); break; + case requestFocus: + if (webView != null) { + webView.requestFocusView(); + } + result.success(true); + break; case requestFocusNodeHref: if (webView != null) { result.success(webView.requestFocusNodeHref()); diff --git a/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/webview/WebViewChannelDelegateMethods.java b/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/webview/WebViewChannelDelegateMethods.java index d87954f69..a144d4fca 100644 --- a/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/webview/WebViewChannelDelegateMethods.java +++ b/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/webview/WebViewChannelDelegateMethods.java @@ -60,6 +60,7 @@ public enum WebViewChannelDelegateMethods { zoomOut, clearFocus, setContextMenu, + requestFocus, requestFocusNodeHref, requestImageRef, getScrollX, diff --git a/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/webview/in_app_webview/InAppWebView.java b/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/webview/in_app_webview/InAppWebView.java index ad24f2590..f9cdc95e7 100755 --- a/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/webview/in_app_webview/InAppWebView.java +++ b/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/webview/in_app_webview/InAppWebView.java @@ -1719,6 +1719,15 @@ public void onReceiveValue(String value) { }); } + public void requestFocusView() { + requestFocus(); + InputMethodManager inputManager = + (InputMethodManager) this.getContext().getSystemService(Context.INPUT_METHOD_SERVICE); + if (inputManager != null) { + inputManager.toggleSoftInput(InputMethodManager.SHOW_IMPLICIT, InputMethodManager.HIDE_NOT_ALWAYS); + } + } + public Map requestFocusNodeHref() { Message msg = InAppWebView.mHandler.obtainMessage(); requestFocusNodeHref(msg); diff --git a/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/webview/in_app_webview/InputAwareWebView.java b/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/webview/in_app_webview/InputAwareWebView.java index 0613f133f..76ca143b1 100755 --- a/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/webview/in_app_webview/InputAwareWebView.java +++ b/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/webview/in_app_webview/InputAwareWebView.java @@ -150,6 +150,11 @@ public boolean checkInputConnectionProxy(final View view) { public void clearFocus() { super.clearFocus(); + InputMethodManager inputManager = (InputMethodManager) this.getContext().getSystemService(Context.INPUT_METHOD_SERVICE); + if (inputManager != null) { + inputManager.hideSoftInputFromWindow(this.getWindowToken(), 0); + } + if (useHybridComposition) { return; } diff --git a/ios/Classes/InAppWebView/InAppWebView.swift b/ios/Classes/InAppWebView/InAppWebView.swift index 1506aab5b..d18b465a7 100755 --- a/ios/Classes/InAppWebView/InAppWebView.swift +++ b/ios/Classes/InAppWebView/InAppWebView.swift @@ -9,6 +9,75 @@ import Flutter import Foundation import WebKit +// Support `keyboardDisplayRequiresUserAction` to focus automaticlly +typealias OldClosureType = @convention(c) (Any, Selector, UnsafeRawPointer, Bool, Bool, Any?) -> Void +typealias NewClosureType = @convention(c) (Any, Selector, UnsafeRawPointer, Bool, Bool, Bool, Any?) -> Void +extension WKWebView{ + var keyboardDisplayRequiresUserAction: Bool? { + get { + return self.keyboardDisplayRequiresUserAction + } + set { + self.setKeyboardRequiresUserInteraction(newValue ?? true) + } + } + + func setKeyboardRequiresUserInteraction( _ value: Bool) { + guard let WKContentView: AnyClass = NSClassFromString("WKContentView") else { + print("keyboardDisplayRequiresUserAction extension: Cannot find the WKContentView class") + return + } + // For iOS 10, * + let sel_10: Selector = sel_getUid("_startAssistingNode:userIsInteracting:blurPreviousNode:userObject:") + // For iOS 11.3, * + let sel_11_3: Selector = sel_getUid("_startAssistingNode:userIsInteracting:blurPreviousNode:changingActivityState:userObject:") + // For iOS 12.2, * + let sel_12_2: Selector = sel_getUid("_elementDidFocus:userIsInteracting:blurPreviousNode:changingActivityState:userObject:") + // For iOS 13.0, * + let sel_13_0: Selector = sel_getUid("_elementDidFocus:userIsInteracting:blurPreviousNode:activityStateChanges:userObject:") + + if let method = class_getInstanceMethod(WKContentView, sel_10) { + let originalImp: IMP = method_getImplementation(method) + let original: OldClosureType = unsafeBitCast(originalImp, to: OldClosureType.self) + let block : @convention(block) (Any, UnsafeRawPointer, Bool, Bool, Any?) -> Void = { (me, arg0, arg1, arg2, arg3) in + original(me, sel_10, arg0, !value, arg2, arg3) + } + let imp: IMP = imp_implementationWithBlock(block) + method_setImplementation(method, imp) + } + + if let method = class_getInstanceMethod(WKContentView, sel_11_3) { + let originalImp: IMP = method_getImplementation(method) + let original: NewClosureType = unsafeBitCast(originalImp, to: NewClosureType.self) + let block : @convention(block) (Any, UnsafeRawPointer, Bool, Bool, Bool, Any?) -> Void = { (me, arg0, arg1, arg2, arg3, arg4) in + original(me, sel_11_3, arg0, !value, arg2, arg3, arg4) + } + let imp: IMP = imp_implementationWithBlock(block) + method_setImplementation(method, imp) + } + + if let method = class_getInstanceMethod(WKContentView, sel_12_2) { + let originalImp: IMP = method_getImplementation(method) + let original: NewClosureType = unsafeBitCast(originalImp, to: NewClosureType.self) + let block : @convention(block) (Any, UnsafeRawPointer, Bool, Bool, Bool, Any?) -> Void = { (me, arg0, arg1, arg2, arg3, arg4) in + original(me, sel_12_2, arg0, !value, arg2, arg3, arg4) + } + let imp: IMP = imp_implementationWithBlock(block) + method_setImplementation(method, imp) + } + + if let method = class_getInstanceMethod(WKContentView, sel_13_0) { + let originalImp: IMP = method_getImplementation(method) + let original: NewClosureType = unsafeBitCast(originalImp, to: NewClosureType.self) + let block : @convention(block) (Any, UnsafeRawPointer, Bool, Bool, Bool, Any?) -> Void = { (me, arg0, arg1, arg2, arg3, arg4) in + original(me, sel_13_0, arg0, !value, arg2, arg3, arg4) + } + let imp: IMP = imp_implementationWithBlock(block) + method_setImplementation(method, imp) + } + } +} + public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavigationDelegate, WKScriptMessageHandler, UIGestureRecognizerDelegate, WKDownloadDelegate, @@ -78,6 +147,7 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, } self.contextMenu = contextMenu self.initialUserScripts = userScripts + self.keyboardDisplayRequiresUserAction = false uiDelegate = self navigationDelegate = self scrollView.delegate = self @@ -1346,11 +1416,9 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, } } - public override func evaluateJavaScript(_ javaScriptString: String, completionHandler: ((Any?, Error?) -> Void)? = nil) { + public override func evaluateJavaScript(_ javaScriptString: String, completionHandler: (@MainActor @Sendable (Any?, (any Error)?) -> Void)? = nil) { if let applePayAPIEnabled = settings?.applePayAPIEnabled, applePayAPIEnabled { - if let completionHandler = completionHandler { - completionHandler(nil, nil) - } + completionHandler?(nil, nil) return } super.evaluateJavaScript(javaScriptString, completionHandler: completionHandler) diff --git a/ios/flutter_inappwebview.podspec b/ios/flutter_inappwebview.podspec index 4e97e77b9..8c2191296 100755 --- a/ios/flutter_inappwebview.podspec +++ b/ios/flutter_inappwebview.podspec @@ -22,6 +22,8 @@ A new Flutter plugin. s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES', 'VALID_ARCHS[sdk=iphonesimulator*]' => 'x86_64' } s.swift_version = '5.0' + s.platforms = { :ios => '11.0' } + s.dependency 'OrderedSet', '~>5.0' s.default_subspec = 'Core' diff --git a/lib/src/in_app_webview/apple/in_app_webview_options.dart b/lib/src/in_app_webview/apple/in_app_webview_options.dart index 63a1caf71..74c9a0cfd 100755 --- a/lib/src/in_app_webview/apple/in_app_webview_options.dart +++ b/lib/src/in_app_webview/apple/in_app_webview_options.dart @@ -181,6 +181,7 @@ class IOSInAppWebViewOptions ///- [InAppWebViewController.pauseTimers] ///- [InAppWebViewController.getSelectedText] ///- [InAppWebViewController.getHitTestResult] + ///- [InAppWebViewController.requestFocus] ///- [InAppWebViewController.requestFocusNodeHref] ///- [InAppWebViewController.requestImageRef] ///- [InAppWebViewController.postWebMessage] diff --git a/lib/src/in_app_webview/in_app_webview_controller.dart b/lib/src/in_app_webview/in_app_webview_controller.dart index 23a0e2133..831d9b56d 100644 --- a/lib/src/in_app_webview/in_app_webview_controller.dart +++ b/lib/src/in_app_webview/in_app_webview_controller.dart @@ -2606,6 +2606,14 @@ class InAppWebViewController { _inAppBrowser?.contextMenu = contextMenu; } + /// Only support android. + /// + ///- Android issue: https://www.pudn.com/news/6228d8129ddf223e1ad224f8.html + Future requestFocus() async { + Map args = {}; + return await _channel.invokeMethod('requestFocus', args); + } + ///Requests the anchor or image element URL at the last tapped point. /// ///**NOTE**: On iOS, it is implemented using JavaScript. diff --git a/lib/src/in_app_webview/in_app_webview_settings.dart b/lib/src/in_app_webview/in_app_webview_settings.dart index 91fabfa92..948c3d53e 100755 --- a/lib/src/in_app_webview/in_app_webview_settings.dart +++ b/lib/src/in_app_webview/in_app_webview_settings.dart @@ -1099,6 +1099,7 @@ class InAppWebViewSettings_ { ///- [InAppWebViewController.pauseTimers] ///- [InAppWebViewController.getSelectedText] ///- [InAppWebViewController.getHitTestResult] + ///- [InAppWebViewController.requestFocus] ///- [InAppWebViewController.requestFocusNodeHref] ///- [InAppWebViewController.requestImageRef] ///- [InAppWebViewController.postWebMessage] diff --git a/lib/src/in_app_webview/in_app_webview_settings.g.dart b/lib/src/in_app_webview/in_app_webview_settings.g.dart index 5db228652..fa8f8b575 100644 --- a/lib/src/in_app_webview/in_app_webview_settings.g.dart +++ b/lib/src/in_app_webview/in_app_webview_settings.g.dart @@ -1054,6 +1054,7 @@ class InAppWebViewSettings { ///- [InAppWebViewController.pauseTimers] ///- [InAppWebViewController.getSelectedText] ///- [InAppWebViewController.getHitTestResult] + ///- [InAppWebViewController.requestFocus] ///- [InAppWebViewController.requestFocusNodeHref] ///- [InAppWebViewController.requestImageRef] ///- [InAppWebViewController.postWebMessage] diff --git a/macos/Classes/InAppWebView/InAppWebView.swift b/macos/Classes/InAppWebView/InAppWebView.swift index 5c8bf04f1..6ad40efef 100755 --- a/macos/Classes/InAppWebView/InAppWebView.swift +++ b/macos/Classes/InAppWebView/InAppWebView.swift @@ -841,11 +841,9 @@ public class InAppWebView: WKWebView, WKUIDelegate, } } - public override func evaluateJavaScript(_ javaScriptString: String, completionHandler: ((Any?, Error?) -> Void)? = nil) { + public override func evaluateJavaScript(_ javaScriptString: String, completionHandler: (@MainActor @Sendable (Any?, (any Error)?) -> Void)? = nil) { if let applePayAPIEnabled = settings?.applePayAPIEnabled, applePayAPIEnabled { - if let completionHandler = completionHandler { - completionHandler(nil, nil) - } + completionHandler?(nil, nil) return } super.evaluateJavaScript(javaScriptString, completionHandler: completionHandler)