完善 OTG 相机有线传图:支持 Sony/Canon 自动识别,页面 detach 保持会话以便再次进入复连。

引入进程级 CameraTetheringSession 与共享 USB Browser;离开传图页仅取消 UI 回调,App 终止时再完整 shutdown 释放 PTP 与 Browser 资源。

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-07-01 15:45:35 +08:00
parent 5f7ef24683
commit 90d135b5ac
34 changed files with 2870 additions and 310 deletions

View File

@ -16,6 +16,7 @@ struct WiredCameraTransferView: View {
@Environment(\.ossUploadService) private var ossUploadService
@EnvironmentObject private var toastCenter: ToastCenter
@Environment(\.dismiss) private var dismiss
@Environment(\.scenePhase) private var scenePhase
@StateObject private var viewModel: WiredCameraTransferViewModel
@State private var previewSources: [String]?
@ -26,7 +27,7 @@ struct WiredCameraTransferView: View {
_viewModel = StateObject(
wrappedValue: WiredCameraTransferViewModel(
context: context,
cameraService: CameraServiceFactory.make(brand: .sony)
cameraService: CameraServiceFactory.makeAutoDetect()
)
)
}
@ -50,6 +51,7 @@ struct WiredCameraTransferView: View {
deviceStorageInfo: viewModel.deviceStorageInfo,
isCameraConnected: viewModel.isCameraConnected,
isRefreshingCameraFiles: viewModel.isRefreshingCameraFiles,
isSearchingCamera: viewModel.isSearchingCamera,
cameraDeviceName: viewModel.cameraDeviceName,
transferModeOption: viewModel.transferModeOption,
tabTitles: tabTitles,
@ -103,7 +105,11 @@ struct WiredCameraTransferView: View {
)
}
.onDisappear {
Task { await viewModel.stop() }
viewModel.detachFromUI()
}
.onChange(of: scenePhase) { phase in
guard phase == .active, !viewModel.isCameraConnected else { return }
Task { await viewModel.restartCameraSearch() }
}
.onChange(of: viewModel.errorMessage) { message in
guard let message, !message.isEmpty else { return }