all lists on lists.proxmox.com
 help / color / mirror / Atom feed
* [pve-devel] [PATCH pve_flutter_frontend] feat: ios: allow app to open virt-viewer (.vv) file with spice app
@ 2025-07-21 13:39 Shan Shaji
  0 siblings, 0 replies; only message in thread
From: Shan Shaji @ 2025-07-21 13:39 UTC (permalink / raw)
  To: pve-devel

The feature to open the spice connection file was disabled in iOS and
was only available in Android. To support iOS, a new native channel
implementation for iOS has been added.

For the iOS implementation, use the `UIActivityViewController` [0] to
show the share sheet with the suggested apps that can open the file.
Alternatively, users can also save the file to the device storage.

The `getExternalChacheDirectories` function has been replaced with
`getTemporaryDirectory` as the external cache directories function is
not supported [1] in iOS.

[0] - https://developer.apple.com/documentation/uikit/uiactivityviewcontroller
[1] - https://developer.apple.com/documentation/bundleresources/information-property-list/utimportedtypedeclarations

References:
- https://docs.swift.org/swift-book/documentation/the-swift-programming-language/closures#Trailing-Closures
- https://medium.com/@dinesh.kachhot/different-ways-to-share-data-between-apps-de75a0a46d4a
- https://docs.flutter.dev/platform-integration/platform-channels#step-4-add-an-ios-platform-specific-implementation
- https://stackoverflow.com/questions/25644054/uiactivityviewcontroller-crashing-on-ios-8-ipads

Signed-off-by: Shan Shaji <s.shaji@proxmox.com>
---
 ios/Runner/AppDelegate.swift             | 64 +++++++++++++++++++++---
 lib/widgets/pve_console_menu_widget.dart | 10 ++--
 2 files changed, 60 insertions(+), 14 deletions(-)

diff --git a/ios/Runner/AppDelegate.swift b/ios/Runner/AppDelegate.swift
index 6266644..115e1fd 100644
--- a/ios/Runner/AppDelegate.swift
+++ b/ios/Runner/AppDelegate.swift
@@ -3,11 +3,61 @@ import UIKit
 
 @main
 @objc class AppDelegate: FlutterAppDelegate {
-  override func application(
-    _ application: UIApplication,
-    didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
-  ) -> Bool {
-    GeneratedPluginRegistrant.register(with: self)
-    return super.application(application, didFinishLaunchingWithOptions: launchOptions)
-  }
+    override func application(
+        _ application: UIApplication,
+        didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
+    ) -> Bool {
+        let controller: FlutterViewController = window?.rootViewController as! FlutterViewController
+        let channel: FlutterMethodChannel = FlutterMethodChannel(
+            name: "com.proxmox.app.pve_flutter_frontend/filesharing",
+            binaryMessenger: controller.binaryMessenger)
+        
+        channel.setMethodCallHandler({
+            [weak self] (call: FlutterMethodCall, result: @escaping FlutterResult) -> Void in
+            
+            guard call.method == "shareFile" else {
+                result(FlutterMethodNotImplemented)
+                return
+            }
+            
+            let arguments = call.arguments as? [String: Any]
+            let path = arguments?["path"] as? String
+            let type = arguments?["type"] as? String
+            
+            if let filePath = path, let _ = type  {
+                self?.shareFile(atPath: filePath, from: controller, result: result)
+            } else {
+                result(FlutterError(code: "FileNotFoundException", message: "File not found", details: nil))
+            }
+        })
+        
+        GeneratedPluginRegistrant.register(with: self)
+        return super.application(application, didFinishLaunchingWithOptions: launchOptions)
+    }
+    
+    private func shareFile(atPath path: String, from controller: UIViewController, result: @escaping FlutterResult) {
+        let fileURL = URL(fileURLWithPath: path)
+        let activityVC = UIActivityViewController(
+            activityItems: [fileURL],
+            applicationActivities: nil,
+        )
+
+      // To avoid crashing in iPad
+      if let popover = activityVC.popoverPresentationController {
+        popover.sourceView = controller.view
+        popover.sourceRect = CGRect(
+            x: controller.view.bounds.midX,
+            y: controller.view.bounds.midY,
+            width: 0,
+            height: 0,
+        )
+      }
+
+        
+    controller.present(activityVC, animated: true) {
+            result(nil)
+        }
+    }
+    
+    
 }
diff --git a/lib/widgets/pve_console_menu_widget.dart b/lib/widgets/pve_console_menu_widget.dart
index cd8c314..8fa5538 100644
--- a/lib/widgets/pve_console_menu_widget.dart
+++ b/lib/widgets/pve_console_menu_widget.dart
@@ -38,7 +38,7 @@ class PveConsoleMenu extends StatelessWidget {
         child: Column(
           mainAxisSize: MainAxisSize.min,
           children: [
-            if (Platform.isAndroid && (allowSpice ?? true))
+            if ((Platform.isAndroid || Platform.isIOS) && (allowSpice ?? true))
               ListTile(
                 title: const Text(
                   "SPICE",
@@ -47,8 +47,7 @@ class PveConsoleMenu extends StatelessWidget {
                 subtitle:
                     const Text("Open SPICE connection file with external App"),
                 onTap: () async {
-                  if (Platform.isAndroid) {
-                    final tempDir = await getExternalCacheDirectories();
+                    final tempDir = await getTemporaryDirectory();
 
                     String apiPath;
                     if (['qemu', 'lxc'].contains(type)) {
@@ -67,7 +66,7 @@ class PveConsoleMenu extends StatelessWidget {
                       }
                       return;
                     }
-                    var filePath = await writeSpiceFile(data, tempDir![0].path);
+                    var filePath = await writeSpiceFile(data, tempDir.path);
 
                     try {
                       await platform.invokeMethod('shareFile', {
@@ -98,9 +97,6 @@ class PveConsoleMenu extends StatelessWidget {
                         }
                       }
                     }
-                  } else {
-                    print('not implemented for current platform');
-                  }
                 },
               ),
             if (Platform.isAndroid) // web_view is only available for mobile :(
-- 
2.39.5 (Apple Git-154)



_______________________________________________
pve-devel mailing list
pve-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel


^ permalink raw reply	[flat|nested] only message in thread

only message in thread, other threads:[~2025-07-21 13:39 UTC | newest]

Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2025-07-21 13:39 [pve-devel] [PATCH pve_flutter_frontend] feat: ios: allow app to open virt-viewer (.vv) file with spice app Shan Shaji

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.
Service provided by Proxmox Server Solutions GmbH | Privacy | Legal