all lists on lists.proxmox.com
 help / color / mirror / Atom feed
* [pve-devel] [PATCH pve_flutter_frontend v2] feat: ios: enable opening of virt-viewer (.vv) file with spice app
@ 2025-07-21 13:46 Shan Shaji
  2025-07-22  9:24 ` [pve-devel] applied: " Thomas Lamprecht
  0 siblings, 1 reply; 2+ messages in thread
From: Shan Shaji @ 2025-07-21 13:46 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>
---
 changes since v1:
 - Fixed commit message

 Tested:
 - The above changes are tested on iPad simulator and a real iPhone in
   debug mode. 

 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] 2+ messages in thread

* [pve-devel] applied: [PATCH pve_flutter_frontend v2] feat: ios: enable opening of virt-viewer (.vv) file with spice app
  2025-07-21 13:46 [pve-devel] [PATCH pve_flutter_frontend v2] feat: ios: enable opening of virt-viewer (.vv) file with spice app Shan Shaji
@ 2025-07-22  9:24 ` Thomas Lamprecht
  0 siblings, 0 replies; 2+ messages in thread
From: Thomas Lamprecht @ 2025-07-22  9:24 UTC (permalink / raw)
  To: pve-devel, Shan Shaji

On Mon, 21 Jul 2025 15:46:43 +0200, Shan Shaji wrote:
> 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.
> 
> [...]

Applied, but had to fixup quite a few whitespace issues [0] please ensure the
code is correctly formated the next time, thanks!

[0]: e.g. git am complains about:

Applying: feat: ios: enable opening of virt-viewer (.vv) file with spice app
pve_flutter_frontend/.git/rebase-apply/patch:36: trailing whitespace.

pve_flutter_frontend/.git/rebase-apply/patch:39: trailing whitespace.

pve_flutter_frontend/.git/rebase-apply/patch:44: trailing whitespace.

pve_flutter_frontend/.git/rebase-apply/patch:48: trailing whitespace.

pve_flutter_frontend/.git/rebase-apply/patch:55: trailing whitespace.


[1/1] feat: ios: enable opening of virt-viewer (.vv) file with spice app
      commit: 826474c276a051077c4e10ca3bc818eb78e247c4


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


^ permalink raw reply	[flat|nested] 2+ messages in thread

end of thread, other threads:[~2025-07-22  9:23 UTC | newest]

Thread overview: 2+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2025-07-21 13:46 [pve-devel] [PATCH pve_flutter_frontend v2] feat: ios: enable opening of virt-viewer (.vv) file with spice app Shan Shaji
2025-07-22  9:24 ` [pve-devel] applied: " Thomas Lamprecht

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