From 51d1454d45e98f9ce1bff115aab4f808bdeb6fe1 Mon Sep 17 00:00:00 2001 From: Pham Cong Anh Date: Wed, 6 Apr 2022 17:39:11 +0700 Subject: [PATCH 1/2] add feature decideMediaCapturePermissions on ios15+ --- ios/Classes/InAppWebView/InAppWebView.swift | 56 +++++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/ios/Classes/InAppWebView/InAppWebView.swift b/ios/Classes/InAppWebView/InAppWebView.swift index f49fcef7..a5744e8d 100755 --- a/ios/Classes/InAppWebView/InAppWebView.swift +++ b/ios/Classes/InAppWebView/InAppWebView.swift @@ -1442,6 +1442,43 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavi return result; } + + @available(iOS 15.0, *) + @available(macOS 12.0, *) + @available(macCatalyst 15.0, *) + public func webView(_ webView: WKWebView, + requestMediaCapturePermissionFor origin: WKSecurityOrigin, + initiatedByFrame frame: WKFrameInfo, + type: WKMediaCaptureType, + decisionHandler: @escaping (WKPermissionDecision) -> Void) { + onPermissionRequest(origin: origin, type: type, result: {(result) -> Void in + if result is FlutterError { + print((result as! FlutterError).message ?? "") + decisionHandler(.deny) + } + else if (result as? NSObject) == FlutterMethodNotImplemented { + decisionHandler(.deny) + } + else { + var response: [String: Any] + if let r = result { + response = r as! [String: Any] + var action = response["action"] as? Int + action = action != nil ? action : 0; +// var resources = response["resources"] as? [String] + switch action { + case 1: + decisionHandler(.grant) + break + default: + decisionHandler(.deny) + } + return; + } + decisionHandler(.deny) + } + }) + } @available(iOS 13.0, *) public func webView(_ webView: WKWebView, @@ -2374,6 +2411,25 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavi channel?.invokeMethod("onNavigationResponse", arguments: navigationResponse.toMap(), result: result) } + @available(iOS 15.0, *) + @available(macOS 12.0, *) + @available(macCatalyst 15.0, *) + public func onPermissionRequest(origin: WKSecurityOrigin, type: WKMediaCaptureType, result: FlutterResult?) { + let resources: [String]; + switch type { + case .camera: + resources = ["camera"] + break; + case .microphone: + resources = ["microphone"] + break; + default: + resources = ["cameraAndMicrophone"] + } + let arguments: [String: Any] = ["origin": origin.protocol + "://" + origin.host, "resources": resources] + channel?.invokeMethod("onPermissionRequest", arguments: arguments, result: result) + } + public func onReceivedHttpAuthRequest(challenge: URLAuthenticationChallenge, result: FlutterResult?) { channel?.invokeMethod("onReceivedHttpAuthRequest", arguments: HttpAuthenticationChallenge(fromChallenge: challenge).toMap(), result: result) From f265063a3763d95e9e474941194e477cb0c82142 Mon Sep 17 00:00:00 2001 From: Lorenzo Pichilli Date: Thu, 21 Apr 2022 13:33:07 +0200 Subject: [PATCH 2/2] Added support for onPermissionRequest event on iOS 15.0+ --- CHANGELOG.md | 1 + example/lib/in_app_webiew_example.screen.dart | 8 +- ios/Classes/InAppWebView/InAppWebView.swift | 130 +++++++++++------- .../InAppWebView/InAppWebViewSettings.swift | 4 +- ios/Classes/Types/PermissionRequest.swift | 29 ++++ ios/Classes/Types/StringOrInt.swift | 13 ++ lib/src/types.dart | 16 ++- 7 files changed, 142 insertions(+), 59 deletions(-) create mode 100644 ios/Classes/Types/PermissionRequest.swift create mode 100644 ios/Classes/Types/StringOrInt.swift diff --git a/CHANGELOG.md b/CHANGELOG.md index 457fbd66..c28ba671 100755 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ - Deprecated old classes/properties/methods to make them eventually compatible with other operating systems and WebView engines. - Added `pauseAllMediaPlayback`, `setAllMediaPlaybackSuspended`, `closeAllMediaPresentations`, `requestMediaPlaybackState` WebView controller methods - Added `underPageBackgroundColor`, `isTextInteractionEnabled`, `isSiteSpecificQuirksModeEnabled`, `upgradeKnownHostsToHTTPS` WebView settings +- Added support for `onPermissionRequest` event on iOS 15.0+ - Updated `getMetaThemeColor` on iOS 15.0+ ## 5.4.0+2 diff --git a/example/lib/in_app_webiew_example.screen.dart b/example/lib/in_app_webiew_example.screen.dart index 8293ff2c..f6b6a4d6 100755 --- a/example/lib/in_app_webiew_example.screen.dart +++ b/example/lib/in_app_webiew_example.screen.dart @@ -132,10 +132,10 @@ class _InAppWebViewExampleScreenState extends State { urlController.text = this.url; }); }, - onPermissionRequest: (controller, origin, resources) async { - return PermissionRequestResponse( - resources: resources, - action: PermissionRequestResponseAction.GRANT); + onPermissionRequest: (controller, request) async { + return PermissionResponse( + resources: request.resources, + action: PermissionResponseAction.GRANT); }, shouldOverrideUrlLoading: (controller, navigationAction) async { var uri = navigationAction.request.url!; diff --git a/ios/Classes/InAppWebView/InAppWebView.swift b/ios/Classes/InAppWebView/InAppWebView.swift index 31558130..df2b5d8e 100755 --- a/ios/Classes/InAppWebView/InAppWebView.swift +++ b/ios/Classes/InAppWebView/InAppWebView.swift @@ -433,7 +433,9 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavi } if #available(iOS 15.0, *) { - configuration.preferences.isSiteSpecificQuirksModeEnabled = settings.isSiteSpecificQuirksModeEnabled + if (configuration.preferences.responds(to: #selector(getter: InAppWebViewSettings.isSiteSpecificQuirksModeEnabled))) { + configuration.preferences.isSiteSpecificQuirksModeEnabled = settings.isSiteSpecificQuirksModeEnabled + } } } } @@ -1130,7 +1132,8 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavi let underPageBackgroundColor = newSettings.underPageBackgroundColor, !underPageBackgroundColor.isEmpty { self.underPageBackgroundColor = UIColor(hexString: underPageBackgroundColor) } - if newSettingsMap["isSiteSpecificQuirksModeEnabled"] != nil && + if configuration.preferences.responds(to: #selector(getter: InAppWebViewSettings.isSiteSpecificQuirksModeEnabled)), + newSettingsMap["isSiteSpecificQuirksModeEnabled"] != nil && settings?.isSiteSpecificQuirksModeEnabled != newSettings.isSiteSpecificQuirksModeEnabled { configuration.preferences.isSiteSpecificQuirksModeEnabled = newSettings.isSiteSpecificQuirksModeEnabled } @@ -1489,38 +1492,78 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavi @available(macOS 12.0, *) @available(macCatalyst 15.0, *) public func webView(_ webView: WKWebView, - requestMediaCapturePermissionFor origin: WKSecurityOrigin, - initiatedByFrame frame: WKFrameInfo, - type: WKMediaCaptureType, - decisionHandler: @escaping (WKPermissionDecision) -> Void) { - onPermissionRequest(origin: origin, type: type, result: {(result) -> Void in - if result is FlutterError { - print((result as! FlutterError).message ?? "") - decisionHandler(.deny) - } - else if (result as? NSObject) == FlutterMethodNotImplemented { - decisionHandler(.deny) - } - else { - var response: [String: Any] - if let r = result { - response = r as! [String: Any] - var action = response["action"] as? Int - action = action != nil ? action : 0; -// var resources = response["resources"] as? [String] - switch action { - case 1: - decisionHandler(.grant) - break - default: - decisionHandler(.deny) - } - return; - } - decisionHandler(.deny) - } - }) - } + requestMediaCapturePermissionFor origin: WKSecurityOrigin, + initiatedByFrame frame: WKFrameInfo, + type: WKMediaCaptureType, + decisionHandler: @escaping (WKPermissionDecision) -> Void) { + let origin = "\(origin.protocol)://\(origin.host)\(origin.port != 0 ? ":" + String(origin.port) : "")" + let permissionRequest = PermissionRequest(origin: origin, resources: [type.rawValue], frame: frame) + + onPermissionRequest(request: permissionRequest, result: {(result) -> Void in + if result is FlutterError { + print((result as! FlutterError).message ?? "") + decisionHandler(.deny) + } + else if (result as? NSObject) == FlutterMethodNotImplemented { + decisionHandler(.deny) + } + else { + var response: [String: Any] + if let r = result { + response = r as! [String: Any] + var action = response["action"] as? Int + action = action != nil ? action : 0; + switch action { + case 1: + decisionHandler(.grant) + break + default: + decisionHandler(.deny) + } + return; + } + decisionHandler(.deny) + } + }) + } + + @available(iOS 15.0, *) + @available(macOS 12.0, *) + @available(macCatalyst 15.0, *) + public func webView(_ webView: WKWebView, + requestDeviceOrientationAndMotionPermissionFor origin: WKSecurityOrigin, + initiatedByFrame frame: WKFrameInfo, + decisionHandler: @escaping (WKPermissionDecision) -> Void) { + let origin = "\(origin.protocol)://\(origin.host)\(origin.port != 0 ? ":" + String(origin.port) : "")" + let permissionRequest = PermissionRequest(origin: origin, resources: ["deviceOrientationAndMotion"], frame: frame) + + onPermissionRequest(request: permissionRequest, result: {(result) -> Void in + if result is FlutterError { + print((result as! FlutterError).message ?? "") + decisionHandler(.deny) + } + else if (result as? NSObject) == FlutterMethodNotImplemented { + decisionHandler(.deny) + } + else { + var response: [String: Any] + if let r = result { + response = r as! [String: Any] + var action = response["action"] as? Int + action = action != nil ? action : 0; + switch action { + case 1: + decisionHandler(.grant) + break + default: + decisionHandler(.deny) + } + return; + } + decisionHandler(.deny) + } + }) + } @available(iOS 13.0, *) public func webView(_ webView: WKWebView, @@ -2463,23 +2506,8 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavi channel?.invokeMethod("onNavigationResponse", arguments: navigationResponse.toMap(), result: result) } - @available(iOS 15.0, *) - @available(macOS 12.0, *) - @available(macCatalyst 15.0, *) - public func onPermissionRequest(origin: WKSecurityOrigin, type: WKMediaCaptureType, result: FlutterResult?) { - let resources: [String]; - switch type { - case .camera: - resources = ["camera"] - break; - case .microphone: - resources = ["microphone"] - break; - default: - resources = ["cameraAndMicrophone"] - } - let arguments: [String: Any] = ["origin": origin.protocol + "://" + origin.host, "resources": resources] - channel?.invokeMethod("onPermissionRequest", arguments: arguments, result: result) + public func onPermissionRequest(request: PermissionRequest, result: FlutterResult?) { + channel?.invokeMethod("onPermissionRequest", arguments: request.toMap(), result: result) } public func onReceivedHttpAuthRequest(challenge: URLAuthenticationChallenge, result: FlutterResult?) { diff --git a/ios/Classes/InAppWebView/InAppWebViewSettings.swift b/ios/Classes/InAppWebView/InAppWebViewSettings.swift index d567f984..c51b1c99 100755 --- a/ios/Classes/InAppWebView/InAppWebViewSettings.swift +++ b/ios/Classes/InAppWebView/InAppWebViewSettings.swift @@ -143,7 +143,9 @@ public class InAppWebViewSettings: IWebViewSettings { } if #available(iOS 15.0, *) { realSettings["underPageBackgroundColor"] = webView.underPageBackgroundColor.hexString - realSettings["isSiteSpecificQuirksModeEnabled"] = configuration.preferences.isSiteSpecificQuirksModeEnabled + if configuration.preferences.responds(to: #selector(getter: self.isSiteSpecificQuirksModeEnabled)) { + realSettings["isSiteSpecificQuirksModeEnabled"] = configuration.preferences.isSiteSpecificQuirksModeEnabled + } } } return realSettings diff --git a/ios/Classes/Types/PermissionRequest.swift b/ios/Classes/Types/PermissionRequest.swift new file mode 100644 index 00000000..a286a3f3 --- /dev/null +++ b/ios/Classes/Types/PermissionRequest.swift @@ -0,0 +1,29 @@ +// +// PermissionRequest.swift +// flutter_inappwebview +// +// Created by Lorenzo Pichilli on 21/04/22. +// + +import Foundation +import WebKit + +public class PermissionRequest: NSObject { + var origin: String + var resources: [StringOrInt] + var frame: WKFrameInfo + + public init(origin: String, resources: [StringOrInt], frame: WKFrameInfo) { + self.origin = origin + self.resources = resources + self.frame = frame + } + + public func toMap () -> [String:Any?] { + return [ + "origin": origin, + "resources": resources, + "frame": frame.toMap() + ] + } +} diff --git a/ios/Classes/Types/StringOrInt.swift b/ios/Classes/Types/StringOrInt.swift new file mode 100644 index 00000000..f7cd40c7 --- /dev/null +++ b/ios/Classes/Types/StringOrInt.swift @@ -0,0 +1,13 @@ +// +// StringOrInt.swift +// flutter_inappwebview +// +// Created by Lorenzo Pichilli on 21/04/22. +// + +import Foundation + +public protocol StringOrInt { } + +extension Int: StringOrInt { } +extension String: StringOrInt { } diff --git a/lib/src/types.dart b/lib/src/types.dart index 51718c26..e9fa0c63 100755 --- a/lib/src/types.dart +++ b/lib/src/types.dart @@ -4756,6 +4756,10 @@ class PermissionResourceType { PermissionResourceType.RESOURCE_MIDI_SYSEX, PermissionResourceType.RESOURCE_PROTECTED_MEDIA_ID, PermissionResourceType.RESOURCE_VIDEO_CAPTURE, + PermissionResourceType.CAMERA, + PermissionResourceType.MICROPHONE, + PermissionResourceType.CAMERA_AND_MICROPHONE, + PermissionResourceType.DEVICE_ORIENTATION_AND_MOTION, ].toSet(); static final Set _androidValues = [ @@ -4766,7 +4770,10 @@ class PermissionResourceType { ].toSet(); static final Set _appleValues = [ - + PermissionResourceType.CAMERA, + PermissionResourceType.MICROPHONE, + PermissionResourceType.CAMERA_AND_MICROPHONE, + PermissionResourceType.DEVICE_ORIENTATION_AND_MOTION, ].toSet(); static PermissionResourceType? fromValue(dynamic? value) { @@ -4903,7 +4910,7 @@ class PermissionRequest { return { "origin": origin.toString(), "resources": resources.map((e) => e.toValue()).toList(), - "initiatedByFrame": frame?.toMap() + "frame": frame?.toMap() }; } @@ -4930,7 +4937,10 @@ class PermissionResponse { this.action = PermissionResponseAction.DENY}); Map toMap() { - return {"resources": resources, "action": action?.toValue()}; + return { + "resources": resources.map((e) => e.toValue()).toList(), + "action": action?.toValue() + }; } Map toJson() {