clipboard: add UxClipboard.readImage native bridge
Flutter's Clipboard API only exposes text shapes. Banlu's chat composer needs image bytes from the system clipboard for desktop paste, so add a UxClipboard facade backed by per-platform native plugins: * iOS: prefer raw PNG/JPEG bytes off the pasteboard, fall back to re-encoding `UIPasteboard.image` as PNG. * macOS: prefer NSPasteboard `.png`, fall back to TIFF transcoded through NSBitmapImageRep so screenshots / Preview hand-offs still work. * Android: read primary ClipData's first item URI and stream the bytes through ContentResolver — don't trust the clip description's MIME, copy whatever the resolver returns. Returns null (never throws) when the clipboard has no image — callers treat null as "fall through to text paste".
This commit is contained in:
52
ios/Classes/ClipboardPlugin.swift
Normal file
52
ios/Classes/ClipboardPlugin.swift
Normal file
@@ -0,0 +1,52 @@
|
||||
import Flutter
|
||||
import MobileCoreServices
|
||||
import UIKit
|
||||
import UniformTypeIdentifiers
|
||||
|
||||
public class ClipboardPlugin: NSObject, NativePlugin {
|
||||
private var channel: FlutterMethodChannel?
|
||||
|
||||
public func register(with registrar: FlutterPluginRegistrar) {
|
||||
let c = FlutterMethodChannel(name: "ux/clipboard", binaryMessenger: registrar.messenger())
|
||||
c.setMethodCallHandler { [weak self] call, result in
|
||||
self?.handle(call, result: result)
|
||||
}
|
||||
channel = c
|
||||
}
|
||||
|
||||
private func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
|
||||
switch call.method {
|
||||
case "readImage": handleReadImage(result: result)
|
||||
default: result(FlutterMethodNotImplemented)
|
||||
}
|
||||
}
|
||||
|
||||
private func handleReadImage(result: @escaping FlutterResult) {
|
||||
let pb = UIPasteboard.general
|
||||
|
||||
// Prefer raw PNG / JPEG bytes already on the clipboard so we don't
|
||||
// round-trip through UIImage when we don't have to.
|
||||
if #available(iOS 14.0, *) {
|
||||
if let png = pb.data(forPasteboardType: UTType.png.identifier) {
|
||||
return result(FlutterStandardTypedData(bytes: png))
|
||||
}
|
||||
if let jpg = pb.data(forPasteboardType: UTType.jpeg.identifier) {
|
||||
return result(FlutterStandardTypedData(bytes: jpg))
|
||||
}
|
||||
} else {
|
||||
if let png = pb.data(forPasteboardType: kUTTypePNG as String) {
|
||||
return result(FlutterStandardTypedData(bytes: png))
|
||||
}
|
||||
if let jpg = pb.data(forPasteboardType: kUTTypeJPEG as String) {
|
||||
return result(FlutterStandardTypedData(bytes: jpg))
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback: re-encode whatever UIImage resolves to as PNG.
|
||||
if let img = pb.image, let png = img.pngData() {
|
||||
return result(FlutterStandardTypedData(bytes: png))
|
||||
}
|
||||
|
||||
result(nil)
|
||||
}
|
||||
}
|
||||
@@ -10,6 +10,7 @@ public class UxPlugin: NSObject, FlutterPlugin {
|
||||
SensorPlugin(),
|
||||
FilePlugin(),
|
||||
ScannerPlugin(),
|
||||
ClipboardPlugin(),
|
||||
]
|
||||
for plugin in plugins {
|
||||
plugin.register(with: registrar)
|
||||
|
||||
Reference in New Issue
Block a user