From 4c650b8d10d01bb805e41ccddc96e11c89f52bd7 Mon Sep 17 00:00:00 2001 From: lanistor Date: Wed, 30 Nov 2022 14:55:36 +0800 Subject: [PATCH 1/6] Feat: Support `keyboardDisplayRequiresUserAction` to focus automaticlly --- ios/Classes/InAppWebView/InAppWebView.swift | 70 +++++++++++++++++++++ 1 file changed, 70 insertions(+) diff --git a/ios/Classes/InAppWebView/InAppWebView.swift b/ios/Classes/InAppWebView/InAppWebView.swift index 1506aab5b..5bf5d0822 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 From 3545dd5226d6ad490f73a176cbdd3c7bf20b5425 Mon Sep 17 00:00:00 2001 From: lanistor Date: Wed, 30 Nov 2022 17:09:15 +0800 Subject: [PATCH 2/6] Feat: Android suuport requestFocus function --- .../webview/InAppWebViewInterface.java | 1 + .../webview/WebViewChannelDelegate.java | 7 +++++++ .../webview/WebViewChannelDelegateMethods.java | 1 + .../webview/in_app_webview/InAppWebView.java | 10 ++++++++++ .../in_app_webview/apple/in_app_webview_options.dart | 1 + lib/src/in_app_webview/in_app_webview_controller.dart | 8 ++++++++ lib/src/in_app_webview/in_app_webview_settings.dart | 1 + lib/src/in_app_webview/in_app_webview_settings.g.dart | 1 + 8 files changed, 30 insertions(+) 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..5a41bec3a 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,13 @@ public void onReceiveValue(String value) { } result.success(true); break; + case requestFocus: + if (webView != null) { + webView.requestFocusView(); + } else { + 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..ea14edf14 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,16 @@ 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_FORCED, + InputMethodManager.HIDE_IMPLICIT_ONLY); + } + } + public Map requestFocusNodeHref() { Message msg = InAppWebView.mHandler.obtainMessage(); requestFocusNodeHref(msg); 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] From 7d4f296827d1069636201b2f24c6f17d42723575 Mon Sep 17 00:00:00 2001 From: lanistor Date: Fri, 2 Dec 2022 17:00:27 +0800 Subject: [PATCH 3/6] Fix: Android clearFocus hide keyboard --- .../webview/in_app_webview/InAppWebView.java | 3 +-- .../webview/in_app_webview/InputAwareWebView.java | 5 +++++ 2 files changed, 6 insertions(+), 2 deletions(-) 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 ea14edf14..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 @@ -1724,8 +1724,7 @@ public void requestFocusView() { InputMethodManager inputManager = (InputMethodManager) this.getContext().getSystemService(Context.INPUT_METHOD_SERVICE); if (inputManager != null) { - inputManager.toggleSoftInput(InputMethodManager.SHOW_FORCED, - InputMethodManager.HIDE_IMPLICIT_ONLY); + inputManager.toggleSoftInput(InputMethodManager.SHOW_IMPLICIT, InputMethodManager.HIDE_NOT_ALWAYS); } } 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; } From a474c249828850a91d74889d28ca17ac038bd5b3 Mon Sep 17 00:00:00 2001 From: lanistor Date: Fri, 16 Dec 2022 22:49:26 +0800 Subject: [PATCH 4/6] Fix: Adjust for requestFocus --- .../flutter_inappwebview/webview/WebViewChannelDelegate.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) 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 5a41bec3a..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 @@ -486,9 +486,8 @@ public void onReceiveValue(String value) { case requestFocus: if (webView != null) { webView.requestFocusView(); - } else { - result.success(true); } + result.success(true); break; case requestFocusNodeHref: if (webView != null) { From c6ce2d360e9eb280a5cafbfc5540e469b4b85a33 Mon Sep 17 00:00:00 2001 From: lanistzhang Date: Thu, 28 Sep 2023 15:25:08 +0800 Subject: [PATCH 5/6] Fix: iOS17 Bug --- ios/flutter_inappwebview.podspec | 2 ++ 1 file changed, 2 insertions(+) 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' From e5e1c78836fa248316a00a333146b8a14d420cf5 Mon Sep 17 00:00:00 2001 From: lanistor Date: Sat, 28 Sep 2024 22:40:39 +0800 Subject: [PATCH 6/6] fix: support xcode16+ios18 --- ios/Classes/InAppWebView/InAppWebView.swift | 6 ++---- macos/Classes/InAppWebView/InAppWebView.swift | 6 ++---- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/ios/Classes/InAppWebView/InAppWebView.swift b/ios/Classes/InAppWebView/InAppWebView.swift index 5bf5d0822..d18b465a7 100755 --- a/ios/Classes/InAppWebView/InAppWebView.swift +++ b/ios/Classes/InAppWebView/InAppWebView.swift @@ -1416,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/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)