diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj index 57d49b7..58a3e85 100644 --- a/ios/Runner.xcodeproj/project.pbxproj +++ b/ios/Runner.xcodeproj/project.pbxproj @@ -11,6 +11,11 @@ 331C808B294A63AB00263BE5 /* RunnerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 331C807B294A618700263BE5 /* RunnerTests.swift */; }; 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; }; + AA1234561234567890ABCDE1 /* PigeonApi.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA1234561234567890ABCDE0 /* PigeonApi.swift */; }; + AA1234561234567890ABCDE3 /* OpenListManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA1234561234567890ABCDE2 /* OpenListManager.swift */; }; + AA1234561234567890ABCDE5 /* OpenListBridge.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA1234561234567890ABCDE4 /* OpenListBridge.swift */; }; + AA1234561234567890ABCDE7 /* AppConfigBridge.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA1234561234567890ABCDE6 /* AppConfigBridge.swift */; }; + AA1234561234567890ABCDE9 /* CommonBridge.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA1234561234567890ABCDE8 /* CommonBridge.swift */; }; 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; @@ -47,6 +52,11 @@ 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; }; 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + AA1234561234567890ABCDE0 /* PigeonApi.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PigeonApi.swift; sourceTree = ""; }; + AA1234561234567890ABCDE2 /* OpenListManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OpenListManager.swift; sourceTree = ""; }; + AA1234561234567890ABCDE4 /* OpenListBridge.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OpenListBridge.swift; sourceTree = ""; }; + AA1234561234567890ABCDE6 /* AppConfigBridge.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppConfigBridge.swift; sourceTree = ""; }; + AA1234561234567890ABCDE8 /* CommonBridge.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CommonBridge.swift; sourceTree = ""; }; 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; @@ -109,6 +119,7 @@ 97C146F01CF9000F007C117D /* Runner */ = { isa = PBXGroup; children = ( + AA1234561234567890ABCDEA /* Bridges */, 97C146FA1CF9000F007C117D /* Main.storyboard */, 97C146FD1CF9000F007C117D /* Assets.xcassets */, 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */, @@ -117,10 +128,22 @@ 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */, 74858FAE1ED2DC5600515810 /* AppDelegate.swift */, 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */, + AA1234561234567890ABCDE0 /* PigeonApi.swift */, + AA1234561234567890ABCDE2 /* OpenListManager.swift */, ); path = Runner; sourceTree = ""; }; + AA1234561234567890ABCDEA /* Bridges */ = { + isa = PBXGroup; + children = ( + AA1234561234567890ABCDE4 /* OpenListBridge.swift */, + AA1234561234567890ABCDE6 /* AppConfigBridge.swift */, + AA1234561234567890ABCDE8 /* CommonBridge.swift */, + ); + path = Bridges; + sourceTree = ""; + }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ @@ -270,6 +293,11 @@ files = ( 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */, 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */, + AA1234561234567890ABCDE1 /* PigeonApi.swift in Sources */, + AA1234561234567890ABCDE3 /* OpenListManager.swift in Sources */, + AA1234561234567890ABCDE5 /* OpenListBridge.swift in Sources */, + AA1234561234567890ABCDE7 /* AppConfigBridge.swift in Sources */, + AA1234561234567890ABCDE9 /* CommonBridge.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/ios/Runner/AppDelegate.swift b/ios/Runner/AppDelegate.swift index 6266644..1889b67 100644 --- a/ios/Runner/AppDelegate.swift +++ b/ios/Runner/AppDelegate.swift @@ -3,11 +3,85 @@ import UIKit @main @objc class AppDelegate: FlutterAppDelegate { + var eventAPI: Event? + private var backgroundTask: UIBackgroundTaskIdentifier = .invalid + override func application( _ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? ) -> Bool { GeneratedPluginRegistrant.register(with: self) + + // Setup Pigeon APIs + guard let controller = window?.rootViewController as? FlutterViewController else { + print("[AppDelegate] Failed to get FlutterViewController") + return super.application(application, didFinishLaunchingWithOptions: launchOptions) + } + + let messenger = controller.binaryMessenger + + // Register Pigeon API implementations + AppConfigSetup.setUp(binaryMessenger: messenger, api: AppConfigBridge()) + AndroidSetup.setUp(binaryMessenger: messenger, api: OpenListBridge()) + NativeCommonSetup.setUp(binaryMessenger: messenger, api: CommonBridge(viewController: controller)) + + // Setup Event API for Flutter callbacks + eventAPI = Event(binaryMessenger: messenger) + + // Initialize OpenList core (if XCFramework is available) + #if canImport(Openlistlib) + let eventHandler = OpenListEventHandler() + let logCallback = OpenListLogCallback() + eventHandler.eventAPI = eventAPI + logCallback.eventAPI = eventAPI + + do { + try OpenListManager.shared.initialize(event: eventHandler, logger: logCallback) + print("[AppDelegate] OpenList core initialized") + } catch { + print("[AppDelegate] OpenList core initialization failed: \(error)") + // Continue without core - will work in Flutter-only mode + } + #else + print("[AppDelegate] OpenList core not available - running in Flutter-only mode") + #endif + + print("[AppDelegate] Pigeon APIs registered successfully") + return super.application(application, didFinishLaunchingWithOptions: launchOptions) } + + // MARK: - Application Lifecycle + + override func applicationWillTerminate(_ application: UIApplication) { + // Cleanup OpenList core + OpenListManager.shared.stopServer() + + // End background task if still active + endBackgroundTask() + + super.applicationWillTerminate(application) + } + + override func applicationDidEnterBackground(_ application: UIApplication) { + // Begin background task to prevent WebView process suspension + backgroundTask = application.beginBackgroundTask { [weak self] in + // Background task is about to expire, clean up + self?.endBackgroundTask() + } + } + + override func applicationWillEnterForeground(_ application: UIApplication) { + // End background task when returning to foreground + endBackgroundTask() + } + + // MARK: - Background Task Management + + private func endBackgroundTask() { + if backgroundTask != .invalid { + UIApplication.shared.endBackgroundTask(backgroundTask) + backgroundTask = .invalid + } + } } diff --git a/ios/Runner/Bridges/AppConfigBridge.swift b/ios/Runner/Bridges/AppConfigBridge.swift new file mode 100644 index 0000000..fb683ee --- /dev/null +++ b/ios/Runner/Bridges/AppConfigBridge.swift @@ -0,0 +1,109 @@ +import Flutter +import Foundation + +/// Bridge implementation for App Configuration APIs +class AppConfigBridge: NSObject, AppConfig { + private let defaults = UserDefaults.standard + + // Keys for UserDefaults + private enum Keys { + static let wakeLock = "app_config_wake_lock" + static let startAtBoot = "app_config_start_at_boot" + static let autoCheckUpdate = "app_config_auto_check_update" + static let autoOpenWebPage = "app_config_auto_open_web_page" + static let dataDir = "app_config_data_dir" + static let silentJumpApp = "app_config_silent_jump_app" + } + + func isWakeLockEnabled() throws -> Bool { + return defaults.bool(forKey: Keys.wakeLock) + } + + func setWakeLockEnabled(enabled: Bool) throws { + defaults.set(enabled, forKey: Keys.wakeLock) + print("[AppConfigBridge] Wake lock enabled: \(enabled)") + } + + func isStartAtBootEnabled() throws -> Bool { + return defaults.bool(forKey: Keys.startAtBoot) + } + + func setStartAtBootEnabled(enabled: Bool) throws { + defaults.set(enabled, forKey: Keys.startAtBoot) + print("[AppConfigBridge] Start at boot enabled: \(enabled)") + } + + func isAutoCheckUpdateEnabled() throws -> Bool { + return defaults.bool(forKey: Keys.autoCheckUpdate) + } + + func setAutoCheckUpdateEnabled(enabled: Bool) throws { + defaults.set(enabled, forKey: Keys.autoCheckUpdate) + print("[AppConfigBridge] Auto check update enabled: \(enabled)") + } + + func isAutoOpenWebPageEnabled() throws -> Bool { + return defaults.bool(forKey: Keys.autoOpenWebPage) + } + + func setAutoOpenWebPageEnabled(enabled: Bool) throws { + defaults.set(enabled, forKey: Keys.autoOpenWebPage) + print("[AppConfigBridge] Auto open web page enabled: \(enabled)") + } + + func getDataDir() throws -> String { + if let customDir = defaults.string(forKey: Keys.dataDir), !customDir.isEmpty { + print("[AppConfigBridge] Using custom data directory: \(customDir)") + return customDir + } + + // Default to app's document directory with openlist_data subdirectory + // This follows iOS app data storage guidelines + let paths = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask) + let documentsDirectory = paths[0] + let openlistDataDir = documentsDirectory.appendingPathComponent("openlist_data") + + // Create directory if not exists + if !FileManager.default.fileExists(atPath: openlistDataDir.path) { + do { + try FileManager.default.createDirectory(at: openlistDataDir, withIntermediateDirectories: true, attributes: nil) + print("[AppConfigBridge] Created data directory: \(openlistDataDir.path)") + } catch { + print("[AppConfigBridge] Failed to create data directory: \(error)") + throw error + } + } + + print("[AppConfigBridge] Data directory: \(openlistDataDir.path)") + return openlistDataDir.path + } + + func setDataDir(dir: String) throws { + // On iOS, we should not allow users to change data directory arbitrarily + // But we keep the method for compatibility + if dir.isEmpty { + defaults.removeObject(forKey: Keys.dataDir) + print("[AppConfigBridge] Data directory reset to default") + } else { + // iOS: Only allow setting within app's container + let appDir = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0].path + if dir.hasPrefix(appDir) { + defaults.set(dir, forKey: Keys.dataDir) + print("[AppConfigBridge] Data directory set to: \(dir)") + } else { + print("[AppConfigBridge] Rejected invalid data directory (outside app container): \(dir)") + throw NSError(domain: "AppConfigBridge", code: -2, + userInfo: [NSLocalizedDescriptionKey: "Data directory must be within app container"]) + } + } + } + + func isSilentJumpAppEnabled() throws -> Bool { + return defaults.bool(forKey: Keys.silentJumpApp) + } + + func setSilentJumpAppEnabled(enabled: Bool) throws { + defaults.set(enabled, forKey: Keys.silentJumpApp) + print("[AppConfigBridge] Silent jump app enabled: \(enabled)") + } +} diff --git a/ios/Runner/Bridges/CommonBridge.swift b/ios/Runner/Bridges/CommonBridge.swift new file mode 100644 index 0000000..b08987e --- /dev/null +++ b/ios/Runner/Bridges/CommonBridge.swift @@ -0,0 +1,124 @@ +import Flutter +import Foundation +import UIKit + +/// Bridge implementation for common native APIs +class CommonBridge: NSObject, NativeCommon { + private let viewController: UIViewController? + + init(viewController: UIViewController? = nil) { + self.viewController = viewController + super.init() + } + + func startActivityFromUri(intentUri: String) throws -> Bool { + print("[CommonBridge] startActivityFromUri: \(intentUri)") + + guard let url = URL(string: intentUri) else { + print("[CommonBridge] Invalid URL: \(intentUri)") + return false + } + + // Check if the URL can be opened + guard UIApplication.shared.canOpenURL(url) else { + print("[CommonBridge] Cannot open URL: \(intentUri)") + return false + } + + // Open the URL + UIApplication.shared.open(url, options: [:]) { success in + print("[CommonBridge] Open URL result: \(success)") + } + + return true + } + + func getDeviceSdkInt() throws -> Int64 { + // iOS doesn't have SDK int like Android, return iOS major version + let systemVersion = UIDevice.current.systemVersion + let majorVersion = systemVersion.components(separatedBy: ".").first ?? "0" + let version = Int64(majorVersion) ?? 0 + print("[CommonBridge] Device iOS version: \(version)") + return version + } + + func getDeviceCPUABI() throws -> String { + // Get CPU architecture + var systemInfo = utsname() + uname(&systemInfo) + let machineMirror = Mirror(reflecting: systemInfo.machine) + let identifier = machineMirror.children.reduce("") { identifier, element in + guard let value = element.value as? Int8, value != 0 else { return identifier } + return identifier + String(UnicodeScalar(UInt8(value))) + } + + print("[CommonBridge] Device CPU ABI: \(identifier)") + return identifier + } + + func getVersionName() throws -> String { + let version = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String ?? "1.0.0" + print("[CommonBridge] Version name: \(version)") + return version + } + + func getVersionCode() throws -> Int64 { + let build = Bundle.main.infoDictionary?["CFBundleVersion"] as? String ?? "1" + let code = Int64(build) ?? 1 + print("[CommonBridge] Version code: \(code)") + return code + } + + func toast(msg: String) throws { + print("[CommonBridge] Toast: \(msg)") + showToast(message: msg, duration: 2.0) + } + + func longToast(msg: String) throws { + print("[CommonBridge] Long toast: \(msg)") + showToast(message: msg, duration: 4.0) + } + + // MARK: - Toast Helper + + private func showToast(message: String, duration: TimeInterval) { + DispatchQueue.main.async { [weak self] in + guard let window = UIApplication.shared.windows.first(where: { $0.isKeyWindow }) else { + print("[CommonBridge] No key window found for toast") + return + } + + let toastLabel = UILabel() + toastLabel.backgroundColor = UIColor.black.withAlphaComponent(0.7) + toastLabel.textColor = UIColor.white + toastLabel.textAlignment = .center + toastLabel.font = UIFont.systemFont(ofSize: 14) + toastLabel.text = message + toastLabel.alpha = 0.0 + toastLabel.layer.cornerRadius = 10 + toastLabel.clipsToBounds = true + toastLabel.numberOfLines = 0 + + let maxSize = CGSize(width: window.frame.width - 80, height: window.frame.height) + let expectedSize = toastLabel.sizeThatFits(maxSize) + toastLabel.frame = CGRect( + x: (window.frame.width - expectedSize.width - 20) / 2, + y: window.frame.height - 150, + width: expectedSize.width + 20, + height: expectedSize.height + 20 + ) + + window.addSubview(toastLabel) + + UIView.animate(withDuration: 0.3, animations: { + toastLabel.alpha = 1.0 + }) { _ in + UIView.animate(withDuration: 0.3, delay: duration, options: [], animations: { + toastLabel.alpha = 0.0 + }) { _ in + toastLabel.removeFromSuperview() + } + } + } + } +} diff --git a/ios/Runner/Bridges/OpenListBridge.swift b/ios/Runner/Bridges/OpenListBridge.swift new file mode 100644 index 0000000..7bbcc79 --- /dev/null +++ b/ios/Runner/Bridges/OpenListBridge.swift @@ -0,0 +1,48 @@ +import Flutter +import Foundation + +/// Bridge implementation for Android-specific APIs (iOS equivalent) +class OpenListBridge: NSObject, Android { + private let registrar: FlutterPluginRegistrar? + + init(registrar: FlutterPluginRegistrar? = nil) { + self.registrar = registrar + super.init() + } + + func addShortcut() throws { + print("[OpenListBridge] addShortcut called - iOS does not support shortcuts like Android") + // iOS doesn't have the same shortcut system as Android + // This is a no-op on iOS + } + + func startService() throws { + print("[OpenListBridge] startService called") + OpenListManager.shared.startServer() + } + + func setAdminPwd(pwd: String) throws { + print("[OpenListBridge] setAdminPwd called") + // Store admin password in UserDefaults or Keychain + UserDefaults.standard.set(pwd, forKey: "openlist_admin_pwd") + } + + func getOpenListHttpPort() throws -> Int64 { + let port = OpenListManager.shared.getHttpPort() + print("[OpenListBridge] getOpenListHttpPort: \(port)") + return Int64(port) + } + + func isRunning() throws -> Bool { + let running = OpenListManager.shared.isRunning() + print("[OpenListBridge] isRunning: \(running)") + return running + } + + func getOpenListVersion() throws -> String { + // Get version from build configuration or Info.plist + let version = Bundle.main.infoDictionary?["OpenListVersion"] as? String ?? "dev" + print("[OpenListBridge] getOpenListVersion: \(version)") + return version + } +} diff --git a/ios/Runner/Info.plist b/ios/Runner/Info.plist index 6307ad0..db78556 100644 --- a/ios/Runner/Info.plist +++ b/ios/Runner/Info.plist @@ -94,5 +94,21 @@ + UIBackgroundModes + + fetch + processing + + OpenListVersion + dev-ios-beta + WKAppBoundDomains + + localhost + 127.0.0.1 + + UIFileSharingEnabled + + LSSupportsOpeningDocumentsInPlace + diff --git a/ios/Runner/OpenListManager.swift b/ios/Runner/OpenListManager.swift new file mode 100644 index 0000000..bb66ada --- /dev/null +++ b/ios/Runner/OpenListManager.swift @@ -0,0 +1,197 @@ +import Foundation +import Openlistlib + +/// Manages OpenList core server lifecycle +class OpenListManager: NSObject { + static let shared = OpenListManager() + + private var isInitialized = false + private var isServerRunning = false + private var dataDir: String? + + // Keep strong references to prevent deallocation + var eventHandler: OpenListEventHandler? + var logCallback: OpenListLogCallback? + + private override init() { + super.init() + } + + // MARK: - Initialization + + func initialize(event: OpenListEventHandler, logger: OpenListLogCallback) throws { + guard !isInitialized else { + print("[OpenListManager] Already initialized") + return + } + + // Get data directory from AppConfigBridge + let appConfig = AppConfigBridge() + let dataDirPath: String + do { + dataDirPath = try appConfig.getDataDir() + self.dataDir = dataDirPath + print("[OpenListManager] Data directory: \(dataDirPath)") + } catch { + print("[OpenListManager] Failed to get data directory: \(error)") + throw error + } + + // Set data directory for OpenList core (no error return) + OpenlistlibSetConfigData(dataDirPath) + + // Enable stdout logging (no error return) + OpenlistlibSetConfigLogStd(true) + + var error: NSError? + OpenlistlibInit(event, logger, &error) + if let err = error { + print("[OpenListManager] Initialization failed: \(err)") + throw err + } + isInitialized = true + print("[OpenListManager] Initialized successfully with data directory: \(dataDirPath)") + } + + // MARK: - Server Control + + func startServer() { + print("[OpenListManager] Start server request received") + + // Check if initialized, if not, try to initialize first + if !isInitialized { + print("[OpenListManager] Not initialized, attempting initialization...") + let eventHandler = OpenListEventHandler() + let logCallback = OpenListLogCallback() + + // Set event API reference before initialization + let appDelegate = UIApplication.shared.delegate as? AppDelegate + eventHandler.eventAPI = appDelegate?.eventAPI + logCallback.eventAPI = appDelegate?.eventAPI + + // Store references globally for persistence + OpenListManager.shared.eventHandler = eventHandler + OpenListManager.shared.logCallback = logCallback + + do { + try initialize(event: eventHandler, logger: logCallback) + print("[OpenListManager] Initialization completed, proceeding to start server") + } catch { + print("[OpenListManager] Initialization failed: \(error), cannot start server") + return + } + } + + guard !isServerRunning else { + print("[OpenListManager] Server already running") + return + } + + print("[OpenListManager] Starting OpenList server with data directory: \(dataDir ?? "unknown")...") + DispatchQueue.global(qos: .userInitiated).async { [weak self] in + OpenlistlibStart() + + // Small delay to ensure server is ready + Thread.sleep(forTimeInterval: 0.5) + + DispatchQueue.main.async { + self?.isServerRunning = true + print("[OpenListManager] Server started successfully") + + // Notify Flutter side + if let eventAPI = (UIApplication.shared.delegate as? AppDelegate)?.eventAPI { + eventAPI.onServiceStatusChanged(isRunning: true) { result in + switch result { + case .failure(let error): + print("[OpenListManager] Failed to notify Flutter of status change: \(error)") + case .success: + print("[OpenListManager] Status change notification sent to Flutter") + } + } + } + } + } + } + + func stopServer(timeout: Int64 = 5000) { + guard isServerRunning else { + print("[OpenListManager] Server not running") + return + } + + print("[OpenListManager] Stopping OpenList server...") + var error: NSError? + OpenlistlibShutdown(timeout, &error) + if let err = error { + print("[OpenListManager] Failed to stop server: \(err)") + return + } + isServerRunning = false + print("[OpenListManager] Server stopped") + } + + func isRunning() -> Bool { + return isServerRunning && OpenlistlibIsRunning("http") + } + + func getHttpPort() -> Int { + // Default port for OpenList + return 5244 + } + + func forceDBSync() { + var error: NSError? + OpenlistlibForceDBSync(&error) + if let err = error { + print("[OpenListManager] Database sync failed: \(err)") + return + } + print("[OpenListManager] Database sync completed") + } +} + +// MARK: - Event Handler + +class OpenListEventHandler: NSObject, OpenlistlibEventProtocol { + weak var eventAPI: Event? + + func onStartError(_ t: String?, err: String?) { + print("[OpenListEvent] Start error - Type: \(t ?? "unknown"), Error: \(err ?? "unknown")") + // Notify Flutter side via Event API if needed + } + + func onShutdown(_ t: String?) { + print("[OpenListEvent] Shutdown - Type: \(t ?? "unknown")") + // Notify Flutter side via Event API if needed + } + + func onProcessExit(_ code: Int) { + print("[OpenListEvent] Process exit - Code: \(code)") + // Handle process exit if needed + } +} + +// MARK: - Log Callback + +class OpenListLogCallback: NSObject, OpenlistlibLogCallbackProtocol { + weak var eventAPI: Event? + + func onLog(_ level: Int16, time: Int64, message: String?) { + let logMessage = message ?? "" + print("[OpenListLog] Level: \(level), Message: \(logMessage)") + + // Forward logs to Flutter side + if let api = eventAPI { + api.onServerLog(level: Int64(level), time: "\(time)", log: logMessage) { result in + switch result { + case .failure(let error): + print("[OpenListLog] Failed to send log to Flutter: \(error)") + case .success: + break // Success, no action needed + } + } + } else { + print("[OpenListLog] Warning: eventAPI is nil, cannot forward log to Flutter") + } + } +} diff --git a/ios/Runner/PigeonApi.swift b/ios/Runner/PigeonApi.swift new file mode 100644 index 0000000..a9bbda6 --- /dev/null +++ b/ios/Runner/PigeonApi.swift @@ -0,0 +1,549 @@ +// Autogenerated from Pigeon (v26.0.2), do not edit directly. +// See also: https://pub.dev/packages/pigeon + +import Foundation + +#if os(iOS) + import Flutter +#elseif os(macOS) + import FlutterMacOS +#else + #error("Unsupported platform.") +#endif + +/// Error class for passing custom error details to Dart side. +final class PigeonError: Error { + let code: String + let message: String? + let details: Sendable? + + init(code: String, message: String?, details: Sendable?) { + self.code = code + self.message = message + self.details = details + } + + var localizedDescription: String { + return + "PigeonError(code: \(code), message: \(message ?? ""), details: \(details ?? "")" + } +} + +private func wrapResult(_ result: Any?) -> [Any?] { + return [result] +} + +private func wrapError(_ error: Any) -> [Any?] { + if let pigeonError = error as? PigeonError { + return [ + pigeonError.code, + pigeonError.message, + pigeonError.details, + ] + } + if let flutterError = error as? FlutterError { + return [ + flutterError.code, + flutterError.message, + flutterError.details, + ] + } + return [ + "\(error)", + "\(type(of: error))", + "Stacktrace: \(Thread.callStackSymbols)", + ] +} + +private func createConnectionError(withChannelName channelName: String) -> PigeonError { + return PigeonError(code: "channel-error", message: "Unable to establish connection on channel: '\(channelName)'.", details: "") +} + +private func isNullish(_ value: Any?) -> Bool { + return value is NSNull || value == nil +} + +private func nilOrValue(_ value: Any?) -> T? { + if value is NSNull { return nil } + return value as! T? +} + + +private class PigeonApiPigeonCodecReader: FlutterStandardReader { +} + +private class PigeonApiPigeonCodecWriter: FlutterStandardWriter { +} + +private class PigeonApiPigeonCodecReaderWriter: FlutterStandardReaderWriter { + override func reader(with data: Data) -> FlutterStandardReader { + return PigeonApiPigeonCodecReader(data: data) + } + + override func writer(with data: NSMutableData) -> FlutterStandardWriter { + return PigeonApiPigeonCodecWriter(data: data) + } +} + +class PigeonApiPigeonCodec: FlutterStandardMessageCodec, @unchecked Sendable { + static let shared = PigeonApiPigeonCodec(readerWriter: PigeonApiPigeonCodecReaderWriter()) +} + +/// Generated protocol from Pigeon that represents a handler of messages from Flutter. +protocol AppConfig { + func isWakeLockEnabled() throws -> Bool + func setWakeLockEnabled(enabled: Bool) throws + func isStartAtBootEnabled() throws -> Bool + func setStartAtBootEnabled(enabled: Bool) throws + func isAutoCheckUpdateEnabled() throws -> Bool + func setAutoCheckUpdateEnabled(enabled: Bool) throws + func isAutoOpenWebPageEnabled() throws -> Bool + func setAutoOpenWebPageEnabled(enabled: Bool) throws + func getDataDir() throws -> String + func setDataDir(dir: String) throws + func isSilentJumpAppEnabled() throws -> Bool + func setSilentJumpAppEnabled(enabled: Bool) throws +} + +/// Generated setup class from Pigeon to handle messages through the `binaryMessenger`. +class AppConfigSetup { + static var codec: FlutterStandardMessageCodec { PigeonApiPigeonCodec.shared } + /// Sets up an instance of `AppConfig` to handle messages through the `binaryMessenger`. + static func setUp(binaryMessenger: FlutterBinaryMessenger, api: AppConfig?, messageChannelSuffix: String = "") { + let channelSuffix = messageChannelSuffix.count > 0 ? ".\(messageChannelSuffix)" : "" + let isWakeLockEnabledChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.openlist_mobile.AppConfig.isWakeLockEnabled\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec) + if let api = api { + isWakeLockEnabledChannel.setMessageHandler { _, reply in + do { + let result = try api.isWakeLockEnabled() + reply(wrapResult(result)) + } catch { + reply(wrapError(error)) + } + } + } else { + isWakeLockEnabledChannel.setMessageHandler(nil) + } + let setWakeLockEnabledChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.openlist_mobile.AppConfig.setWakeLockEnabled\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec) + if let api = api { + setWakeLockEnabledChannel.setMessageHandler { message, reply in + let args = message as! [Any?] + let enabledArg = args[0] as! Bool + do { + try api.setWakeLockEnabled(enabled: enabledArg) + reply(wrapResult(nil)) + } catch { + reply(wrapError(error)) + } + } + } else { + setWakeLockEnabledChannel.setMessageHandler(nil) + } + let isStartAtBootEnabledChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.openlist_mobile.AppConfig.isStartAtBootEnabled\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec) + if let api = api { + isStartAtBootEnabledChannel.setMessageHandler { _, reply in + do { + let result = try api.isStartAtBootEnabled() + reply(wrapResult(result)) + } catch { + reply(wrapError(error)) + } + } + } else { + isStartAtBootEnabledChannel.setMessageHandler(nil) + } + let setStartAtBootEnabledChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.openlist_mobile.AppConfig.setStartAtBootEnabled\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec) + if let api = api { + setStartAtBootEnabledChannel.setMessageHandler { message, reply in + let args = message as! [Any?] + let enabledArg = args[0] as! Bool + do { + try api.setStartAtBootEnabled(enabled: enabledArg) + reply(wrapResult(nil)) + } catch { + reply(wrapError(error)) + } + } + } else { + setStartAtBootEnabledChannel.setMessageHandler(nil) + } + let isAutoCheckUpdateEnabledChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.openlist_mobile.AppConfig.isAutoCheckUpdateEnabled\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec) + if let api = api { + isAutoCheckUpdateEnabledChannel.setMessageHandler { _, reply in + do { + let result = try api.isAutoCheckUpdateEnabled() + reply(wrapResult(result)) + } catch { + reply(wrapError(error)) + } + } + } else { + isAutoCheckUpdateEnabledChannel.setMessageHandler(nil) + } + let setAutoCheckUpdateEnabledChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.openlist_mobile.AppConfig.setAutoCheckUpdateEnabled\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec) + if let api = api { + setAutoCheckUpdateEnabledChannel.setMessageHandler { message, reply in + let args = message as! [Any?] + let enabledArg = args[0] as! Bool + do { + try api.setAutoCheckUpdateEnabled(enabled: enabledArg) + reply(wrapResult(nil)) + } catch { + reply(wrapError(error)) + } + } + } else { + setAutoCheckUpdateEnabledChannel.setMessageHandler(nil) + } + let isAutoOpenWebPageEnabledChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.openlist_mobile.AppConfig.isAutoOpenWebPageEnabled\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec) + if let api = api { + isAutoOpenWebPageEnabledChannel.setMessageHandler { _, reply in + do { + let result = try api.isAutoOpenWebPageEnabled() + reply(wrapResult(result)) + } catch { + reply(wrapError(error)) + } + } + } else { + isAutoOpenWebPageEnabledChannel.setMessageHandler(nil) + } + let setAutoOpenWebPageEnabledChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.openlist_mobile.AppConfig.setAutoOpenWebPageEnabled\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec) + if let api = api { + setAutoOpenWebPageEnabledChannel.setMessageHandler { message, reply in + let args = message as! [Any?] + let enabledArg = args[0] as! Bool + do { + try api.setAutoOpenWebPageEnabled(enabled: enabledArg) + reply(wrapResult(nil)) + } catch { + reply(wrapError(error)) + } + } + } else { + setAutoOpenWebPageEnabledChannel.setMessageHandler(nil) + } + let getDataDirChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.openlist_mobile.AppConfig.getDataDir\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec) + if let api = api { + getDataDirChannel.setMessageHandler { _, reply in + do { + let result = try api.getDataDir() + reply(wrapResult(result)) + } catch { + reply(wrapError(error)) + } + } + } else { + getDataDirChannel.setMessageHandler(nil) + } + let setDataDirChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.openlist_mobile.AppConfig.setDataDir\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec) + if let api = api { + setDataDirChannel.setMessageHandler { message, reply in + let args = message as! [Any?] + let dirArg = args[0] as! String + do { + try api.setDataDir(dir: dirArg) + reply(wrapResult(nil)) + } catch { + reply(wrapError(error)) + } + } + } else { + setDataDirChannel.setMessageHandler(nil) + } + let isSilentJumpAppEnabledChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.openlist_mobile.AppConfig.isSilentJumpAppEnabled\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec) + if let api = api { + isSilentJumpAppEnabledChannel.setMessageHandler { _, reply in + do { + let result = try api.isSilentJumpAppEnabled() + reply(wrapResult(result)) + } catch { + reply(wrapError(error)) + } + } + } else { + isSilentJumpAppEnabledChannel.setMessageHandler(nil) + } + let setSilentJumpAppEnabledChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.openlist_mobile.AppConfig.setSilentJumpAppEnabled\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec) + if let api = api { + setSilentJumpAppEnabledChannel.setMessageHandler { message, reply in + let args = message as! [Any?] + let enabledArg = args[0] as! Bool + do { + try api.setSilentJumpAppEnabled(enabled: enabledArg) + reply(wrapResult(nil)) + } catch { + reply(wrapError(error)) + } + } + } else { + setSilentJumpAppEnabledChannel.setMessageHandler(nil) + } + } +} +/// Generated protocol from Pigeon that represents a handler of messages from Flutter. +protocol NativeCommon { + func startActivityFromUri(intentUri: String) throws -> Bool + func getDeviceSdkInt() throws -> Int64 + func getDeviceCPUABI() throws -> String + func getVersionName() throws -> String + func getVersionCode() throws -> Int64 + func toast(msg: String) throws + func longToast(msg: String) throws +} + +/// Generated setup class from Pigeon to handle messages through the `binaryMessenger`. +class NativeCommonSetup { + static var codec: FlutterStandardMessageCodec { PigeonApiPigeonCodec.shared } + /// Sets up an instance of `NativeCommon` to handle messages through the `binaryMessenger`. + static func setUp(binaryMessenger: FlutterBinaryMessenger, api: NativeCommon?, messageChannelSuffix: String = "") { + let channelSuffix = messageChannelSuffix.count > 0 ? ".\(messageChannelSuffix)" : "" + let startActivityFromUriChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.openlist_mobile.NativeCommon.startActivityFromUri\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec) + if let api = api { + startActivityFromUriChannel.setMessageHandler { message, reply in + let args = message as! [Any?] + let intentUriArg = args[0] as! String + do { + let result = try api.startActivityFromUri(intentUri: intentUriArg) + reply(wrapResult(result)) + } catch { + reply(wrapError(error)) + } + } + } else { + startActivityFromUriChannel.setMessageHandler(nil) + } + let getDeviceSdkIntChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.openlist_mobile.NativeCommon.getDeviceSdkInt\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec) + if let api = api { + getDeviceSdkIntChannel.setMessageHandler { _, reply in + do { + let result = try api.getDeviceSdkInt() + reply(wrapResult(result)) + } catch { + reply(wrapError(error)) + } + } + } else { + getDeviceSdkIntChannel.setMessageHandler(nil) + } + let getDeviceCPUABIChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.openlist_mobile.NativeCommon.getDeviceCPUABI\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec) + if let api = api { + getDeviceCPUABIChannel.setMessageHandler { _, reply in + do { + let result = try api.getDeviceCPUABI() + reply(wrapResult(result)) + } catch { + reply(wrapError(error)) + } + } + } else { + getDeviceCPUABIChannel.setMessageHandler(nil) + } + let getVersionNameChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.openlist_mobile.NativeCommon.getVersionName\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec) + if let api = api { + getVersionNameChannel.setMessageHandler { _, reply in + do { + let result = try api.getVersionName() + reply(wrapResult(result)) + } catch { + reply(wrapError(error)) + } + } + } else { + getVersionNameChannel.setMessageHandler(nil) + } + let getVersionCodeChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.openlist_mobile.NativeCommon.getVersionCode\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec) + if let api = api { + getVersionCodeChannel.setMessageHandler { _, reply in + do { + let result = try api.getVersionCode() + reply(wrapResult(result)) + } catch { + reply(wrapError(error)) + } + } + } else { + getVersionCodeChannel.setMessageHandler(nil) + } + let toastChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.openlist_mobile.NativeCommon.toast\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec) + if let api = api { + toastChannel.setMessageHandler { message, reply in + let args = message as! [Any?] + let msgArg = args[0] as! String + do { + try api.toast(msg: msgArg) + reply(wrapResult(nil)) + } catch { + reply(wrapError(error)) + } + } + } else { + toastChannel.setMessageHandler(nil) + } + let longToastChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.openlist_mobile.NativeCommon.longToast\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec) + if let api = api { + longToastChannel.setMessageHandler { message, reply in + let args = message as! [Any?] + let msgArg = args[0] as! String + do { + try api.longToast(msg: msgArg) + reply(wrapResult(nil)) + } catch { + reply(wrapError(error)) + } + } + } else { + longToastChannel.setMessageHandler(nil) + } + } +} +/// Generated protocol from Pigeon that represents a handler of messages from Flutter. +protocol Android { + func addShortcut() throws + func startService() throws + func setAdminPwd(pwd: String) throws + func getOpenListHttpPort() throws -> Int64 + func isRunning() throws -> Bool + func getOpenListVersion() throws -> String +} + +/// Generated setup class from Pigeon to handle messages through the `binaryMessenger`. +class AndroidSetup { + static var codec: FlutterStandardMessageCodec { PigeonApiPigeonCodec.shared } + /// Sets up an instance of `Android` to handle messages through the `binaryMessenger`. + static func setUp(binaryMessenger: FlutterBinaryMessenger, api: Android?, messageChannelSuffix: String = "") { + let channelSuffix = messageChannelSuffix.count > 0 ? ".\(messageChannelSuffix)" : "" + let addShortcutChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.openlist_mobile.Android.addShortcut\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec) + if let api = api { + addShortcutChannel.setMessageHandler { _, reply in + do { + try api.addShortcut() + reply(wrapResult(nil)) + } catch { + reply(wrapError(error)) + } + } + } else { + addShortcutChannel.setMessageHandler(nil) + } + let startServiceChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.openlist_mobile.Android.startService\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec) + if let api = api { + startServiceChannel.setMessageHandler { _, reply in + do { + try api.startService() + reply(wrapResult(nil)) + } catch { + reply(wrapError(error)) + } + } + } else { + startServiceChannel.setMessageHandler(nil) + } + let setAdminPwdChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.openlist_mobile.Android.setAdminPwd\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec) + if let api = api { + setAdminPwdChannel.setMessageHandler { message, reply in + let args = message as! [Any?] + let pwdArg = args[0] as! String + do { + try api.setAdminPwd(pwd: pwdArg) + reply(wrapResult(nil)) + } catch { + reply(wrapError(error)) + } + } + } else { + setAdminPwdChannel.setMessageHandler(nil) + } + let getOpenListHttpPortChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.openlist_mobile.Android.getOpenListHttpPort\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec) + if let api = api { + getOpenListHttpPortChannel.setMessageHandler { _, reply in + do { + let result = try api.getOpenListHttpPort() + reply(wrapResult(result)) + } catch { + reply(wrapError(error)) + } + } + } else { + getOpenListHttpPortChannel.setMessageHandler(nil) + } + let isRunningChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.openlist_mobile.Android.isRunning\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec) + if let api = api { + isRunningChannel.setMessageHandler { _, reply in + do { + let result = try api.isRunning() + reply(wrapResult(result)) + } catch { + reply(wrapError(error)) + } + } + } else { + isRunningChannel.setMessageHandler(nil) + } + let getOpenListVersionChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.openlist_mobile.Android.getOpenListVersion\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec) + if let api = api { + getOpenListVersionChannel.setMessageHandler { _, reply in + do { + let result = try api.getOpenListVersion() + reply(wrapResult(result)) + } catch { + reply(wrapError(error)) + } + } + } else { + getOpenListVersionChannel.setMessageHandler(nil) + } + } +} +/// Generated protocol from Pigeon that represents Flutter messages that can be called from Swift. +protocol EventProtocol { + func onServiceStatusChanged(isRunning isRunningArg: Bool, completion: @escaping (Result) -> Void) + func onServerLog(level levelArg: Int64, time timeArg: String, log logArg: String, completion: @escaping (Result) -> Void) +} +class Event: EventProtocol { + private let binaryMessenger: FlutterBinaryMessenger + private let messageChannelSuffix: String + init(binaryMessenger: FlutterBinaryMessenger, messageChannelSuffix: String = "") { + self.binaryMessenger = binaryMessenger + self.messageChannelSuffix = messageChannelSuffix.count > 0 ? ".\(messageChannelSuffix)" : "" + } + var codec: PigeonApiPigeonCodec { + return PigeonApiPigeonCodec.shared + } + func onServiceStatusChanged(isRunning isRunningArg: Bool, completion: @escaping (Result) -> Void) { + let channelName: String = "dev.flutter.pigeon.openlist_mobile.Event.onServiceStatusChanged\(messageChannelSuffix)" + let channel = FlutterBasicMessageChannel(name: channelName, binaryMessenger: binaryMessenger, codec: codec) + channel.sendMessage([isRunningArg] as [Any?]) { response in + guard let listResponse = response as? [Any?] else { + completion(.failure(createConnectionError(withChannelName: channelName))) + return + } + if listResponse.count > 1 { + let code: String = listResponse[0] as! String + let message: String? = nilOrValue(listResponse[1]) + let details: String? = nilOrValue(listResponse[2]) + completion(.failure(PigeonError(code: code, message: message, details: details))) + } else { + completion(.success(())) + } + } + } + func onServerLog(level levelArg: Int64, time timeArg: String, log logArg: String, completion: @escaping (Result) -> Void) { + let channelName: String = "dev.flutter.pigeon.openlist_mobile.Event.onServerLog\(messageChannelSuffix)" + let channel = FlutterBasicMessageChannel(name: channelName, binaryMessenger: binaryMessenger, codec: codec) + channel.sendMessage([levelArg, timeArg, logArg] as [Any?]) { response in + guard let listResponse = response as? [Any?] else { + completion(.failure(createConnectionError(withChannelName: channelName))) + return + } + if listResponse.count > 1 { + let code: String = listResponse[0] as! String + let message: String? = nilOrValue(listResponse[1]) + let details: String? = nilOrValue(listResponse[2]) + completion(.failure(PigeonError(code: code, message: message, details: details))) + } else { + completion(.success(())) + } + } + } +} diff --git a/lib/generated_api.dart b/lib/generated_api.dart index e579c72..0ce4219 100644 --- a/lib/generated_api.dart +++ b/lib/generated_api.dart @@ -1,4 +1,4 @@ -// Autogenerated from Pigeon (v16.0.0), do not edit directly. +// Autogenerated from Pigeon (v26.0.2), do not edit directly. // See also: https://pub.dev/packages/pigeon // ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import, unnecessary_parenthesis, prefer_null_aware_operators, omit_local_variable_types, unused_shown_name, unnecessary_import, no_leading_underscores_for_local_identifiers @@ -25,59 +25,86 @@ List wrapResponse({Object? result, PlatformException? error, bool empty return [error.code, error.message, error.details]; } + +class _PigeonCodec extends StandardMessageCodec { + const _PigeonCodec(); + @override + void writeValue(WriteBuffer buffer, Object? value) { + if (value is int) { + buffer.putUint8(4); + buffer.putInt64(value); + } else { + super.writeValue(buffer, value); + } + } + + @override + Object? readValueOfType(int type, ReadBuffer buffer) { + switch (type) { + default: + return super.readValueOfType(type, buffer); + } + } +} + class AppConfig { /// Constructor for [AppConfig]. The [binaryMessenger] named argument is /// available for dependency injection. If it is left null, the default /// BinaryMessenger will be used which routes to the host platform. - AppConfig({BinaryMessenger? binaryMessenger}) - : __pigeon_binaryMessenger = binaryMessenger; - final BinaryMessenger? __pigeon_binaryMessenger; + AppConfig({BinaryMessenger? binaryMessenger, String messageChannelSuffix = ''}) + : pigeonVar_binaryMessenger = binaryMessenger, + pigeonVar_messageChannelSuffix = messageChannelSuffix.isNotEmpty ? '.$messageChannelSuffix' : ''; + final BinaryMessenger? pigeonVar_binaryMessenger; - static const MessageCodec pigeonChannelCodec = StandardMessageCodec(); + static const MessageCodec pigeonChannelCodec = _PigeonCodec(); + + final String pigeonVar_messageChannelSuffix; Future isWakeLockEnabled() async { - const String __pigeon_channelName = 'dev.flutter.pigeon.openlist_mobile.AppConfig.isWakeLockEnabled'; - final BasicMessageChannel __pigeon_channel = BasicMessageChannel( - __pigeon_channelName, + final String pigeonVar_channelName = 'dev.flutter.pigeon.openlist_mobile.AppConfig.isWakeLockEnabled$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + pigeonVar_channelName, pigeonChannelCodec, - binaryMessenger: __pigeon_binaryMessenger, + binaryMessenger: pigeonVar_binaryMessenger, ); - final List? __pigeon_replyList = - await __pigeon_channel.send(null) as List?; - if (__pigeon_replyList == null) { - throw _createConnectionError(__pigeon_channelName); - } else if (__pigeon_replyList.length > 1) { + final Future pigeonVar_sendFuture = pigeonVar_channel.send(null); + final List? pigeonVar_replyList = + await pigeonVar_sendFuture as List?; + if (pigeonVar_replyList == null) { + throw _createConnectionError(pigeonVar_channelName); + } else if (pigeonVar_replyList.length > 1) { throw PlatformException( - code: __pigeon_replyList[0]! as String, - message: __pigeon_replyList[1] as String?, - details: __pigeon_replyList[2], + code: pigeonVar_replyList[0]! as String, + message: pigeonVar_replyList[1] as String?, + details: pigeonVar_replyList[2], ); - } else if (__pigeon_replyList[0] == null) { + } else if (pigeonVar_replyList[0] == null) { throw PlatformException( code: 'null-error', message: 'Host platform returned null value for non-null return value.', ); } else { - return (__pigeon_replyList[0] as bool?)!; + return (pigeonVar_replyList[0] as bool?)!; } } Future setWakeLockEnabled(bool enabled) async { - const String __pigeon_channelName = 'dev.flutter.pigeon.openlist_mobile.AppConfig.setWakeLockEnabled'; - final BasicMessageChannel __pigeon_channel = BasicMessageChannel( - __pigeon_channelName, + final String pigeonVar_channelName = 'dev.flutter.pigeon.openlist_mobile.AppConfig.setWakeLockEnabled$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + pigeonVar_channelName, pigeonChannelCodec, - binaryMessenger: __pigeon_binaryMessenger, + binaryMessenger: pigeonVar_binaryMessenger, ); - final List? __pigeon_replyList = - await __pigeon_channel.send([enabled]) as List?; - if (__pigeon_replyList == null) { - throw _createConnectionError(__pigeon_channelName); - } else if (__pigeon_replyList.length > 1) { + final Future pigeonVar_sendFuture = pigeonVar_channel.send([enabled]); + final List? pigeonVar_replyList = + await pigeonVar_sendFuture as List?; + if (pigeonVar_replyList == null) { + throw _createConnectionError(pigeonVar_channelName); + } else if (pigeonVar_replyList.length > 1) { throw PlatformException( - code: __pigeon_replyList[0]! as String, - message: __pigeon_replyList[1] as String?, - details: __pigeon_replyList[2], + code: pigeonVar_replyList[0]! as String, + message: pigeonVar_replyList[1] as String?, + details: pigeonVar_replyList[2], ); } else { return; @@ -85,48 +112,50 @@ class AppConfig { } Future isStartAtBootEnabled() async { - const String __pigeon_channelName = 'dev.flutter.pigeon.openlist_mobile.AppConfig.isStartAtBootEnabled'; - final BasicMessageChannel __pigeon_channel = BasicMessageChannel( - __pigeon_channelName, + final String pigeonVar_channelName = 'dev.flutter.pigeon.openlist_mobile.AppConfig.isStartAtBootEnabled$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + pigeonVar_channelName, pigeonChannelCodec, - binaryMessenger: __pigeon_binaryMessenger, + binaryMessenger: pigeonVar_binaryMessenger, ); - final List? __pigeon_replyList = - await __pigeon_channel.send(null) as List?; - if (__pigeon_replyList == null) { - throw _createConnectionError(__pigeon_channelName); - } else if (__pigeon_replyList.length > 1) { + final Future pigeonVar_sendFuture = pigeonVar_channel.send(null); + final List? pigeonVar_replyList = + await pigeonVar_sendFuture as List?; + if (pigeonVar_replyList == null) { + throw _createConnectionError(pigeonVar_channelName); + } else if (pigeonVar_replyList.length > 1) { throw PlatformException( - code: __pigeon_replyList[0]! as String, - message: __pigeon_replyList[1] as String?, - details: __pigeon_replyList[2], + code: pigeonVar_replyList[0]! as String, + message: pigeonVar_replyList[1] as String?, + details: pigeonVar_replyList[2], ); - } else if (__pigeon_replyList[0] == null) { + } else if (pigeonVar_replyList[0] == null) { throw PlatformException( code: 'null-error', message: 'Host platform returned null value for non-null return value.', ); } else { - return (__pigeon_replyList[0] as bool?)!; + return (pigeonVar_replyList[0] as bool?)!; } } Future setStartAtBootEnabled(bool enabled) async { - const String __pigeon_channelName = 'dev.flutter.pigeon.openlist_mobile.AppConfig.setStartAtBootEnabled'; - final BasicMessageChannel __pigeon_channel = BasicMessageChannel( - __pigeon_channelName, + final String pigeonVar_channelName = 'dev.flutter.pigeon.openlist_mobile.AppConfig.setStartAtBootEnabled$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + pigeonVar_channelName, pigeonChannelCodec, - binaryMessenger: __pigeon_binaryMessenger, + binaryMessenger: pigeonVar_binaryMessenger, ); - final List? __pigeon_replyList = - await __pigeon_channel.send([enabled]) as List?; - if (__pigeon_replyList == null) { - throw _createConnectionError(__pigeon_channelName); - } else if (__pigeon_replyList.length > 1) { + final Future pigeonVar_sendFuture = pigeonVar_channel.send([enabled]); + final List? pigeonVar_replyList = + await pigeonVar_sendFuture as List?; + if (pigeonVar_replyList == null) { + throw _createConnectionError(pigeonVar_channelName); + } else if (pigeonVar_replyList.length > 1) { throw PlatformException( - code: __pigeon_replyList[0]! as String, - message: __pigeon_replyList[1] as String?, - details: __pigeon_replyList[2], + code: pigeonVar_replyList[0]! as String, + message: pigeonVar_replyList[1] as String?, + details: pigeonVar_replyList[2], ); } else { return; @@ -134,48 +163,50 @@ class AppConfig { } Future isAutoCheckUpdateEnabled() async { - const String __pigeon_channelName = 'dev.flutter.pigeon.openlist_mobile.AppConfig.isAutoCheckUpdateEnabled'; - final BasicMessageChannel __pigeon_channel = BasicMessageChannel( - __pigeon_channelName, + final String pigeonVar_channelName = 'dev.flutter.pigeon.openlist_mobile.AppConfig.isAutoCheckUpdateEnabled$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + pigeonVar_channelName, pigeonChannelCodec, - binaryMessenger: __pigeon_binaryMessenger, + binaryMessenger: pigeonVar_binaryMessenger, ); - final List? __pigeon_replyList = - await __pigeon_channel.send(null) as List?; - if (__pigeon_replyList == null) { - throw _createConnectionError(__pigeon_channelName); - } else if (__pigeon_replyList.length > 1) { + final Future pigeonVar_sendFuture = pigeonVar_channel.send(null); + final List? pigeonVar_replyList = + await pigeonVar_sendFuture as List?; + if (pigeonVar_replyList == null) { + throw _createConnectionError(pigeonVar_channelName); + } else if (pigeonVar_replyList.length > 1) { throw PlatformException( - code: __pigeon_replyList[0]! as String, - message: __pigeon_replyList[1] as String?, - details: __pigeon_replyList[2], + code: pigeonVar_replyList[0]! as String, + message: pigeonVar_replyList[1] as String?, + details: pigeonVar_replyList[2], ); - } else if (__pigeon_replyList[0] == null) { + } else if (pigeonVar_replyList[0] == null) { throw PlatformException( code: 'null-error', message: 'Host platform returned null value for non-null return value.', ); } else { - return (__pigeon_replyList[0] as bool?)!; + return (pigeonVar_replyList[0] as bool?)!; } } Future setAutoCheckUpdateEnabled(bool enabled) async { - const String __pigeon_channelName = 'dev.flutter.pigeon.openlist_mobile.AppConfig.setAutoCheckUpdateEnabled'; - final BasicMessageChannel __pigeon_channel = BasicMessageChannel( - __pigeon_channelName, + final String pigeonVar_channelName = 'dev.flutter.pigeon.openlist_mobile.AppConfig.setAutoCheckUpdateEnabled$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + pigeonVar_channelName, pigeonChannelCodec, - binaryMessenger: __pigeon_binaryMessenger, + binaryMessenger: pigeonVar_binaryMessenger, ); - final List? __pigeon_replyList = - await __pigeon_channel.send([enabled]) as List?; - if (__pigeon_replyList == null) { - throw _createConnectionError(__pigeon_channelName); - } else if (__pigeon_replyList.length > 1) { + final Future pigeonVar_sendFuture = pigeonVar_channel.send([enabled]); + final List? pigeonVar_replyList = + await pigeonVar_sendFuture as List?; + if (pigeonVar_replyList == null) { + throw _createConnectionError(pigeonVar_channelName); + } else if (pigeonVar_replyList.length > 1) { throw PlatformException( - code: __pigeon_replyList[0]! as String, - message: __pigeon_replyList[1] as String?, - details: __pigeon_replyList[2], + code: pigeonVar_replyList[0]! as String, + message: pigeonVar_replyList[1] as String?, + details: pigeonVar_replyList[2], ); } else { return; @@ -183,48 +214,50 @@ class AppConfig { } Future isAutoOpenWebPageEnabled() async { - const String __pigeon_channelName = 'dev.flutter.pigeon.openlist_mobile.AppConfig.isAutoOpenWebPageEnabled'; - final BasicMessageChannel __pigeon_channel = BasicMessageChannel( - __pigeon_channelName, + final String pigeonVar_channelName = 'dev.flutter.pigeon.openlist_mobile.AppConfig.isAutoOpenWebPageEnabled$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + pigeonVar_channelName, pigeonChannelCodec, - binaryMessenger: __pigeon_binaryMessenger, + binaryMessenger: pigeonVar_binaryMessenger, ); - final List? __pigeon_replyList = - await __pigeon_channel.send(null) as List?; - if (__pigeon_replyList == null) { - throw _createConnectionError(__pigeon_channelName); - } else if (__pigeon_replyList.length > 1) { + final Future pigeonVar_sendFuture = pigeonVar_channel.send(null); + final List? pigeonVar_replyList = + await pigeonVar_sendFuture as List?; + if (pigeonVar_replyList == null) { + throw _createConnectionError(pigeonVar_channelName); + } else if (pigeonVar_replyList.length > 1) { throw PlatformException( - code: __pigeon_replyList[0]! as String, - message: __pigeon_replyList[1] as String?, - details: __pigeon_replyList[2], + code: pigeonVar_replyList[0]! as String, + message: pigeonVar_replyList[1] as String?, + details: pigeonVar_replyList[2], ); - } else if (__pigeon_replyList[0] == null) { + } else if (pigeonVar_replyList[0] == null) { throw PlatformException( code: 'null-error', message: 'Host platform returned null value for non-null return value.', ); } else { - return (__pigeon_replyList[0] as bool?)!; + return (pigeonVar_replyList[0] as bool?)!; } } Future setAutoOpenWebPageEnabled(bool enabled) async { - const String __pigeon_channelName = 'dev.flutter.pigeon.openlist_mobile.AppConfig.setAutoOpenWebPageEnabled'; - final BasicMessageChannel __pigeon_channel = BasicMessageChannel( - __pigeon_channelName, + final String pigeonVar_channelName = 'dev.flutter.pigeon.openlist_mobile.AppConfig.setAutoOpenWebPageEnabled$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + pigeonVar_channelName, pigeonChannelCodec, - binaryMessenger: __pigeon_binaryMessenger, + binaryMessenger: pigeonVar_binaryMessenger, ); - final List? __pigeon_replyList = - await __pigeon_channel.send([enabled]) as List?; - if (__pigeon_replyList == null) { - throw _createConnectionError(__pigeon_channelName); - } else if (__pigeon_replyList.length > 1) { + final Future pigeonVar_sendFuture = pigeonVar_channel.send([enabled]); + final List? pigeonVar_replyList = + await pigeonVar_sendFuture as List?; + if (pigeonVar_replyList == null) { + throw _createConnectionError(pigeonVar_channelName); + } else if (pigeonVar_replyList.length > 1) { throw PlatformException( - code: __pigeon_replyList[0]! as String, - message: __pigeon_replyList[1] as String?, - details: __pigeon_replyList[2], + code: pigeonVar_replyList[0]! as String, + message: pigeonVar_replyList[1] as String?, + details: pigeonVar_replyList[2], ); } else { return; @@ -232,48 +265,50 @@ class AppConfig { } Future getDataDir() async { - const String __pigeon_channelName = 'dev.flutter.pigeon.openlist_mobile.AppConfig.getDataDir'; - final BasicMessageChannel __pigeon_channel = BasicMessageChannel( - __pigeon_channelName, + final String pigeonVar_channelName = 'dev.flutter.pigeon.openlist_mobile.AppConfig.getDataDir$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + pigeonVar_channelName, pigeonChannelCodec, - binaryMessenger: __pigeon_binaryMessenger, + binaryMessenger: pigeonVar_binaryMessenger, ); - final List? __pigeon_replyList = - await __pigeon_channel.send(null) as List?; - if (__pigeon_replyList == null) { - throw _createConnectionError(__pigeon_channelName); - } else if (__pigeon_replyList.length > 1) { + final Future pigeonVar_sendFuture = pigeonVar_channel.send(null); + final List? pigeonVar_replyList = + await pigeonVar_sendFuture as List?; + if (pigeonVar_replyList == null) { + throw _createConnectionError(pigeonVar_channelName); + } else if (pigeonVar_replyList.length > 1) { throw PlatformException( - code: __pigeon_replyList[0]! as String, - message: __pigeon_replyList[1] as String?, - details: __pigeon_replyList[2], + code: pigeonVar_replyList[0]! as String, + message: pigeonVar_replyList[1] as String?, + details: pigeonVar_replyList[2], ); - } else if (__pigeon_replyList[0] == null) { + } else if (pigeonVar_replyList[0] == null) { throw PlatformException( code: 'null-error', message: 'Host platform returned null value for non-null return value.', ); } else { - return (__pigeon_replyList[0] as String?)!; + return (pigeonVar_replyList[0] as String?)!; } } Future setDataDir(String dir) async { - const String __pigeon_channelName = 'dev.flutter.pigeon.openlist_mobile.AppConfig.setDataDir'; - final BasicMessageChannel __pigeon_channel = BasicMessageChannel( - __pigeon_channelName, + final String pigeonVar_channelName = 'dev.flutter.pigeon.openlist_mobile.AppConfig.setDataDir$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + pigeonVar_channelName, pigeonChannelCodec, - binaryMessenger: __pigeon_binaryMessenger, + binaryMessenger: pigeonVar_binaryMessenger, ); - final List? __pigeon_replyList = - await __pigeon_channel.send([dir]) as List?; - if (__pigeon_replyList == null) { - throw _createConnectionError(__pigeon_channelName); - } else if (__pigeon_replyList.length > 1) { + final Future pigeonVar_sendFuture = pigeonVar_channel.send([dir]); + final List? pigeonVar_replyList = + await pigeonVar_sendFuture as List?; + if (pigeonVar_replyList == null) { + throw _createConnectionError(pigeonVar_channelName); + } else if (pigeonVar_replyList.length > 1) { throw PlatformException( - code: __pigeon_replyList[0]! as String, - message: __pigeon_replyList[1] as String?, - details: __pigeon_replyList[2], + code: pigeonVar_replyList[0]! as String, + message: pigeonVar_replyList[1] as String?, + details: pigeonVar_replyList[2], ); } else { return; @@ -281,48 +316,50 @@ class AppConfig { } Future isSilentJumpAppEnabled() async { - const String __pigeon_channelName = 'dev.flutter.pigeon.openlist_mobile.AppConfig.isSilentJumpAppEnabled'; - final BasicMessageChannel __pigeon_channel = BasicMessageChannel( - __pigeon_channelName, + final String pigeonVar_channelName = 'dev.flutter.pigeon.openlist_mobile.AppConfig.isSilentJumpAppEnabled$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + pigeonVar_channelName, pigeonChannelCodec, - binaryMessenger: __pigeon_binaryMessenger, + binaryMessenger: pigeonVar_binaryMessenger, ); - final List? __pigeon_replyList = - await __pigeon_channel.send(null) as List?; - if (__pigeon_replyList == null) { - throw _createConnectionError(__pigeon_channelName); - } else if (__pigeon_replyList.length > 1) { + final Future pigeonVar_sendFuture = pigeonVar_channel.send(null); + final List? pigeonVar_replyList = + await pigeonVar_sendFuture as List?; + if (pigeonVar_replyList == null) { + throw _createConnectionError(pigeonVar_channelName); + } else if (pigeonVar_replyList.length > 1) { throw PlatformException( - code: __pigeon_replyList[0]! as String, - message: __pigeon_replyList[1] as String?, - details: __pigeon_replyList[2], + code: pigeonVar_replyList[0]! as String, + message: pigeonVar_replyList[1] as String?, + details: pigeonVar_replyList[2], ); - } else if (__pigeon_replyList[0] == null) { + } else if (pigeonVar_replyList[0] == null) { throw PlatformException( code: 'null-error', message: 'Host platform returned null value for non-null return value.', ); } else { - return (__pigeon_replyList[0] as bool?)!; + return (pigeonVar_replyList[0] as bool?)!; } } Future setSilentJumpAppEnabled(bool enabled) async { - const String __pigeon_channelName = 'dev.flutter.pigeon.openlist_mobile.AppConfig.setSilentJumpAppEnabled'; - final BasicMessageChannel __pigeon_channel = BasicMessageChannel( - __pigeon_channelName, + final String pigeonVar_channelName = 'dev.flutter.pigeon.openlist_mobile.AppConfig.setSilentJumpAppEnabled$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + pigeonVar_channelName, pigeonChannelCodec, - binaryMessenger: __pigeon_binaryMessenger, + binaryMessenger: pigeonVar_binaryMessenger, ); - final List? __pigeon_replyList = - await __pigeon_channel.send([enabled]) as List?; - if (__pigeon_replyList == null) { - throw _createConnectionError(__pigeon_channelName); - } else if (__pigeon_replyList.length > 1) { + final Future pigeonVar_sendFuture = pigeonVar_channel.send([enabled]); + final List? pigeonVar_replyList = + await pigeonVar_sendFuture as List?; + if (pigeonVar_replyList == null) { + throw _createConnectionError(pigeonVar_channelName); + } else if (pigeonVar_replyList.length > 1) { throw PlatformException( - code: __pigeon_replyList[0]! as String, - message: __pigeon_replyList[1] as String?, - details: __pigeon_replyList[2], + code: pigeonVar_replyList[0]! as String, + message: pigeonVar_replyList[1] as String?, + details: pigeonVar_replyList[2], ); } else { return; @@ -334,163 +371,172 @@ class NativeCommon { /// Constructor for [NativeCommon]. The [binaryMessenger] named argument is /// available for dependency injection. If it is left null, the default /// BinaryMessenger will be used which routes to the host platform. - NativeCommon({BinaryMessenger? binaryMessenger}) - : __pigeon_binaryMessenger = binaryMessenger; - final BinaryMessenger? __pigeon_binaryMessenger; + NativeCommon({BinaryMessenger? binaryMessenger, String messageChannelSuffix = ''}) + : pigeonVar_binaryMessenger = binaryMessenger, + pigeonVar_messageChannelSuffix = messageChannelSuffix.isNotEmpty ? '.$messageChannelSuffix' : ''; + final BinaryMessenger? pigeonVar_binaryMessenger; + + static const MessageCodec pigeonChannelCodec = _PigeonCodec(); - static const MessageCodec pigeonChannelCodec = StandardMessageCodec(); + final String pigeonVar_messageChannelSuffix; Future startActivityFromUri(String intentUri) async { - const String __pigeon_channelName = 'dev.flutter.pigeon.openlist_mobile.NativeCommon.startActivityFromUri'; - final BasicMessageChannel __pigeon_channel = BasicMessageChannel( - __pigeon_channelName, + final String pigeonVar_channelName = 'dev.flutter.pigeon.openlist_mobile.NativeCommon.startActivityFromUri$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + pigeonVar_channelName, pigeonChannelCodec, - binaryMessenger: __pigeon_binaryMessenger, + binaryMessenger: pigeonVar_binaryMessenger, ); - final List? __pigeon_replyList = - await __pigeon_channel.send([intentUri]) as List?; - if (__pigeon_replyList == null) { - throw _createConnectionError(__pigeon_channelName); - } else if (__pigeon_replyList.length > 1) { + final Future pigeonVar_sendFuture = pigeonVar_channel.send([intentUri]); + final List? pigeonVar_replyList = + await pigeonVar_sendFuture as List?; + if (pigeonVar_replyList == null) { + throw _createConnectionError(pigeonVar_channelName); + } else if (pigeonVar_replyList.length > 1) { throw PlatformException( - code: __pigeon_replyList[0]! as String, - message: __pigeon_replyList[1] as String?, - details: __pigeon_replyList[2], + code: pigeonVar_replyList[0]! as String, + message: pigeonVar_replyList[1] as String?, + details: pigeonVar_replyList[2], ); - } else if (__pigeon_replyList[0] == null) { + } else if (pigeonVar_replyList[0] == null) { throw PlatformException( code: 'null-error', message: 'Host platform returned null value for non-null return value.', ); } else { - return (__pigeon_replyList[0] as bool?)!; + return (pigeonVar_replyList[0] as bool?)!; } } Future getDeviceSdkInt() async { - const String __pigeon_channelName = 'dev.flutter.pigeon.openlist_mobile.NativeCommon.getDeviceSdkInt'; - final BasicMessageChannel __pigeon_channel = BasicMessageChannel( - __pigeon_channelName, + final String pigeonVar_channelName = 'dev.flutter.pigeon.openlist_mobile.NativeCommon.getDeviceSdkInt$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + pigeonVar_channelName, pigeonChannelCodec, - binaryMessenger: __pigeon_binaryMessenger, + binaryMessenger: pigeonVar_binaryMessenger, ); - final List? __pigeon_replyList = - await __pigeon_channel.send(null) as List?; - if (__pigeon_replyList == null) { - throw _createConnectionError(__pigeon_channelName); - } else if (__pigeon_replyList.length > 1) { + final Future pigeonVar_sendFuture = pigeonVar_channel.send(null); + final List? pigeonVar_replyList = + await pigeonVar_sendFuture as List?; + if (pigeonVar_replyList == null) { + throw _createConnectionError(pigeonVar_channelName); + } else if (pigeonVar_replyList.length > 1) { throw PlatformException( - code: __pigeon_replyList[0]! as String, - message: __pigeon_replyList[1] as String?, - details: __pigeon_replyList[2], + code: pigeonVar_replyList[0]! as String, + message: pigeonVar_replyList[1] as String?, + details: pigeonVar_replyList[2], ); - } else if (__pigeon_replyList[0] == null) { + } else if (pigeonVar_replyList[0] == null) { throw PlatformException( code: 'null-error', message: 'Host platform returned null value for non-null return value.', ); } else { - return (__pigeon_replyList[0] as int?)!; + return (pigeonVar_replyList[0] as int?)!; } } Future getDeviceCPUABI() async { - const String __pigeon_channelName = 'dev.flutter.pigeon.openlist_mobile.NativeCommon.getDeviceCPUABI'; - final BasicMessageChannel __pigeon_channel = BasicMessageChannel( - __pigeon_channelName, + final String pigeonVar_channelName = 'dev.flutter.pigeon.openlist_mobile.NativeCommon.getDeviceCPUABI$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + pigeonVar_channelName, pigeonChannelCodec, - binaryMessenger: __pigeon_binaryMessenger, + binaryMessenger: pigeonVar_binaryMessenger, ); - final List? __pigeon_replyList = - await __pigeon_channel.send(null) as List?; - if (__pigeon_replyList == null) { - throw _createConnectionError(__pigeon_channelName); - } else if (__pigeon_replyList.length > 1) { + final Future pigeonVar_sendFuture = pigeonVar_channel.send(null); + final List? pigeonVar_replyList = + await pigeonVar_sendFuture as List?; + if (pigeonVar_replyList == null) { + throw _createConnectionError(pigeonVar_channelName); + } else if (pigeonVar_replyList.length > 1) { throw PlatformException( - code: __pigeon_replyList[0]! as String, - message: __pigeon_replyList[1] as String?, - details: __pigeon_replyList[2], + code: pigeonVar_replyList[0]! as String, + message: pigeonVar_replyList[1] as String?, + details: pigeonVar_replyList[2], ); - } else if (__pigeon_replyList[0] == null) { + } else if (pigeonVar_replyList[0] == null) { throw PlatformException( code: 'null-error', message: 'Host platform returned null value for non-null return value.', ); } else { - return (__pigeon_replyList[0] as String?)!; + return (pigeonVar_replyList[0] as String?)!; } } Future getVersionName() async { - const String __pigeon_channelName = 'dev.flutter.pigeon.openlist_mobile.NativeCommon.getVersionName'; - final BasicMessageChannel __pigeon_channel = BasicMessageChannel( - __pigeon_channelName, + final String pigeonVar_channelName = 'dev.flutter.pigeon.openlist_mobile.NativeCommon.getVersionName$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + pigeonVar_channelName, pigeonChannelCodec, - binaryMessenger: __pigeon_binaryMessenger, + binaryMessenger: pigeonVar_binaryMessenger, ); - final List? __pigeon_replyList = - await __pigeon_channel.send(null) as List?; - if (__pigeon_replyList == null) { - throw _createConnectionError(__pigeon_channelName); - } else if (__pigeon_replyList.length > 1) { + final Future pigeonVar_sendFuture = pigeonVar_channel.send(null); + final List? pigeonVar_replyList = + await pigeonVar_sendFuture as List?; + if (pigeonVar_replyList == null) { + throw _createConnectionError(pigeonVar_channelName); + } else if (pigeonVar_replyList.length > 1) { throw PlatformException( - code: __pigeon_replyList[0]! as String, - message: __pigeon_replyList[1] as String?, - details: __pigeon_replyList[2], + code: pigeonVar_replyList[0]! as String, + message: pigeonVar_replyList[1] as String?, + details: pigeonVar_replyList[2], ); - } else if (__pigeon_replyList[0] == null) { + } else if (pigeonVar_replyList[0] == null) { throw PlatformException( code: 'null-error', message: 'Host platform returned null value for non-null return value.', ); } else { - return (__pigeon_replyList[0] as String?)!; + return (pigeonVar_replyList[0] as String?)!; } } Future getVersionCode() async { - const String __pigeon_channelName = 'dev.flutter.pigeon.openlist_mobile.NativeCommon.getVersionCode'; - final BasicMessageChannel __pigeon_channel = BasicMessageChannel( - __pigeon_channelName, + final String pigeonVar_channelName = 'dev.flutter.pigeon.openlist_mobile.NativeCommon.getVersionCode$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + pigeonVar_channelName, pigeonChannelCodec, - binaryMessenger: __pigeon_binaryMessenger, + binaryMessenger: pigeonVar_binaryMessenger, ); - final List? __pigeon_replyList = - await __pigeon_channel.send(null) as List?; - if (__pigeon_replyList == null) { - throw _createConnectionError(__pigeon_channelName); - } else if (__pigeon_replyList.length > 1) { + final Future pigeonVar_sendFuture = pigeonVar_channel.send(null); + final List? pigeonVar_replyList = + await pigeonVar_sendFuture as List?; + if (pigeonVar_replyList == null) { + throw _createConnectionError(pigeonVar_channelName); + } else if (pigeonVar_replyList.length > 1) { throw PlatformException( - code: __pigeon_replyList[0]! as String, - message: __pigeon_replyList[1] as String?, - details: __pigeon_replyList[2], + code: pigeonVar_replyList[0]! as String, + message: pigeonVar_replyList[1] as String?, + details: pigeonVar_replyList[2], ); - } else if (__pigeon_replyList[0] == null) { + } else if (pigeonVar_replyList[0] == null) { throw PlatformException( code: 'null-error', message: 'Host platform returned null value for non-null return value.', ); } else { - return (__pigeon_replyList[0] as int?)!; + return (pigeonVar_replyList[0] as int?)!; } } Future toast(String msg) async { - const String __pigeon_channelName = 'dev.flutter.pigeon.openlist_mobile.NativeCommon.toast'; - final BasicMessageChannel __pigeon_channel = BasicMessageChannel( - __pigeon_channelName, + final String pigeonVar_channelName = 'dev.flutter.pigeon.openlist_mobile.NativeCommon.toast$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + pigeonVar_channelName, pigeonChannelCodec, - binaryMessenger: __pigeon_binaryMessenger, + binaryMessenger: pigeonVar_binaryMessenger, ); - final List? __pigeon_replyList = - await __pigeon_channel.send([msg]) as List?; - if (__pigeon_replyList == null) { - throw _createConnectionError(__pigeon_channelName); - } else if (__pigeon_replyList.length > 1) { + final Future pigeonVar_sendFuture = pigeonVar_channel.send([msg]); + final List? pigeonVar_replyList = + await pigeonVar_sendFuture as List?; + if (pigeonVar_replyList == null) { + throw _createConnectionError(pigeonVar_channelName); + } else if (pigeonVar_replyList.length > 1) { throw PlatformException( - code: __pigeon_replyList[0]! as String, - message: __pigeon_replyList[1] as String?, - details: __pigeon_replyList[2], + code: pigeonVar_replyList[0]! as String, + message: pigeonVar_replyList[1] as String?, + details: pigeonVar_replyList[2], ); } else { return; @@ -498,21 +544,22 @@ class NativeCommon { } Future longToast(String msg) async { - const String __pigeon_channelName = 'dev.flutter.pigeon.openlist_mobile.NativeCommon.longToast'; - final BasicMessageChannel __pigeon_channel = BasicMessageChannel( - __pigeon_channelName, + final String pigeonVar_channelName = 'dev.flutter.pigeon.openlist_mobile.NativeCommon.longToast$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + pigeonVar_channelName, pigeonChannelCodec, - binaryMessenger: __pigeon_binaryMessenger, + binaryMessenger: pigeonVar_binaryMessenger, ); - final List? __pigeon_replyList = - await __pigeon_channel.send([msg]) as List?; - if (__pigeon_replyList == null) { - throw _createConnectionError(__pigeon_channelName); - } else if (__pigeon_replyList.length > 1) { + final Future pigeonVar_sendFuture = pigeonVar_channel.send([msg]); + final List? pigeonVar_replyList = + await pigeonVar_sendFuture as List?; + if (pigeonVar_replyList == null) { + throw _createConnectionError(pigeonVar_channelName); + } else if (pigeonVar_replyList.length > 1) { throw PlatformException( - code: __pigeon_replyList[0]! as String, - message: __pigeon_replyList[1] as String?, - details: __pigeon_replyList[2], + code: pigeonVar_replyList[0]! as String, + message: pigeonVar_replyList[1] as String?, + details: pigeonVar_replyList[2], ); } else { return; @@ -524,28 +571,32 @@ class Android { /// Constructor for [Android]. The [binaryMessenger] named argument is /// available for dependency injection. If it is left null, the default /// BinaryMessenger will be used which routes to the host platform. - Android({BinaryMessenger? binaryMessenger}) - : __pigeon_binaryMessenger = binaryMessenger; - final BinaryMessenger? __pigeon_binaryMessenger; + Android({BinaryMessenger? binaryMessenger, String messageChannelSuffix = ''}) + : pigeonVar_binaryMessenger = binaryMessenger, + pigeonVar_messageChannelSuffix = messageChannelSuffix.isNotEmpty ? '.$messageChannelSuffix' : ''; + final BinaryMessenger? pigeonVar_binaryMessenger; + + static const MessageCodec pigeonChannelCodec = _PigeonCodec(); - static const MessageCodec pigeonChannelCodec = StandardMessageCodec(); + final String pigeonVar_messageChannelSuffix; Future addShortcut() async { - const String __pigeon_channelName = 'dev.flutter.pigeon.openlist_mobile.Android.addShortcut'; - final BasicMessageChannel __pigeon_channel = BasicMessageChannel( - __pigeon_channelName, + final String pigeonVar_channelName = 'dev.flutter.pigeon.openlist_mobile.Android.addShortcut$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + pigeonVar_channelName, pigeonChannelCodec, - binaryMessenger: __pigeon_binaryMessenger, + binaryMessenger: pigeonVar_binaryMessenger, ); - final List? __pigeon_replyList = - await __pigeon_channel.send(null) as List?; - if (__pigeon_replyList == null) { - throw _createConnectionError(__pigeon_channelName); - } else if (__pigeon_replyList.length > 1) { + final Future pigeonVar_sendFuture = pigeonVar_channel.send(null); + final List? pigeonVar_replyList = + await pigeonVar_sendFuture as List?; + if (pigeonVar_replyList == null) { + throw _createConnectionError(pigeonVar_channelName); + } else if (pigeonVar_replyList.length > 1) { throw PlatformException( - code: __pigeon_replyList[0]! as String, - message: __pigeon_replyList[1] as String?, - details: __pigeon_replyList[2], + code: pigeonVar_replyList[0]! as String, + message: pigeonVar_replyList[1] as String?, + details: pigeonVar_replyList[2], ); } else { return; @@ -553,21 +604,22 @@ class Android { } Future startService() async { - const String __pigeon_channelName = 'dev.flutter.pigeon.openlist_mobile.Android.startService'; - final BasicMessageChannel __pigeon_channel = BasicMessageChannel( - __pigeon_channelName, + final String pigeonVar_channelName = 'dev.flutter.pigeon.openlist_mobile.Android.startService$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + pigeonVar_channelName, pigeonChannelCodec, - binaryMessenger: __pigeon_binaryMessenger, + binaryMessenger: pigeonVar_binaryMessenger, ); - final List? __pigeon_replyList = - await __pigeon_channel.send(null) as List?; - if (__pigeon_replyList == null) { - throw _createConnectionError(__pigeon_channelName); - } else if (__pigeon_replyList.length > 1) { + final Future pigeonVar_sendFuture = pigeonVar_channel.send(null); + final List? pigeonVar_replyList = + await pigeonVar_sendFuture as List?; + if (pigeonVar_replyList == null) { + throw _createConnectionError(pigeonVar_channelName); + } else if (pigeonVar_replyList.length > 1) { throw PlatformException( - code: __pigeon_replyList[0]! as String, - message: __pigeon_replyList[1] as String?, - details: __pigeon_replyList[2], + code: pigeonVar_replyList[0]! as String, + message: pigeonVar_replyList[1] as String?, + details: pigeonVar_replyList[2], ); } else { return; @@ -575,21 +627,22 @@ class Android { } Future setAdminPwd(String pwd) async { - const String __pigeon_channelName = 'dev.flutter.pigeon.openlist_mobile.Android.setAdminPwd'; - final BasicMessageChannel __pigeon_channel = BasicMessageChannel( - __pigeon_channelName, + final String pigeonVar_channelName = 'dev.flutter.pigeon.openlist_mobile.Android.setAdminPwd$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + pigeonVar_channelName, pigeonChannelCodec, - binaryMessenger: __pigeon_binaryMessenger, + binaryMessenger: pigeonVar_binaryMessenger, ); - final List? __pigeon_replyList = - await __pigeon_channel.send([pwd]) as List?; - if (__pigeon_replyList == null) { - throw _createConnectionError(__pigeon_channelName); - } else if (__pigeon_replyList.length > 1) { + final Future pigeonVar_sendFuture = pigeonVar_channel.send([pwd]); + final List? pigeonVar_replyList = + await pigeonVar_sendFuture as List?; + if (pigeonVar_replyList == null) { + throw _createConnectionError(pigeonVar_channelName); + } else if (pigeonVar_replyList.length > 1) { throw PlatformException( - code: __pigeon_replyList[0]! as String, - message: __pigeon_replyList[1] as String?, - details: __pigeon_replyList[2], + code: pigeonVar_replyList[0]! as String, + message: pigeonVar_replyList[1] as String?, + details: pigeonVar_replyList[2], ); } else { return; @@ -597,103 +650,107 @@ class Android { } Future getOpenListHttpPort() async { - const String __pigeon_channelName = 'dev.flutter.pigeon.openlist_mobile.Android.getOpenListHttpPort'; - final BasicMessageChannel __pigeon_channel = BasicMessageChannel( - __pigeon_channelName, + final String pigeonVar_channelName = 'dev.flutter.pigeon.openlist_mobile.Android.getOpenListHttpPort$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + pigeonVar_channelName, pigeonChannelCodec, - binaryMessenger: __pigeon_binaryMessenger, + binaryMessenger: pigeonVar_binaryMessenger, ); - final List? __pigeon_replyList = - await __pigeon_channel.send(null) as List?; - if (__pigeon_replyList == null) { - throw _createConnectionError(__pigeon_channelName); - } else if (__pigeon_replyList.length > 1) { + final Future pigeonVar_sendFuture = pigeonVar_channel.send(null); + final List? pigeonVar_replyList = + await pigeonVar_sendFuture as List?; + if (pigeonVar_replyList == null) { + throw _createConnectionError(pigeonVar_channelName); + } else if (pigeonVar_replyList.length > 1) { throw PlatformException( - code: __pigeon_replyList[0]! as String, - message: __pigeon_replyList[1] as String?, - details: __pigeon_replyList[2], + code: pigeonVar_replyList[0]! as String, + message: pigeonVar_replyList[1] as String?, + details: pigeonVar_replyList[2], ); - } else if (__pigeon_replyList[0] == null) { + } else if (pigeonVar_replyList[0] == null) { throw PlatformException( code: 'null-error', message: 'Host platform returned null value for non-null return value.', ); } else { - return (__pigeon_replyList[0] as int?)!; + return (pigeonVar_replyList[0] as int?)!; } } Future isRunning() async { - const String __pigeon_channelName = 'dev.flutter.pigeon.openlist_mobile.Android.isRunning'; - final BasicMessageChannel __pigeon_channel = BasicMessageChannel( - __pigeon_channelName, + final String pigeonVar_channelName = 'dev.flutter.pigeon.openlist_mobile.Android.isRunning$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + pigeonVar_channelName, pigeonChannelCodec, - binaryMessenger: __pigeon_binaryMessenger, + binaryMessenger: pigeonVar_binaryMessenger, ); - final List? __pigeon_replyList = - await __pigeon_channel.send(null) as List?; - if (__pigeon_replyList == null) { - throw _createConnectionError(__pigeon_channelName); - } else if (__pigeon_replyList.length > 1) { + final Future pigeonVar_sendFuture = pigeonVar_channel.send(null); + final List? pigeonVar_replyList = + await pigeonVar_sendFuture as List?; + if (pigeonVar_replyList == null) { + throw _createConnectionError(pigeonVar_channelName); + } else if (pigeonVar_replyList.length > 1) { throw PlatformException( - code: __pigeon_replyList[0]! as String, - message: __pigeon_replyList[1] as String?, - details: __pigeon_replyList[2], + code: pigeonVar_replyList[0]! as String, + message: pigeonVar_replyList[1] as String?, + details: pigeonVar_replyList[2], ); - } else if (__pigeon_replyList[0] == null) { + } else if (pigeonVar_replyList[0] == null) { throw PlatformException( code: 'null-error', message: 'Host platform returned null value for non-null return value.', ); } else { - return (__pigeon_replyList[0] as bool?)!; + return (pigeonVar_replyList[0] as bool?)!; } } Future getOpenListVersion() async { - const String __pigeon_channelName = 'dev.flutter.pigeon.openlist_mobile.Android.getOpenListVersion'; - final BasicMessageChannel __pigeon_channel = BasicMessageChannel( - __pigeon_channelName, + final String pigeonVar_channelName = 'dev.flutter.pigeon.openlist_mobile.Android.getOpenListVersion$pigeonVar_messageChannelSuffix'; + final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + pigeonVar_channelName, pigeonChannelCodec, - binaryMessenger: __pigeon_binaryMessenger, + binaryMessenger: pigeonVar_binaryMessenger, ); - final List? __pigeon_replyList = - await __pigeon_channel.send(null) as List?; - if (__pigeon_replyList == null) { - throw _createConnectionError(__pigeon_channelName); - } else if (__pigeon_replyList.length > 1) { + final Future pigeonVar_sendFuture = pigeonVar_channel.send(null); + final List? pigeonVar_replyList = + await pigeonVar_sendFuture as List?; + if (pigeonVar_replyList == null) { + throw _createConnectionError(pigeonVar_channelName); + } else if (pigeonVar_replyList.length > 1) { throw PlatformException( - code: __pigeon_replyList[0]! as String, - message: __pigeon_replyList[1] as String?, - details: __pigeon_replyList[2], + code: pigeonVar_replyList[0]! as String, + message: pigeonVar_replyList[1] as String?, + details: pigeonVar_replyList[2], ); - } else if (__pigeon_replyList[0] == null) { + } else if (pigeonVar_replyList[0] == null) { throw PlatformException( code: 'null-error', message: 'Host platform returned null value for non-null return value.', ); } else { - return (__pigeon_replyList[0] as String?)!; + return (pigeonVar_replyList[0] as String?)!; } } } abstract class Event { - static const MessageCodec pigeonChannelCodec = StandardMessageCodec(); + static const MessageCodec pigeonChannelCodec = _PigeonCodec(); void onServiceStatusChanged(bool isRunning); void onServerLog(int level, String time, String log); - static void setup(Event? api, {BinaryMessenger? binaryMessenger}) { + static void setUp(Event? api, {BinaryMessenger? binaryMessenger, String messageChannelSuffix = '',}) { + messageChannelSuffix = messageChannelSuffix.isNotEmpty ? '.$messageChannelSuffix' : ''; { - final BasicMessageChannel __pigeon_channel = BasicMessageChannel( - 'dev.flutter.pigeon.openlist_mobile.Event.onServiceStatusChanged', pigeonChannelCodec, + final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + 'dev.flutter.pigeon.openlist_mobile.Event.onServiceStatusChanged$messageChannelSuffix', pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { - __pigeon_channel.setMessageHandler(null); + pigeonVar_channel.setMessageHandler(null); } else { - __pigeon_channel.setMessageHandler((Object? message) async { + pigeonVar_channel.setMessageHandler((Object? message) async { assert(message != null, 'Argument for dev.flutter.pigeon.openlist_mobile.Event.onServiceStatusChanged was null.'); final List args = (message as List?)!; @@ -712,13 +769,13 @@ abstract class Event { } } { - final BasicMessageChannel __pigeon_channel = BasicMessageChannel( - 'dev.flutter.pigeon.openlist_mobile.Event.onServerLog', pigeonChannelCodec, + final BasicMessageChannel pigeonVar_channel = BasicMessageChannel( + 'dev.flutter.pigeon.openlist_mobile.Event.onServerLog$messageChannelSuffix', pigeonChannelCodec, binaryMessenger: binaryMessenger); if (api == null) { - __pigeon_channel.setMessageHandler(null); + pigeonVar_channel.setMessageHandler(null); } else { - __pigeon_channel.setMessageHandler((Object? message) async { + pigeonVar_channel.setMessageHandler((Object? message) async { assert(message != null, 'Argument for dev.flutter.pigeon.openlist_mobile.Event.onServerLog was null.'); final List args = (message as List?)!; diff --git a/lib/main.dart b/lib/main.dart index 355b4cc..823482e 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -21,16 +21,30 @@ import 'contant/native_bridge.dart'; void main() async { WidgetsFlutterBinding.ensureInitialized(); - // 初始化语言控制器 + // Initialize language controller Get.put(LanguageController()); - // 初始化通知管理器 + // Initialize notification manager await NotificationManager.initialize(); - // 初始化服务管理器 + // Initialize service manager (supports both Android and iOS) await ServiceManager.instance.initialize(); - // Android + // For iOS: Ensure service is started on first launch + if (defaultTargetPlatform == TargetPlatform.iOS) { + try { + // Check if service is running + final isRunning = await ServiceManager.instance.checkServiceStatus(); + if (!isRunning) { + // Start service automatically on iOS + await ServiceManager.instance.startService(); + } + } catch (e) { + debugPrint('Failed to start iOS service on launch: $e'); + } + } + + // Android WebView debugging if (!kIsWeb && kDebugMode && defaultTargetPlatform == TargetPlatform.android) { diff --git a/lib/pages/openlist/openlist.dart b/lib/pages/openlist/openlist.dart index 0fd285f..d9e5f54 100644 --- a/lib/pages/openlist/openlist.dart +++ b/lib/pages/openlist/openlist.dart @@ -1,3 +1,4 @@ +import 'dart:io'; import 'package:openlist_mobile/generated_api.dart'; import 'package:openlist_mobile/pages/openlist/about_dialog.dart'; import 'package:openlist_mobile/pages/openlist/pwd_edit_dialog.dart'; @@ -28,12 +29,19 @@ class OpenListScreen extends StatelessWidget { onPressed: () { showDialog( context: context, - builder: (context) => PwdEditDialog(onConfirm: (pwd) { - Get.showSnackbar(GetSnackBar( - title: S.current.setAdminPassword, - message: pwd, - duration: const Duration(seconds: 1))); - Android().setAdminPwd(pwd); + builder: (context) => PwdEditDialog(onConfirm: (pwd) async { + try { + await Android().setAdminPwd(pwd); + Get.showSnackbar(GetSnackBar( + title: S.current.setAdminPassword, + message: pwd, + duration: const Duration(seconds: 1))); + } catch (e) { + Get.showSnackbar(GetSnackBar( + title: S.current.setAdminPassword, + message: 'Error: $e', + duration: const Duration(seconds: 2))); + } })); }, icon: const Icon(Icons.password), @@ -45,13 +53,24 @@ class OpenListScreen extends StatelessWidget { }, icon: const Icon(Icons.edit_note), ), - IconButton( - tooltip: S.of(context).desktopShortcut, - onPressed: () async { - Android().addShortcut(); - }, - icon: const Icon(Icons.add_home), - ), + // Desktop shortcut is only available on Android + if (Platform.isAndroid) + IconButton( + tooltip: S.of(context).desktopShortcut, + onPressed: () async { + try { + await Android().addShortcut(); + Get.showSnackbar(GetSnackBar( + message: S.of(context).desktopShortcut, + duration: const Duration(seconds: 1))); + } catch (e) { + Get.showSnackbar(GetSnackBar( + message: 'Error: $e', + duration: const Duration(seconds: 2))); + } + }, + icon: const Icon(Icons.add_home), + ), PopupMenuButton( tooltip: S.of(context).moreOptions, itemBuilder: (context) { @@ -137,7 +156,7 @@ class OpenListController extends GetxController { @override void onInit() { // 设置日志接收器,但状态变化只通过ServiceManager处理 - Event.setup(MyEventReceiver( + Event.setUp(MyEventReceiver( (isRunning) { // 不在这里更新状态,避免冲突 print('Event receiver status: $isRunning'); diff --git a/lib/pages/settings/settings.dart b/lib/pages/settings/settings.dart index 750a0d2..3136386 100644 --- a/lib/pages/settings/settings.dart +++ b/lib/pages/settings/settings.dart @@ -1,3 +1,4 @@ +import 'dart:io'; import 'package:openlist_mobile/contant/native_bridge.dart'; import 'package:openlist_mobile/generated_api.dart'; import 'package:openlist_mobile/pages/settings/preference_widgets.dart'; @@ -47,6 +48,8 @@ class _SettingsScreenState extends State { () => ListView( children: [ // SizedBox(height: MediaQuery.of(context).padding.top), + // Android-specific permission requests + if (Platform.isAndroid) ...[ Visibility( visible: !controller._managerStorageGranted.value || !controller._notificationGranted.value || @@ -82,6 +85,7 @@ class _SettingsScreenState extends State { Permission.notification.request(); }, )), + ], // End of Android-specific permissions DividerPreference(title: S.of(context).general), @@ -133,29 +137,31 @@ class _SettingsScreenState extends State { }, ), - BasicPreference( - title: S.of(context).dataDirectory, - subtitle: controller._dataDir.value, - leading: const Icon(Icons.folder), - onTap: () async { - final path = await FilePicker.platform.getDirectoryPath(); - - if (path == null) { - Get.showSnackbar(GetSnackBar( - message: S.current.setDefaultDirectory, - duration: const Duration(seconds: 3), - mainButton: TextButton( - onPressed: () { - controller.setDataDir(""); - Get.back(); - }, - child: Text(S.current.confirm), - ))); - } else { - controller.setDataDir(path); - } - }, - ), + // Data directory setting - only for Android + if (Platform.isAndroid) + BasicPreference( + title: S.of(context).dataDirectory, + subtitle: controller._dataDir.value, + leading: const Icon(Icons.folder), + onTap: () async { + final path = await FilePicker.platform.getDirectoryPath(); + + if (path == null) { + Get.showSnackbar(GetSnackBar( + message: S.current.setDefaultDirectory, + duration: const Duration(seconds: 3), + mainButton: TextButton( + onPressed: () { + controller.setDataDir(""); + Get.back(); + }, + child: Text(S.current.confirm), + ))); + } else { + controller.setDataDir(path); + } + }, + ), DividerPreference(title: S.of(context).uiSettings), SwitchPreference( icon: const Icon(Icons.pan_tool_alt_outlined), diff --git a/lib/pages/web/web.dart b/lib/pages/web/web.dart index 9d67d4f..5cdba2a 100644 --- a/lib/pages/web/web.dart +++ b/lib/pages/web/web.dart @@ -22,7 +22,7 @@ class WebScreen extends StatefulWidget { } } -class WebScreenState extends State { +class WebScreenState extends State with WidgetsBindingObserver { InAppWebViewController? _webViewController; InAppWebViewSettings settings = InAppWebViewSettings( allowsInlineMediaPlayback: true, @@ -31,11 +31,22 @@ class WebScreenState extends State { javaScriptEnabled: true, mediaPlaybackRequiresUserGesture: false, useShouldOverrideUrlLoading: true, + // iOS specific: Enable page caching and state preservation + cacheEnabled: true, + sharedCookiesEnabled: true, + limitsNavigationsToAppBoundDomains: false, + // Enable disk and memory cache for better state preservation + cacheMode: CacheMode.LOAD_DEFAULT, + // Prevent WebView from being suspended in background + allowsBackForwardNavigationGestures: true, + // iOS: Suppress rendering until content is loaded + suppressesIncrementalRendering: false, ); double _progress = 0; String _url = "http://localhost:5244"; bool _canGoBack = false; + bool _isLoading = false; onClickNavigationBar() { log("onClickNavigationBar"); @@ -44,22 +55,72 @@ class WebScreenState extends State { @override void initState() { + super.initState(); + // Register lifecycle observer to handle app state changes + WidgetsBinding.instance.addObserver(this); + + // Get OpenList HTTP port Android() .getOpenListHttpPort() - .then((port) => {_url = "http://localhost:$port"}); + .then((port) { + setState(() { + _url = "http://localhost:$port"; + }); + log("OpenList URL set to: $_url"); + }) + .catchError((error) { + log("Failed to get OpenList port: $error"); + }); - // NativeEvent().addServiceStatusListener((isRunning) { - // if (isRunning) _webViewController?.reload(); - // }); - super.initState(); + // Wait a bit for service to be ready before loading + Future.delayed(const Duration(milliseconds: 500), () { + if (mounted && _webViewController == null) { + // Will be initialized when WebView is created + log("WebView will initialize with URL: $_url"); + } + }); } @override void dispose() { + // Remove lifecycle observer when widget is disposed + WidgetsBinding.instance.removeObserver(this); _webViewController?.dispose(); super.dispose(); } + @override + void didChangeAppLifecycleState(AppLifecycleState state) { + super.didChangeAppLifecycleState(state); + log("App lifecycle state changed: $state"); + + switch (state) { + case AppLifecycleState.resumed: + // App returned to foreground, WebView should be active + log("App resumed, WebView is active"); + _webViewController?.resume(); + break; + case AppLifecycleState.paused: + // App entered background, ensure WebView state is preserved + log("App paused, WebView entering background"); + // Note: Do not pause WebView to keep background tasks running + // The UIBackgroundModes in Info.plist allows WebKit processes to continue + break; + case AppLifecycleState.inactive: + // App transitioning states (e.g., incoming call, app switcher) + log("App inactive"); + break; + case AppLifecycleState.detached: + // App is detached from UI + log("App detached"); + break; + case AppLifecycleState.hidden: + // App is hidden + log("App hidden"); + break; + } + } + @override Widget build(BuildContext context) { return PopScope( @@ -83,11 +144,13 @@ class WebScreenState extends State { initialUrlRequest: URLRequest(url: WebUri(_url)), onWebViewCreated: (InAppWebViewController controller) { _webViewController = controller; + log("WebView created, loading URL: $_url"); }, onLoadStart: (InAppWebViewController controller, Uri? url) { log("onLoadStart $url"); setState(() { _progress = 0; + _isLoading = true; }); }, shouldOverrideUrlLoading: (controller, navigationAction) async { @@ -127,16 +190,26 @@ class WebScreenState extends State { return NavigationActionPolicy.ALLOW; }, onReceivedError: (controller, request, error) async { - if (!await Android().isRunning()) { - await Android().startService(); - - for (int i = 0; i < 3; i++) { - await Future.delayed(const Duration(milliseconds: 500)); - if (await Android().isRunning()) { - _webViewController?.reload(); - break; + log("WebView error: ${error.description}"); + + // Check if OpenList service is running + try { + if (!await Android().isRunning()) { + log("Service not running, attempting to start..."); + await Android().startService(); + + // Wait for service to start and retry + for (int i = 0; i < 3; i++) { + await Future.delayed(const Duration(milliseconds: 500)); + if (await Android().isRunning()) { + log("Service started, reloading WebView"); + _webViewController?.reload(); + break; + } } } + } catch (e) { + log("Failed to handle WebView error: $e"); } }, onDownloadStartRequest: (controller, url) async { @@ -185,8 +258,10 @@ class WebScreenState extends State { }, onLoadStop: (InAppWebViewController controller, Uri? url) async { + log("onLoadStop $url"); setState(() { _progress = 0; + _isLoading = false; }); }, onProgressChanged: diff --git a/lib/utils/service_manager.dart b/lib/utils/service_manager.dart index 1ca1ad7..afc5c0f 100644 --- a/lib/utils/service_manager.dart +++ b/lib/utils/service_manager.dart @@ -2,8 +2,9 @@ import 'dart:async'; import 'dart:io'; import 'package:flutter/services.dart'; import 'package:flutter/foundation.dart'; +import 'package:openlist_mobile/generated_api.dart'; -/// 服务管理器 - 管理OpenList后台服务的启动、停止和状态监控 +/// Service Manager - Manages OpenList backend service lifecycle class ServiceManager { static const String _channelName = 'com.openlist.mobile/service'; static const MethodChannel _channel = MethodChannel(_channelName); @@ -13,21 +14,20 @@ class ServiceManager { ServiceManager._(); - // 服务状态流控制器 + // Service status stream controller final StreamController _serviceStatusController = StreamController.broadcast(); - /// 服务状态流 + /// Service status stream Stream get serviceStatusStream => _serviceStatusController.stream; bool _isServiceRunning = false; Timer? _statusCheckTimer; - /// 当前服务是否运行 + /// Current service running status bool get isServiceRunning => _isServiceRunning; - /// 初始化服务管理器 + /// Initialize service manager Future initialize() async { - if (!Platform.isAndroid) return; try { // 设置方法调用处理器 @@ -59,53 +59,77 @@ class ServiceManager { } } - /// 启动OpenList服务 + /// Start OpenList service Future startService() async { - if (!Platform.isAndroid) return false; - try { - final bool result = await _channel.invokeMethod('startService'); - debugPrint('Start service result: $result'); - - // 延迟检查状态,给服务启动时间 - Timer(const Duration(seconds: 2), () => checkServiceStatus()); + if (Platform.isAndroid) { + final bool result = await _channel.invokeMethod('startService'); + debugPrint('Start service result (Android): $result'); + + // Delay status check to give service startup time + Timer(const Duration(seconds: 2), () => checkServiceStatus()); + + return result; + } else if (Platform.isIOS) { + // Use Pigeon API for iOS + await Android().startService(); + debugPrint('Start service called (iOS)'); + + // Delay status check + Timer(const Duration(seconds: 2), () => checkServiceStatus()); + + return true; + } - return result; + return false; } catch (e) { debugPrint('Failed to start service: $e'); return false; } } - /// 停止OpenList服务 + /// Stop OpenList service Future stopService() async { - if (!Platform.isAndroid) return false; - try { - final bool result = await _channel.invokeMethod('stopService'); - debugPrint('Stop service result: $result'); - - // 立即更新状态为停止 - if (result) { + if (Platform.isAndroid) { + final bool result = await _channel.invokeMethod('stopService'); + debugPrint('Stop service result (Android): $result'); + + // Update status immediately + if (result) { + _updateServiceStatus(false); + } + + // Delay status check to confirm service stopped + Timer(const Duration(seconds: 1), () => checkServiceStatus()); + + return result; + } else if (Platform.isIOS) { + // iOS does not need explicit stop - managed by OpenListManager + debugPrint('Stop service called (iOS) - managed by system'); _updateServiceStatus(false); + return true; } - // 延迟检查状态,确认服务已停止 - Timer(const Duration(seconds: 1), () => checkServiceStatus()); - - return result; + return false; } catch (e) { debugPrint('Failed to stop service: $e'); return false; } } - /// 检查服务状态 + /// Check service status Future checkServiceStatus() async { - if (!Platform.isAndroid) return false; - try { - final bool isRunning = await _channel.invokeMethod('isServiceRunning'); + bool isRunning = false; + + if (Platform.isAndroid) { + isRunning = await _channel.invokeMethod('isServiceRunning'); + } else if (Platform.isIOS) { + // Use Pigeon API for iOS + isRunning = await Android().isRunning(); + } + _updateServiceStatus(isRunning); return isRunning; } catch (e) { @@ -114,10 +138,8 @@ class ServiceManager { } } - /// 重启服务 + /// Restart service Future restartService() async { - if (!Platform.isAndroid) return false; - try { await stopService(); await Future.delayed(const Duration(seconds: 2)); diff --git a/pigeon_config.yaml b/pigeon_config.yaml new file mode 100644 index 0000000..36ff76c --- /dev/null +++ b/pigeon_config.yaml @@ -0,0 +1,10 @@ +# Pigeon configuration for OpenList Mobile +# Generate platform-specific API code from Dart definitions + +input: pigeons/pigeon.dart +dart_out: lib/generated_api.dart + +java_out: android/app/src/main/java/com/openlist/pigeon/GeneratedApi.java +java_package: com.openlist.pigeon + +swift_out: ios/Runner/GeneratedPluginRegistrant.swift