From d6ca6d7fa55c6434f7900d54f4157ae1c6b9f270 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=AD=99=E5=A8=87?= Date: Mon, 26 Feb 2024 11:26:41 +0800 Subject: [PATCH] add iOS support --- README.md | 1 + ios/Runner.xcodeproj/project.pbxproj | 7 ++--- ios/Runner/AppDelegate.swift | 47 +++++++++++++++++++++++----- ios/Runner/Info.plist | 3 ++ lib/main.dart | 2 -- lib/pages/files_page.dart | 2 ++ lib/tools/ios_platform.dart | 21 +++++++++++++ 7 files changed, 70 insertions(+), 13 deletions(-) create mode 100644 lib/tools/ios_platform.dart diff --git a/README.md b/README.md index 2df343c..97710c5 100644 --- a/README.md +++ b/README.md @@ -7,6 +7,7 @@ todo: - ~~Convert, including case convert, Chinese simp/trad/pinyin convert, Latin/Cyrillic script transliteration~~.(Done.) - ~~Incremental renaming: for example, RenamerFile-1, RenamerFile-2, RenamerFile-3, RenamerFile-4, ...~~.(Done.) - Rules re-editing. +- Implement iOS renamer with specific code and Platform channel, references: [Writing custom platform-specific code](https://docs.flutter.dev/platform-integration/platform-channels?tab=type-mappings-swift-tab#type-mappings-swift-tab), [Providing access to directories](https://developer.apple.com/documentation/uikit/view_controllers/providing_access_to_directories), [juanmartin/renamerApp-ios](https://github.com/juanmartin/renamerApp-ios) # Screenshots ## Desktop diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj index 87927dc..d1e9320 100644 --- a/ios/Runner.xcodeproj/project.pbxproj +++ b/ios/Runner.xcodeproj/project.pbxproj @@ -97,7 +97,6 @@ A40B1E21F74A722135CAF778 /* Pods-RunnerTests.release.xcconfig */, 2581B69E8F1CCD428434EC5E /* Pods-RunnerTests.profile.xcconfig */, ); - name = Pods; path = Pods; sourceTree = ""; }; @@ -475,7 +474,7 @@ INFOPLIST_FILE = Runner/Info.plist; INFOPLIST_KEY_CFBundleDisplayName = Renamer; INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.utilities"; - IPHONEOS_DEPLOYMENT_TARGET = 17.0; + IPHONEOS_DEPLOYMENT_TARGET = 14.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -661,7 +660,7 @@ INFOPLIST_FILE = Runner/Info.plist; INFOPLIST_KEY_CFBundleDisplayName = Renamer; INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.utilities"; - IPHONEOS_DEPLOYMENT_TARGET = 17.0; + IPHONEOS_DEPLOYMENT_TARGET = 14.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -687,7 +686,7 @@ INFOPLIST_FILE = Runner/Info.plist; INFOPLIST_KEY_CFBundleDisplayName = Renamer; INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.utilities"; - IPHONEOS_DEPLOYMENT_TARGET = 17.0; + IPHONEOS_DEPLOYMENT_TARGET = 14.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", diff --git a/ios/Runner/AppDelegate.swift b/ios/Runner/AppDelegate.swift index 70693e4..93f479a 100644 --- a/ios/Runner/AppDelegate.swift +++ b/ios/Runner/AppDelegate.swift @@ -3,11 +3,44 @@ import Flutter @UIApplicationMain @objc class AppDelegate: FlutterAppDelegate { - override func application( - _ application: UIApplication, - didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? - ) -> Bool { - GeneratedPluginRegistrant.register(with: self) - return super.application(application, didFinishLaunchingWithOptions: launchOptions) - } + override func application( + _ application: UIApplication, + didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? + ) -> Bool { + GeneratedPluginRegistrant.register(with: self) + let controller: FlutterViewController = window?.rootViewController as! FlutterViewController + let filePickerChannel = FlutterMethodChannel(name: "net.sunjiao.renamer/picker", + binaryMessenger: controller.binaryMessenger) + filePickerChannel.setMethodCallHandler({ + [weak self] (call: FlutterMethodCall, result: @escaping FlutterResult) -> Void in + // This method is invoked on the UI thread. + if call.method == "dirAccess" { + self.dirAccess(result: result) + } else if call.method == "renameFile" { + self.renameFile(result: result) + } else { + result(FlutterMethodNotImplemented) + } + }) + + return super.application(application, didFinishLaunchingWithOptions: launchOptions) + } + + private func dirAccess(result: @escaping FlutterResult) { + let documentPicker = UIDocumentPickerViewController(forOpeningContentTypes: [.folder]) + documentPicker.delegate = self + window?.rootViewController?.present(documentPicker, animated: true, completion: nil) + } + + private func renameFile(result: @escaping FlutterResult) { + return + } +} + +extension AppDelegate: UIDocumentPickerDelegate { + func documentPicker(_ controller: UIDocumentPickerViewController, didPickDocumentsAt urls: [URL]) { + if let url = urls.first { + // result(url.path) + } + } } diff --git a/ios/Runner/Info.plist b/ios/Runner/Info.plist index 9749aba..b532311 100644 --- a/ios/Runner/Info.plist +++ b/ios/Runner/Info.plist @@ -64,5 +64,8 @@ NSPhotoLibraryUsageDescription Explain why your app uses photo library + NSDocumentsFolderUsageDescription + Explain why your app uses + diff --git a/lib/main.dart b/lib/main.dart index ba9587c..9bdc68c 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -100,8 +100,6 @@ class AppPage extends StatelessWidget { return; } } - - } void _permissionRequest(BuildContext context) => showDialog( diff --git a/lib/pages/files_page.dart b/lib/pages/files_page.dart index 70348f4..c23dd15 100644 --- a/lib/pages/files_page.dart +++ b/lib/pages/files_page.dart @@ -51,6 +51,8 @@ class FilesPageState extends State { } else { return; } + } else if (Platform.isIOS) { + } else { FilePickerResult? result = await FilePicker.platform.pickFiles(allowMultiple: true); if (result != null) { diff --git a/lib/tools/ios_platform.dart b/lib/tools/ios_platform.dart new file mode 100644 index 0000000..d1d2f96 --- /dev/null +++ b/lib/tools/ios_platform.dart @@ -0,0 +1,21 @@ +import 'package:flutter/services.dart'; + +class PlatformFilePicker { + static const MethodChannel _channel = MethodChannel('net.sunjiao.renamer/picker'); + + static Future dirAccess() async { + try { + await _channel.invokeMethod('dirAccess'); + } on PlatformException catch (e) { + // show error message + } + } + + static Future renameFile() async { + try { + await _channel.invokeMethod('renameFile'); + } on PlatformException catch (e) { + // show error message + } + } +}