对齐有线传图页 Android UI,并改用相对路径持久化本地照片。

还原顶栏、浮动卡片、时间侧栏与底部交互;照片路径改为 CameraDownloads 相对路径并兼容旧数据;格式 Chip 固定 JPG 且不可点击。

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-06-30 11:16:28 +08:00
parent e91a46c315
commit c71b45bdfd
29 changed files with 2097 additions and 256 deletions

View File

@ -18,7 +18,7 @@
1. `SonyCameraService.connect()` 启动 USB 设备搜索。
2. 授权通过后打开 ICC 会话并执行 Sony SDIO 握手。
3. 拍摄事件触发 PTP 下载,文件写入 `Documents/CameraDownloads/`
3. 拍摄事件触发 PTP 下载,文件写入 `Documents/CameraDownloads/`,持久化路径为相对路径(如 `CameraDownloads/1730_DSC.JPG`
4. 通过 `onNewAsset` 回调通知上层有新照片。
## 依赖

View File

@ -7,16 +7,74 @@
import Foundation
///
///
enum CameraDownloadStorage {
static let cameraDownloadsFolderName = "CameraDownloads"
/// Documents
static var documentsDirectory: URL {
FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]
}
///
static var downloadsDirectory: URL {
let documents = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]
let directory = documents.appendingPathComponent("CameraDownloads", isDirectory: true)
let directory = documentsDirectory.appendingPathComponent(cameraDownloadsFolderName, isDirectory: true)
try? FileManager.default.createDirectory(at: directory, withIntermediateDirectories: true)
return directory
}
/// Documents `CameraDownloads/1730_DSC.JPG`
static func relativePath(for url: URL) -> String {
"\(cameraDownloadsFolderName)/\(url.lastPathComponent)"
}
/// URL
static func resolveLocalURL(from storedPath: String) -> URL? {
guard !storedPath.isEmpty else { return nil }
if storedPath.hasPrefix("http://") || storedPath.hasPrefix("https://") {
return URL(string: storedPath)
}
if storedPath.hasPrefix("/") {
let legacyURL = URL(fileURLWithPath: storedPath)
if FileManager.default.fileExists(atPath: legacyURL.path) {
return legacyURL
}
let candidate = downloadsDirectory.appendingPathComponent(legacyURL.lastPathComponent)
if FileManager.default.fileExists(atPath: candidate.path) {
return candidate
}
return legacyURL
}
return documentsDirectory.appendingPathComponent(storedPath)
}
/// CameraDownloads
static func migrateStoredPath(_ storedPath: String) -> String? {
guard storedPath.hasPrefix("/") else { return storedPath }
let legacyURL = URL(fileURLWithPath: storedPath)
if FileManager.default.fileExists(atPath: legacyURL.path) {
return relativePath(for: legacyURL)
}
let candidate = downloadsDirectory.appendingPathComponent(legacyURL.lastPathComponent)
if FileManager.default.fileExists(atPath: candidate.path) {
return relativePath(for: candidate)
}
return relativePath(for: legacyURL)
}
/// CameraDownloads
static func isInDownloadsDirectory(_ url: URL) -> Bool {
let downloadsPath = downloadsDirectory.standardizedFileURL.path
let filePath = url.standardizedFileURL.path
return filePath.hasPrefix(downloadsPath) || filePath.hasPrefix("/private" + downloadsPath)
}
/// URL
static func uniqueLocalURL(for filename: String) -> URL {
let timestamp = Int(Date().timeIntervalSince1970)
@ -44,4 +102,13 @@ enum CameraDownloadStorage {
}
return name
}
/// UI file URL
static func previewURLString(from storedPath: String) -> String {
guard let url = resolveLocalURL(from: storedPath),
FileManager.default.fileExists(atPath: url.path) else {
return ""
}
return url.absoluteString
}
}