diff --git a/macos/Runner.xcodeproj/project.pbxproj b/macos/Runner.xcodeproj/project.pbxproj index cfa915b..2521352 100644 --- a/macos/Runner.xcodeproj/project.pbxproj +++ b/macos/Runner.xcodeproj/project.pbxproj @@ -74,7 +74,7 @@ 33CC10F22044A3C60003C045 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Assets.xcassets; path = Runner/Assets.xcassets; sourceTree = ""; }; 33CC10F52044A3C60003C045 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = ""; }; 33CC10F72044A3C60003C045 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = Info.plist; path = Runner/Info.plist; sourceTree = ""; }; - 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainFlutterWindow.swift; sourceTree = ""; }; + 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */ = {isa = PBXFileReference; indentWidth = 2; lastKnownFileType = sourcecode.swift; path = MainFlutterWindow.swift; sourceTree = ""; tabWidth = 2; }; 33CEB47222A05771004F2AC0 /* Flutter-Debug.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Flutter-Debug.xcconfig"; sourceTree = ""; }; 33CEB47422A05771004F2AC0 /* Flutter-Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Flutter-Release.xcconfig"; sourceTree = ""; }; 33CEB47722A0578A004F2AC0 /* Flutter-Generated.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = "Flutter-Generated.xcconfig"; path = "ephemeral/Flutter-Generated.xcconfig"; sourceTree = ""; }; @@ -195,7 +195,6 @@ 6A4FDD73E68B92DC94675AC0 /* Pods-RunnerTests.release.xcconfig */, 0311B7E1884E9DFE7BD27E00 /* Pods-RunnerTests.profile.xcconfig */, ); - name = Pods; path = Pods; sourceTree = ""; }; @@ -575,6 +574,7 @@ "$(inherited)", "@executable_path/../Frameworks", ); + MACOSX_DEPLOYMENT_TARGET = 11.0; PROVISIONING_PROFILE_SPECIFIER = ""; SWIFT_VERSION = 5.0; }; @@ -701,6 +701,7 @@ "$(inherited)", "@executable_path/../Frameworks", ); + MACOSX_DEPLOYMENT_TARGET = 11.0; PROVISIONING_PROFILE_SPECIFIER = ""; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 5.0; @@ -721,6 +722,7 @@ "$(inherited)", "@executable_path/../Frameworks", ); + MACOSX_DEPLOYMENT_TARGET = 11.0; PROVISIONING_PROFILE_SPECIFIER = ""; SWIFT_VERSION = 5.0; }; diff --git a/macos/Runner/DebugProfile.entitlements b/macos/Runner/DebugProfile.entitlements index 08c3ab1..cff5a4b 100644 --- a/macos/Runner/DebugProfile.entitlements +++ b/macos/Runner/DebugProfile.entitlements @@ -10,5 +10,7 @@ com.apple.security.network.client + com.apple.security.files.user-selected.read-write + diff --git a/macos/Runner/MainFlutterWindow.swift b/macos/Runner/MainFlutterWindow.swift index 3cc05eb..73caf98 100644 --- a/macos/Runner/MainFlutterWindow.swift +++ b/macos/Runner/MainFlutterWindow.swift @@ -1,5 +1,53 @@ import Cocoa import FlutterMacOS +import Foundation +import System +import UniformTypeIdentifiers + +func extFromMimetype(mimeType: String) -> String? { + switch (mimeType) { + case "image/jpeg": + return ".jpg" + case "image/png": + return ".png" + case "image/webp": + return ".webp" + case "application/zip": + return ".zip" + default: + return nil + } +} + +func openFile(result: FlutterResult, fn: String, readOnly: Bool, writeOnly: Bool, append: Bool) { + let mode = if readOnly && writeOnly { + FileDescriptor.AccessMode.readWrite + } else if readOnly { + FileDescriptor.AccessMode.readOnly + } else if writeOnly { + FileDescriptor.AccessMode.writeOnly + } else { + FileDescriptor.AccessMode.readWrite + } + var opts = FileDescriptor.OpenOptions.init() + if writeOnly { + opts.insert(FileDescriptor.OpenOptions.create) + if !readOnly { + opts.insert(FileDescriptor.OpenOptions.truncate) + } + } + if append { + opts.insert(FileDescriptor.OpenOptions.append) + } + let permissions = FilePermissions(rawValue: 0o644); + let path = FilePath(stringLiteral: fn); + do { + let fd = try FileDescriptor.open(path, mode, options: opts, permissions: permissions) + result(NSNumber(value: fd.rawValue)) + } catch { + result(FlutterError(code: "OEPN_FILE_FAILED", message: nil, details: nil)) + } +} class MainFlutterWindow: NSWindow { override func awakeFromNib() { @@ -7,9 +55,88 @@ class MainFlutterWindow: NSWindow { let windowFrame = self.frame self.contentViewController = flutterViewController self.setFrame(windowFrame, display: true) - + + let safChannel = FlutterMethodChannel( + name: "lifegpc.eh_downloader_flutter/saf", + binaryMessenger: flutterViewController.engine.binaryMessenger) + safChannel.setMethodCallHandler { (call, result) in + switch call.method { + case "openFile": + if let args = call.arguments as? Array, + let fileName = args[0] as? String, + let dir = args[1] as? String, + let mimeType = args[2] as? String, + let readOnly = args[3] as? NSNumber, + let writeOnly = args[4] as? NSNumber, + let append = args[5] as? NSNumber, + let saveAs = args[6] as? NSNumber { + let ext = extFromMimetype(mimeType: mimeType) + if saveAs.boolValue { + let panel = NSSavePanel() + panel.canCreateDirectories = true + panel.canSelectHiddenExtension = true + if let typ = UTType.init(mimeType: mimeType) { + panel.allowedContentTypes = [typ] + } + panel.allowsOtherFileTypes = true + panel.begin { (res) in + if res == NSApplication.ModalResponse.OK { + if let fn = panel.url { + if fn.isFileURL { + let fp = if #available(macOS 13.0, *) { + fn.path(percentEncoded: false) + } else { + fn.path + } + return openFile(result: result, fn: fp, readOnly: readOnly.boolValue, writeOnly: writeOnly.boolValue, append: append.boolValue) + } + } + } + result(FlutterError(code: "USER_CANCELED", message: nil, details: nil)) + } + } else { + let fn = NSString.path(withComponents: [dir, fileName]) + (ext ?? "") + openFile(result: result, fn: fn, readOnly: readOnly.boolValue, writeOnly: writeOnly.boolValue, append: append.boolValue) + } + } else { + result(FlutterError(code: "INVALID_ARGUMENTS", message: nil, details: nil)) + } + case "writeFile": + if let args = call.arguments as? Array, + let fd = args[0] as? NSNumber, + let data = args[1] as? FlutterStandardTypedData { + let fD = FileDescriptor(rawValue: fd.int32Value) + do { + let readed = try data.data.withUnsafeBytes{ (re) in + try fD.write(re) + } + result(NSNumber(value: readed)) + } catch { + result(FlutterError(code: "WRITE_FILE_FAILED", message: nil, details: nil)) + } + } else { + result(FlutterError(code: "INVALID_ARGUMENTS", message: nil, details: nil)) + } + case "closeFile": + if let args = call.arguments as? Array, + let fd = args[0] as? NSNumber { + let fD = FileDescriptor(rawValue: fd.int32Value) + do { + try fD.close() + result(nil) + } catch { + result(FlutterError(code: "CLOSE_FILE_FAILED", message: nil, details: nil)) + } + } else { + result(FlutterError(code: "INVALID_ARGUMENTS", message: nil, details: nil)) + } + default: + result(FlutterMethodNotImplemented) + } + } + RegisterGeneratedPlugins(registry: flutterViewController) - + super.awakeFromNib() } } diff --git a/macos/Runner/Release.entitlements b/macos/Runner/Release.entitlements index ee95ab7..38da9a9 100644 --- a/macos/Runner/Release.entitlements +++ b/macos/Runner/Release.entitlements @@ -6,5 +6,7 @@ com.apple.security.network.client + com.apple.security.files.user-selected.read-write +