implemented WKDownloadDelegate iOS protocol for iOS 14.5

This commit is contained in:
Lorenzo Pichilli 2022-04-23 17:39:39 +02:00
parent 9e6feccbf6
commit 8315b811a8
4 changed files with 121 additions and 38 deletions

View File

@ -25,6 +25,7 @@ public class NavigationAction {
navigationActionMap.put("navigationType", null); navigationActionMap.put("navigationType", null);
navigationActionMap.put("sourceFrame", null); navigationActionMap.put("sourceFrame", null);
navigationActionMap.put("targetFrame", null); navigationActionMap.put("targetFrame", null);
navigationActionMap.put("shouldPerformDownload", null);
return navigationActionMap; return navigationActionMap;
} }

View File

@ -9,7 +9,10 @@ import Flutter
import Foundation import Foundation
import WebKit import WebKit
public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavigationDelegate, WKScriptMessageHandler, UIGestureRecognizerDelegate, PullToRefreshDelegate { public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate,
WKNavigationDelegate, WKScriptMessageHandler, UIGestureRecognizerDelegate,
WKDownloadDelegate,
PullToRefreshDelegate {
var windowId: Int64? var windowId: Int64?
var windowCreated = false var windowCreated = false
@ -1517,11 +1520,14 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavi
var action = response["action"] as? Int var action = response["action"] as? Int
action = action != nil ? action : 0; action = action != nil ? action : 0;
switch action { switch action {
case 1: case 1:
decisionHandler(.grant) decisionHandler(.grant)
break break
default: case 2:
decisionHandler(.deny) decisionHandler(.prompt)
break
default:
decisionHandler(.deny)
} }
return; return;
} }
@ -1555,11 +1561,14 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavi
var action = response["action"] as? Int var action = response["action"] as? Int
action = action != nil ? action : 0; action = action != nil ? action : 0;
switch action { switch action {
case 1: case 1:
decisionHandler(.grant) decisionHandler(.grant)
break break
default: case 2:
decisionHandler(.deny) decisionHandler(.prompt)
break
default:
decisionHandler(.deny)
} }
return; return;
} }
@ -1578,6 +1587,39 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavi
}) })
} }
@available(iOS 14.5, *)
public func download(_ download: WKDownload, decideDestinationUsing response: URLResponse, suggestedFilename: String, completionHandler: @escaping (URL?) -> Void) {
if let url = response.url, let useOnDownloadStart = settings?.useOnDownloadStart, useOnDownloadStart {
let downloadStartRequest = DownloadStartRequest(url: url.absoluteString,
userAgent: nil,
contentDisposition: nil,
mimeType: response.mimeType,
contentLength: response.expectedContentLength,
suggestedFilename: suggestedFilename,
textEncodingName: response.textEncodingName)
onDownloadStartRequest(request: downloadStartRequest)
}
download.delegate = nil
// cancel the download
completionHandler(nil)
}
@available(iOS 14.5, *)
public func webView(_ webView: WKWebView, navigationResponse: WKNavigationResponse, didBecome download: WKDownload) {
let response = navigationResponse.response
if let url = response.url, let useOnDownloadStart = settings?.useOnDownloadStart, useOnDownloadStart {
let downloadStartRequest = DownloadStartRequest(url: url.absoluteString,
userAgent: nil,
contentDisposition: nil,
mimeType: response.mimeType,
contentLength: response.expectedContentLength,
suggestedFilename: response.suggestedFilename,
textEncodingName: response.textEncodingName)
onDownloadStartRequest(request: downloadStartRequest)
}
download.delegate = nil
}
public func webView(_ webView: WKWebView, public func webView(_ webView: WKWebView,
decidePolicyFor navigationAction: WKNavigationAction, decidePolicyFor navigationAction: WKNavigationAction,
decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) { decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) {
@ -1587,6 +1629,11 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavi
return return
} }
if #available(iOS 14.5, *), navigationAction.request.url!.absoluteString.hasSuffix(".dat") {
decisionHandler(.download)
return
}
if navigationAction.request.url != nil { if navigationAction.request.url != nil {
if let useShouldOverrideUrlLoading = settings?.useShouldOverrideUrlLoading, useShouldOverrideUrlLoading { if let useShouldOverrideUrlLoading = settings?.useShouldOverrideUrlLoading, useShouldOverrideUrlLoading {
@ -1605,7 +1652,8 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavi
if let r = result { if let r = result {
response = r as! [String: Any] response = r as! [String: Any]
let action = response["action"] as? Int let action = response["action"] as? Int
let navigationActionPolicy = WKNavigationActionPolicy.init(rawValue: action ?? WKNavigationActionPolicy.cancel.rawValue) ?? let navigationActionPolicy = WKNavigationActionPolicy
.init(rawValue: action ?? WKNavigationActionPolicy.cancel.rawValue) ??
WKNavigationActionPolicy.cancel WKNavigationActionPolicy.cancel
decisionHandler(navigationActionPolicy) decisionHandler(navigationActionPolicy)
return; return;
@ -1647,15 +1695,11 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavi
var response: [String: Any] var response: [String: Any]
if let r = result { if let r = result {
response = r as! [String: Any] response = r as! [String: Any]
var action = response["action"] as? Int let action = response["action"] as? Int
action = action != nil ? action : 0; let navigationActionPolicy = WKNavigationResponsePolicy
switch action { .init(rawValue: action ?? WKNavigationResponsePolicy.cancel.rawValue) ??
case 1: WKNavigationResponsePolicy.cancel
decisionHandler(.allow) decisionHandler(navigationActionPolicy)
break
default:
decisionHandler(.cancel)
}
return; return;
} }
decisionHandler(.allow) decisionHandler(.allow)
@ -1664,21 +1708,26 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavi
} }
if let useOnDownloadStart = settings?.useOnDownloadStart, useOnDownloadStart { if let useOnDownloadStart = settings?.useOnDownloadStart, useOnDownloadStart {
let mimeType = navigationResponse.response.mimeType if #available(iOS 14.5, *), !navigationResponse.canShowMIMEType {
if let url = navigationResponse.response.url, navigationResponse.isForMainFrame { decisionHandler(.download)
if url.scheme != "file", mimeType != nil, !mimeType!.starts(with: "text/") { return
let downloadStartRequest = DownloadStartRequest(url: url.absoluteString, } else {
userAgent: nil, let mimeType = navigationResponse.response.mimeType
contentDisposition: nil, if let url = navigationResponse.response.url, navigationResponse.isForMainFrame {
mimeType: mimeType, if url.scheme != "file", mimeType != nil, !mimeType!.starts(with: "text/") {
contentLength: navigationResponse.response.expectedContentLength, let downloadStartRequest = DownloadStartRequest(url: url.absoluteString,
suggestedFilename: navigationResponse.response.suggestedFilename, userAgent: nil,
textEncodingName: navigationResponse.response.textEncodingName) contentDisposition: nil,
onDownloadStartRequest(request: downloadStartRequest) mimeType: mimeType,
if useOnNavigationResponse == nil || !useOnNavigationResponse! { contentLength: navigationResponse.response.expectedContentLength,
decisionHandler(.cancel) suggestedFilename: navigationResponse.response.suggestedFilename,
textEncodingName: navigationResponse.response.textEncodingName)
onDownloadStartRequest(request: downloadStartRequest)
if useOnNavigationResponse == nil || !useOnNavigationResponse! {
decisionHandler(.cancel)
}
return
} }
return
} }
} }
} }

View File

@ -10,6 +10,10 @@ import WebKit
extension WKNavigationAction { extension WKNavigationAction {
public func toMap () -> [String:Any?] { public func toMap () -> [String:Any?] {
var shouldPerformDownload: Bool? = nil
if #available(iOS 14.5, *) {
shouldPerformDownload = self.shouldPerformDownload
}
return [ return [
"request": request.toMap(), "request": request.toMap(),
"isForMainFrame": targetFrame?.isMainFrame ?? false, "isForMainFrame": targetFrame?.isMainFrame ?? false,
@ -17,7 +21,8 @@ extension WKNavigationAction {
"isRedirect": nil, "isRedirect": nil,
"navigationType": navigationType.rawValue, "navigationType": navigationType.rawValue,
"sourceFrame": sourceFrame.toMap(), "sourceFrame": sourceFrame.toMap(),
"targetFrame": targetFrame?.toMap() "targetFrame": targetFrame?.toMap(),
"shouldPerformDownload": shouldPerformDownload
] ]
} }
} }

View File

@ -4717,6 +4717,11 @@ class PermissionResponseAction {
///Grants origin the permission to access the given resources. ///Grants origin the permission to access the given resources.
static const GRANT = const PermissionResponseAction._internal(1); static const GRANT = const PermissionResponseAction._internal(1);
///Prompt the user for permission for the requested resource.
///
///**NOTE**: available only on iOS 15.0+. It will fallback to [DENY].
static const PROMPT = const PermissionResponseAction._internal(2);
bool operator ==(value) => value == _value; bool operator ==(value) => value == _value;
@override @override
@ -4875,6 +4880,9 @@ class PermissionRequest {
Uri origin; Uri origin;
///List of resources the web content wants to access. ///List of resources the web content wants to access.
///
///**NOTE for iOS**: this list will have only 1 element and will be used by the [PermissionResponse.action]
///as the resource to consider when applying the corresponding action.
List<PermissionResourceType> resources; List<PermissionResourceType> resources;
///The frame that initiates the request in the web view. ///The frame that initiates the request in the web view.
@ -4925,6 +4933,8 @@ class PermissionRequest {
///Class that represents the response used by the [WebView.onPermissionRequest] event. ///Class that represents the response used by the [WebView.onPermissionRequest] event.
class PermissionResponse { class PermissionResponse {
///Resources granted to be accessed by origin. ///Resources granted to be accessed by origin.
///
///**NOTE for iOS**: not used. The [action] taken is based on the [PermissionRequest.resources].
List<PermissionResourceType> resources; List<PermissionResourceType> resources;
///Indicate the [PermissionResponseAction] to take in response of a permission request. ///Indicate the [PermissionResponseAction] to take in response of a permission request.
@ -4993,6 +5003,11 @@ class NavigationActionPolicy {
///Allow the navigation to continue. ///Allow the navigation to continue.
static const ALLOW = const NavigationActionPolicy._internal(1); static const ALLOW = const NavigationActionPolicy._internal(1);
///Turn the navigation into a download.
///
///**NOTE**: available only on iOS 14.5+. It will fallback to [CANCEL].
static const DOWNLOAD = const NavigationActionPolicy._internal(2);
bool operator ==(value) => value == _value; bool operator ==(value) => value == _value;
@override @override
@ -5761,6 +5776,11 @@ class NavigationAction {
///**NOTE**: available only on iOS. ///**NOTE**: available only on iOS.
FrameInfo? targetFrame; FrameInfo? targetFrame;
///A value indicating whether the web content used a download attribute to indicate that this should be downloaded.
///
///**NOTE**: available only on iOS.
bool? shouldPerformDownload;
NavigationAction( NavigationAction(
{required this.request, {required this.request,
required this.isForMainFrame, required this.isForMainFrame,
@ -5773,7 +5793,8 @@ class NavigationAction {
@Deprecated("Use sourceFrame instead") this.iosSourceFrame, @Deprecated("Use sourceFrame instead") this.iosSourceFrame,
this.sourceFrame, this.sourceFrame,
@Deprecated("Use targetFrame instead") this.iosTargetFrame, @Deprecated("Use targetFrame instead") this.iosTargetFrame,
this.targetFrame}) { this.targetFrame,
this.shouldPerformDownload}) {
// ignore: deprecated_member_use_from_same_package // ignore: deprecated_member_use_from_same_package
this.hasGesture = this.hasGesture ?? this.androidHasGesture; this.hasGesture = this.hasGesture ?? this.androidHasGesture;
// ignore: deprecated_member_use_from_same_package // ignore: deprecated_member_use_from_same_package
@ -5818,7 +5839,8 @@ class NavigationAction {
// ignore: deprecated_member_use_from_same_package // ignore: deprecated_member_use_from_same_package
IOSWKFrameInfo.fromMap(map["targetFrame"]?.cast<String, dynamic>()), IOSWKFrameInfo.fromMap(map["targetFrame"]?.cast<String, dynamic>()),
targetFrame: targetFrame:
FrameInfo.fromMap(map["targetFrame"]?.cast<String, dynamic>())); FrameInfo.fromMap(map["targetFrame"]?.cast<String, dynamic>()),
shouldPerformDownload: map["shouldPerformDownload"]);
} }
Map<String, dynamic> toMap() { Map<String, dynamic> toMap() {
@ -5847,6 +5869,7 @@ class NavigationAction {
"iosTargetFrame": targetFrame?.toMap() ?? iosTargetFrame?.toMap(), "iosTargetFrame": targetFrame?.toMap() ?? iosTargetFrame?.toMap(),
// ignore: deprecated_member_use_from_same_package // ignore: deprecated_member_use_from_same_package
"targetFrame": targetFrame?.toMap() ?? iosTargetFrame?.toMap(), "targetFrame": targetFrame?.toMap() ?? iosTargetFrame?.toMap(),
"shouldPerformDownload": shouldPerformDownload
}; };
} }
@ -8953,6 +8976,11 @@ class NavigationResponseAction {
///Allow the navigation to continue. ///Allow the navigation to continue.
static const ALLOW = const NavigationResponseAction._internal(1); static const ALLOW = const NavigationResponseAction._internal(1);
///Turn the navigation into a download.
///
///**NOTE**: available only on iOS 14.5+. It will fallback to [CANCEL].
static const DOWNLOAD = const NavigationResponseAction._internal(2);
bool operator ==(value) => value == _value; bool operator ==(value) => value == _value;
@override @override