files
This commit is contained in:
115
ios/Classes/FilePlugin.swift
Normal file
115
ios/Classes/FilePlugin.swift
Normal file
@@ -0,0 +1,115 @@
|
||||
import Flutter
|
||||
import QuickLook
|
||||
import UIKit
|
||||
|
||||
public class FilePlugin: NSObject, NativePlugin {
|
||||
private var channel: FlutterMethodChannel?
|
||||
private var previewDataSource: FilePreviewDataSource?
|
||||
|
||||
public func register(with registrar: FlutterPluginRegistrar) {
|
||||
let c = FlutterMethodChannel(name: "ux/file", binaryMessenger: registrar.messenger())
|
||||
c.setMethodCallHandler { [weak self] call, result in
|
||||
self?.handle(call, result: result)
|
||||
}
|
||||
channel = c
|
||||
}
|
||||
|
||||
private func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
|
||||
switch call.method {
|
||||
case "share": handleShare(call, result: result)
|
||||
case "open": handleOpen(call, result: result)
|
||||
default: result(FlutterMethodNotImplemented)
|
||||
}
|
||||
}
|
||||
|
||||
private func handleShare(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
|
||||
guard let args = call.arguments as? [String: Any],
|
||||
let path = args["path"] as? String else {
|
||||
return result(FlutterError(code: "bad_args", message: "path is required", details: nil))
|
||||
}
|
||||
guard let topVC = UxWindow.topViewController else {
|
||||
return result(FlutterError(code: "no_view", message: "no top view controller", details: nil))
|
||||
}
|
||||
|
||||
let url = URL(fileURLWithPath: path)
|
||||
let title = args["title"] as? String
|
||||
|
||||
let items: [Any] = title.map { [FileActivityItemSource(url: url, title: $0)] } ?? [url]
|
||||
let vc = UIActivityViewController(activityItems: items, applicationActivities: nil)
|
||||
|
||||
if UIDevice.current.userInterfaceIdiom == .pad,
|
||||
let popover = vc.popoverPresentationController {
|
||||
popover.sourceView = topVC.view
|
||||
if let r = args["sourceRect"] as? [String: Any],
|
||||
let x = (r["x"] as? NSNumber)?.doubleValue,
|
||||
let y = (r["y"] as? NSNumber)?.doubleValue,
|
||||
let w = (r["w"] as? NSNumber)?.doubleValue,
|
||||
let h = (r["h"] as? NSNumber)?.doubleValue {
|
||||
popover.sourceRect = CGRect(x: x, y: y, width: w, height: h)
|
||||
} else {
|
||||
let b = topVC.view.bounds
|
||||
popover.sourceRect = CGRect(x: b.midX, y: b.midY, width: 1, height: 1)
|
||||
popover.permittedArrowDirections = []
|
||||
}
|
||||
}
|
||||
|
||||
topVC.present(vc, animated: true) {
|
||||
result(true)
|
||||
}
|
||||
}
|
||||
|
||||
private func handleOpen(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
|
||||
guard let args = call.arguments as? [String: Any],
|
||||
let path = args["path"] as? String else {
|
||||
return result(FlutterError(code: "bad_args", message: "path is required", details: nil))
|
||||
}
|
||||
guard let topVC = UxWindow.topViewController else {
|
||||
return result(FlutterError(code: "no_view", message: "no top view controller", details: nil))
|
||||
}
|
||||
|
||||
let url = URL(fileURLWithPath: path)
|
||||
let ds = FilePreviewDataSource(url: url)
|
||||
// QLPreviewController requires a strong-retained data source.
|
||||
previewDataSource = ds
|
||||
|
||||
let vc = QLPreviewController()
|
||||
vc.dataSource = ds
|
||||
|
||||
topVC.present(vc, animated: true) {
|
||||
result(true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private final class FilePreviewDataSource: NSObject, QLPreviewControllerDataSource {
|
||||
let url: URL
|
||||
init(url: URL) { self.url = url }
|
||||
|
||||
func numberOfPreviewItems(in controller: QLPreviewController) -> Int { 1 }
|
||||
|
||||
func previewController(_ controller: QLPreviewController, previewItemAt index: Int) -> QLPreviewItem {
|
||||
url as QLPreviewItem
|
||||
}
|
||||
}
|
||||
|
||||
private final class FileActivityItemSource: NSObject, UIActivityItemSource {
|
||||
let url: URL
|
||||
let title: String
|
||||
|
||||
init(url: URL, title: String) {
|
||||
self.url = url
|
||||
self.title = title
|
||||
}
|
||||
|
||||
func activityViewControllerPlaceholderItem(_ controller: UIActivityViewController) -> Any {
|
||||
url
|
||||
}
|
||||
|
||||
func activityViewController(_ controller: UIActivityViewController, itemForActivityType type: UIActivity.ActivityType?) -> Any? {
|
||||
url
|
||||
}
|
||||
|
||||
func activityViewController(_ controller: UIActivityViewController, subjectForActivityType type: UIActivity.ActivityType?) -> String {
|
||||
title
|
||||
}
|
||||
}
|
||||
@@ -267,22 +267,8 @@ public class KeyboardPlugin: NSObject, NativePlugin {
|
||||
}
|
||||
|
||||
private func setupPanGesture() {
|
||||
var targetView: UIView?
|
||||
|
||||
if let window = UIApplication.shared.delegate?.window ?? nil {
|
||||
targetView = window.rootViewController?.view ?? window
|
||||
} else {
|
||||
for scene in UIApplication.shared.connectedScenes {
|
||||
if let ws = scene as? UIWindowScene {
|
||||
for window in ws.windows where window.isKeyWindow {
|
||||
targetView = window.rootViewController?.view ?? window
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
guard let view = targetView else { return }
|
||||
guard let window = UxWindow.keyWindow else { return }
|
||||
let view: UIView = window.rootViewController?.view ?? window
|
||||
|
||||
let pan = UIPanGestureRecognizer(target: self, action: #selector(handlePan))
|
||||
pan.cancelsTouchesInView = false
|
||||
|
||||
@@ -1,5 +1,23 @@
|
||||
import Flutter
|
||||
import UIKit
|
||||
|
||||
public protocol NativePlugin {
|
||||
func register(with registrar: FlutterPluginRegistrar)
|
||||
}
|
||||
|
||||
public enum UxWindow {
|
||||
public static var keyWindow: UIWindow? {
|
||||
if let w = UIApplication.shared.delegate?.window ?? nil { return w }
|
||||
for scene in UIApplication.shared.connectedScenes {
|
||||
guard let ws = scene as? UIWindowScene else { continue }
|
||||
for window in ws.windows where window.isKeyWindow { return window }
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
public static var topViewController: UIViewController? {
|
||||
var vc = keyWindow?.rootViewController
|
||||
while let presented = vc?.presentedViewController { vc = presented }
|
||||
return vc
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ public class UxPlugin: NSObject, FlutterPlugin {
|
||||
plugins = [
|
||||
KeyboardPlugin(),
|
||||
SensorPlugin(),
|
||||
FilePlugin(),
|
||||
]
|
||||
for plugin in plugins {
|
||||
plugin.register(with: registrar)
|
||||
|
||||
Reference in New Issue
Block a user