完善 OTG 相机有线传图:支持 Sony/Canon 自动识别,页面 detach 保持会话以便再次进入复连。
引入进程级 CameraTetheringSession 与共享 USB Browser;离开传图页仅取消 UI 回调,App 终止时再完整 shutdown 释放 PTP 与 Browser 资源。 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@ -26,10 +26,21 @@ final class CameraTransferPipeline {
|
||||
private let maxRetries = 3
|
||||
private let deferredNotifyInterval: TimeInterval = 0.15
|
||||
private let maxConcurrentUploads = 3
|
||||
private var isUIAttached = false
|
||||
|
||||
/// 初始化传输管道,并注入相机服务。
|
||||
init(cameraService: any CameraServiceProtocol) {
|
||||
self.cameraService = cameraService
|
||||
}
|
||||
|
||||
/// 绑定 UI 回调;再次进入 OTG 页时重新 attach。
|
||||
func attachToUI(
|
||||
onConnectionStateChange: @escaping (CameraConnectionState) -> Void,
|
||||
onTasksUpdated: @escaping ([CameraTransferTask]) -> Void
|
||||
) {
|
||||
isUIAttached = true
|
||||
self.onTasksUpdated = onTasksUpdated
|
||||
cameraService.onConnectionStateChange = onConnectionStateChange
|
||||
cameraService.onNewAsset = { [weak self] asset in
|
||||
Task { @MainActor in
|
||||
await self?.handleNewAsset(asset, skipIfExists: false)
|
||||
@ -37,6 +48,18 @@ final class CameraTransferPipeline {
|
||||
}
|
||||
}
|
||||
|
||||
/// 页面离开:取消 UI 回调与进行中的上传通知,保持相机连接。
|
||||
func detachFromUI() {
|
||||
guard isUIAttached else { return }
|
||||
isUIAttached = false
|
||||
pendingDeferredNotifyTask?.cancel()
|
||||
pendingDeferredNotifyTask = nil
|
||||
activeUploadAssetIDs.removeAll()
|
||||
inFlightAssetIDs.removeAll()
|
||||
onTasksUpdated = nil
|
||||
cameraService.detachFromUI()
|
||||
}
|
||||
|
||||
/// 配置上传 Sink、自动上传开关与可选下载作用域。
|
||||
func configure(
|
||||
uploadSink: (any CameraAssetUploadSink)?,
|
||||
@ -50,14 +73,33 @@ final class CameraTransferPipeline {
|
||||
}
|
||||
}
|
||||
|
||||
/// 启动相机连接。
|
||||
/// 启动相机连接;已连接时同步已有照片。
|
||||
func connect() async {
|
||||
await cameraService.connect()
|
||||
if cameraService.connectionState.isConnected {
|
||||
await syncExistingPhotos()
|
||||
}
|
||||
}
|
||||
|
||||
/// 断开相机连接。
|
||||
/// 完整释放相机资源(App 终止或测试 teardown)。
|
||||
func shutdown() async {
|
||||
detachFromUI()
|
||||
await cameraService.shutdown()
|
||||
}
|
||||
|
||||
/// 断开相机连接(兼容旧调用,语义等同 shutdown)。
|
||||
func disconnect() async {
|
||||
await cameraService.disconnect()
|
||||
await shutdown()
|
||||
}
|
||||
|
||||
/// 重新搜索 USB 相机。
|
||||
func restartCameraDiscovery() async {
|
||||
if let autoDetect = cameraService as? AutoDetectCameraService {
|
||||
await autoDetect.restartDiscovery()
|
||||
return
|
||||
}
|
||||
await cameraService.shutdown()
|
||||
await cameraService.connect()
|
||||
}
|
||||
|
||||
/// 当前相机连接状态。
|
||||
@ -65,14 +107,6 @@ final class CameraTransferPipeline {
|
||||
cameraService.connectionState
|
||||
}
|
||||
|
||||
/// 设置连接状态变化回调。
|
||||
var onConnectionStateChange: ((CameraConnectionState) -> Void)? {
|
||||
get { nil }
|
||||
set {
|
||||
cameraService.onConnectionStateChange = newValue
|
||||
}
|
||||
}
|
||||
|
||||
/// 重试所有失败的上传任务。
|
||||
func retryFailedUploads() async {
|
||||
guard uploadEnabled, uploadSink != nil else { return }
|
||||
|
||||
Reference in New Issue
Block a user