fix: suspend OTG live transfer on exit

This commit is contained in:
2026-07-08 09:32:35 +08:00
parent 92fcad7ac9
commit e9f593643f
4 changed files with 94 additions and 38 deletions

View File

@ -36,6 +36,7 @@ protocol WiredCameraConnectionManaging: AnyObject {
func configureLiveTransfer(albumID: Int?)
func start()
func unbindDelegate()
func suspendLiveTransfer()
func disconnect()
}
@ -86,6 +87,11 @@ final class ConnectionManager: NSObject, WiredCameraConnectionManaging {
func configureLiveTransfer(albumID: Int?) {
liveTransferAlbumID = albumID
OTGLog.debug(.connection, "live transfer album=\(albumID.map(String.init) ?? "nil")")
if albumID == nil {
stopLiveTransferServices()
return
}
resumeLiveTransferIfPossible()
}
/// Entry point when OTG UI appears: reconnect cached device or start discovery.
@ -115,6 +121,13 @@ final class ConnectionManager: NSObject, WiredCameraConnectionManaging {
cancelSearchTimers()
}
/// USB Session
func suspendLiveTransfer() {
OTGLog.info(.connection, "suspend live transfer")
liveTransferAlbumID = nil
stopLiveTransferServices()
}
/// Session Driver USB
func disconnect() {
guard let device = activeDevice else {
@ -131,12 +144,7 @@ final class ConnectionManager: NSObject, WiredCameraConnectionManaging {
isClosingSession = true
isContentCatalogReady = false
cancelSearchTimers()
sonyRemoteCapture?.stop()
sonyRemoteCapture = nil
canonRemoteCapture?.stop()
canonRemoteCapture = nil
nikonLiveCapture?.stop()
nikonLiveCapture = nil
stopLiveTransferServices()
liveTransferAlbumID = nil
currentDriver?.disconnect()
currentDriver = nil
@ -305,12 +313,7 @@ final class ConnectionManager: NSObject, WiredCameraConnectionManaging {
}
private func clearDeviceCache() {
sonyRemoteCapture?.stop()
sonyRemoteCapture = nil
canonRemoteCapture?.stop()
canonRemoteCapture = nil
nikonLiveCapture?.stop()
nikonLiveCapture = nil
stopLiveTransferServices()
cachedDevice = nil
currentDriver?.disconnect()
currentDriver = nil
@ -428,24 +431,7 @@ private extension ConnectionManager {
await runGetDeviceInfoProbe(on: camera)
case .sony:
let transactionID: UInt32 = 1
let result = await SonyPTPHelper.initializeRemoteSession(
on: camera,
startingTransactionID: transactionID
)
if result.success {
let service = SonyRemoteCaptureService()
service.onShotSaved = makeLiveShotSavedHandler()
sonyRemoteCapture = service
service.start(
camera: camera,
startingTransactionID: result.nextTransactionID,
albumID: liveTransferAlbumID
)
OTGLog.info(.sony, "Sony remote session ready")
} else {
OTGLog.error(.sony, "Sony remote session setup failed")
}
await startSonyLiveTransferIfNeeded(for: camera)
default:
await runGetDeviceInfoProbe(on: camera)
@ -474,10 +460,12 @@ private extension ConnectionManager {
/// catalog Canon SetRemoteMode SD
private func startCanonLiveTransferIfNeeded(for camera: ICCameraDevice) {
guard canonRemoteCapture == nil else { return }
guard liveTransferAlbumID != nil else { return }
guard PlatformDetector.detect(from: buildDeviceInfo(from: camera)) == .canon else { return }
Task { @MainActor in
try? await Task.sleep(nanoseconds: 500_000_000)
guard self.canonRemoteCapture == nil, self.liveTransferAlbumID != nil else { return }
let transactionID: UInt32 = 1
let result = await CanonPTPHelper.initializeRemoteSession(
@ -504,6 +492,7 @@ private extension ConnectionManager {
/// catalog didAdd + catalog PTP
private func startNikonLiveTransferIfNeeded(for camera: ICCameraDevice) {
guard nikonLiveCapture == nil else { return }
guard liveTransferAlbumID != nil else { return }
guard PlatformDetector.detect(from: buildDeviceInfo(from: camera)) == .nikon else { return }
let service = NikonCatalogLiveCaptureService()
@ -513,6 +502,57 @@ private extension ConnectionManager {
OTGLog.info(.nikon, "Nikon catalog live capture ready")
}
private func startSonyLiveTransferIfNeeded(for camera: ICCameraDevice) async {
guard sonyRemoteCapture == nil else { return }
guard liveTransferAlbumID != nil else { return }
guard PlatformDetector.detect(from: buildDeviceInfo(from: camera)) == .sony else { return }
let transactionID: UInt32 = 1
let result = await SonyPTPHelper.initializeRemoteSession(
on: camera,
startingTransactionID: transactionID
)
guard liveTransferAlbumID != nil else { return }
if result.success {
let service = SonyRemoteCaptureService()
service.onShotSaved = makeLiveShotSavedHandler()
sonyRemoteCapture = service
service.start(
camera: camera,
startingTransactionID: result.nextTransactionID,
albumID: liveTransferAlbumID
)
OTGLog.info(.sony, "Sony remote session ready")
} else {
OTGLog.error(.sony, "Sony remote session setup failed")
}
}
private func resumeLiveTransferIfPossible() {
guard let camera = cachedDevice, case .connected = state else { return }
switch PlatformDetector.detect(from: buildDeviceInfo(from: camera)) {
case .sony:
Task { await startSonyLiveTransferIfNeeded(for: camera) }
case .canon:
guard isContentCatalogReady else { return }
startCanonLiveTransferIfNeeded(for: camera)
case .nikon:
guard isContentCatalogReady else { return }
startNikonLiveTransferIfNeeded(for: camera)
case .unknown:
break
}
}
private func stopLiveTransferServices() {
sonyRemoteCapture?.stop()
sonyRemoteCapture = nil
canonRemoteCapture?.stop()
canonRemoteCapture = nil
nikonLiveCapture?.stop()
nikonLiveCapture = nil
}
private func makeLiveShotSavedHandler() -> (String) -> Void {
{ [weak self] filename in
guard let self, let albumID = self.liveTransferAlbumID else { return }
@ -557,12 +597,7 @@ extension ConnectionManager: ICCameraDeviceDelegate {
}
isClosingSession = false
sonyRemoteCapture?.stop()
sonyRemoteCapture = nil
canonRemoteCapture?.stop()
canonRemoteCapture = nil
nikonLiveCapture?.stop()
nikonLiveCapture = nil
stopLiveTransferServices()
currentDriver = nil
isContentCatalogReady = false
if let camera = device as? ICCameraDevice {

View File

@ -145,9 +145,10 @@ final class WiredCameraTransferViewModel {
connectionManager.start()
}
/// UI delegate
/// UI delegate
func stop() {
connectionManager.unbindDelegate()
connectionManager.suspendLiveTransfer()
}
///