Commit f18e511d authored by CZ1004's avatar CZ1004

Merge branch 'Advertisement' of…

Merge branch 'Advertisement' of http://gitlab.zhangxindiet.com/ShuMing/phonemanager into Advertisement

* 'Advertisement' of http://gitlab.zhangxindiet.com/ShuMing/phonemanager:
  引入新相册资源管理类
  垃圾桶数据库
  引导页动画
  垃圾桶页面

# Conflicts:
#	PhoneManager/Class/Page/Home/View/DateSelectButtonView.swift
#	PhoneManager/Class/Page/Home/View/ResourceFilterBoxView.swift
#	PhoneManager/Class/Page/Home/View/YearMonthPickerView.swift
#	PhoneManager/Class/Page/Home/View/cell/ResourceFilterBoxTableViewCell.swift
parents 36d4c4e3 21848fa1
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "Frame@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "Frame@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "Frame@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "Frame@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
//
// TrashDatabase.swift
// PhoneManager
//
// Created by edy on 2025/5/12.
//
import Foundation
import SQLite3
class TrashDatabase {
static let shared = TrashDatabase()
private var db: OpaquePointer?
private let dbPath: String
private init() {
let fileURL = try! FileManager.default
.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: false)
.appendingPathComponent("trash.sqlite")
dbPath = fileURL.path
if sqlite3_open(dbPath, &db) != SQLITE_OK {
print("无法打开数据库")
return
}
createTable()
}
private func createTable() {
let createTableString = """
CREATE TABLE IF NOT EXISTS trash(
localIdentifier TEXT PRIMARY KEY,
assetSize DOUBLE,
createDate DOUBLE,
mediaType INTEGER
);
"""
var createTableStatement: OpaquePointer?
if sqlite3_prepare_v2(db, createTableString, -1, &createTableStatement, nil) == SQLITE_OK {
if sqlite3_step(createTableStatement) == SQLITE_DONE {
print("成功创建trash表")
} else {
print("创建表失败")
}
} else {
print("创建表语句准备失败")
}
sqlite3_finalize(createTableStatement)
}
// 插入数据(带去重检查)
func insert(localIdentifier: String, assetSize: Double, createDate: Date, mediaType: Int) -> Bool {
// 先检查是否已存在相同的 localIdentifier
if let _ = query(localIdentifier: localIdentifier) {
// 如果已存在,则执行更新操作
return update(localIdentifier: localIdentifier, assetSize: assetSize, createDate: createDate, mediaType: mediaType)
}
// 如果不存在,执行插入操作
let insertStatementString = "INSERT INTO trash (localIdentifier, assetSize, createDate, mediaType) VALUES (?, ?, ?, ?);"
var insertStatement: OpaquePointer?
if sqlite3_prepare_v2(db, insertStatementString, -1, &insertStatement, nil) == SQLITE_OK {
sqlite3_bind_text(insertStatement, 1, (localIdentifier as NSString).utf8String, -1, nil)
sqlite3_bind_double(insertStatement, 2, assetSize)
sqlite3_bind_double(insertStatement, 3, createDate.timeIntervalSince1970)
sqlite3_bind_int(insertStatement, 4, Int32(mediaType))
if sqlite3_step(insertStatement) == SQLITE_DONE {
print("成功插入数据")
sqlite3_finalize(insertStatement)
return true
} else {
print("插入数据失败")
}
}
sqlite3_finalize(insertStatement)
return false
}
// 删除数据
func delete(localIdentifier: String) -> Bool {
let deleteStatementString = "DELETE FROM trash WHERE localIdentifier = ?;"
var deleteStatement: OpaquePointer?
if sqlite3_prepare_v2(db, deleteStatementString, -1, &deleteStatement, nil) == SQLITE_OK {
sqlite3_bind_text(deleteStatement, 1, (localIdentifier as NSString).utf8String, -1, nil)
if sqlite3_step(deleteStatement) == SQLITE_DONE {
print("成功删除数据")
sqlite3_finalize(deleteStatement)
return true
} else {
print("删除数据失败")
}
}
sqlite3_finalize(deleteStatement)
return false
}
// 更新数据
func update(localIdentifier: String, assetSize: Double, createDate: Date, mediaType: Int) -> Bool {
let updateStatementString = "UPDATE trash SET assetSize = ?, createDate = ?, mediaType = ? WHERE localIdentifier = ?;"
var updateStatement: OpaquePointer?
if sqlite3_prepare_v2(db, updateStatementString, -1, &updateStatement, nil) == SQLITE_OK {
sqlite3_bind_double(updateStatement, 1, assetSize)
sqlite3_bind_double(updateStatement, 2, createDate.timeIntervalSince1970)
sqlite3_bind_int(updateStatement, 3, Int32(mediaType))
sqlite3_bind_text(updateStatement, 4, (localIdentifier as NSString).utf8String, -1, nil)
if sqlite3_step(updateStatement) == SQLITE_DONE {
print("成功更新数据")
sqlite3_finalize(updateStatement)
return true
} else {
print("更新数据失败")
}
}
sqlite3_finalize(updateStatement)
return false
}
// 查询所有数据
func queryAll() -> [(localIdentifier: String, assetSize: Double, createDate: Date, mediaType: Int)] {
let queryStatementString = "SELECT * FROM trash;"
var queryStatement: OpaquePointer?
var result: [(localIdentifier: String, assetSize: Double, createDate: Date, mediaType: Int)] = []
if sqlite3_prepare_v2(db, queryStatementString, -1, &queryStatement, nil) == SQLITE_OK {
while sqlite3_step(queryStatement) == SQLITE_ROW {
let localIdentifier = String(cString: sqlite3_column_text(queryStatement, 0))
let assetSize = sqlite3_column_double(queryStatement, 1)
let createDate = Date(timeIntervalSince1970: sqlite3_column_double(queryStatement, 2))
let mediaType = Int(sqlite3_column_int(queryStatement, 3))
result.append((localIdentifier, assetSize, createDate, mediaType))
}
}
sqlite3_finalize(queryStatement)
return result
}
// 根据localIdentifier查询单条数据
func query(localIdentifier: String) -> (localIdentifier: String, assetSize: Double, createDate: Date, mediaType: Int)? {
let queryStatementString = "SELECT * FROM trash WHERE localIdentifier = ?;"
var queryStatement: OpaquePointer?
if sqlite3_prepare_v2(db, queryStatementString, -1, &queryStatement, nil) == SQLITE_OK {
sqlite3_bind_text(queryStatement, 1, (localIdentifier as NSString).utf8String, -1, nil)
if sqlite3_step(queryStatement) == SQLITE_ROW {
let localIdentifier = String(cString: sqlite3_column_text(queryStatement, 0))
let assetSize = sqlite3_column_double(queryStatement, 1)
let createDate = Date(timeIntervalSince1970: sqlite3_column_double(queryStatement, 2))
let mediaType = Int(sqlite3_column_int(queryStatement, 3))
sqlite3_finalize(queryStatement)
return (localIdentifier, assetSize, createDate, mediaType)
}
}
sqlite3_finalize(queryStatement)
return nil
}
// 根据mediaType查询数据
func queryByMediaType(_ mediaType: Int) -> [(localIdentifier: String, assetSize: Double, createDate: Date, mediaType: Int)] {
let queryStatementString = "SELECT * FROM trash WHERE mediaType = ?;"
var queryStatement: OpaquePointer?
var result: [(localIdentifier: String, assetSize: Double, createDate: Date, mediaType: Int)] = []
if sqlite3_prepare_v2(db, queryStatementString, -1, &queryStatement, nil) == SQLITE_OK {
sqlite3_bind_int(queryStatement, 1, Int32(mediaType))
while sqlite3_step(queryStatement) == SQLITE_ROW {
let localIdentifier = String(cString: sqlite3_column_text(queryStatement, 0))
let assetSize = sqlite3_column_double(queryStatement, 1)
let createDate = Date(timeIntervalSince1970: sqlite3_column_double(queryStatement, 2))
let mediaType = Int(sqlite3_column_int(queryStatement, 3))
result.append((localIdentifier, assetSize, createDate, mediaType))
}
}
sqlite3_finalize(queryStatement)
return result
}
deinit {
sqlite3_close(db)
}
}
//
// ActorManager.swift
// CleanPhoto
//
// Created by edy on 2025/5/9.
//
import Foundation
import UIKit
// 图片状态管理 actor
actor PhotoSimilarStateManager {
private var timeGroups: [TimeGroupModel] = []
private var similarGroups: [SimilarGroupModel] = []
private var pendingSimilarGroups: [SimilarGroupModel] = []
private var processedGroupCount: Int = 0
private var assetsImageCache: [String: UIImage] = [:]
private var hashCache: [String: String] = [:]
func appendTimeGroup(_ group: TimeGroupModel) {
timeGroups.append(group)
}
func appendSimilarGroup(_ group: SimilarGroupModel) {
pendingSimilarGroups.append(group)
processedGroupCount += 1
}
func getAllTimeGroups() -> [TimeGroupModel] {
return timeGroups
}
func getpendingSimilarGroups() -> [SimilarGroupModel] {
return pendingSimilarGroups
}
func getAllSimilarGroups() -> [SimilarGroupModel] {
return similarGroups
}
func getCachedImage(for identifier: String) -> UIImage? {
return assetsImageCache[identifier]
}
func setCachedImage(_ image: UIImage, for identifier: String) {
assetsImageCache[identifier] = image
}
func shouldSavePendingGroups() -> Bool {
return processedGroupCount >= 10
}
func getCachedHash(for identifier: String) async -> String? {
return hashCache[identifier]
}
func setCachedHash(_ hash: String, for identifier: String) async {
hashCache[identifier] = hash
}
func savePendingGroups() {
similarGroups.append(contentsOf: pendingSimilarGroups)
pendingSimilarGroups.removeAll()
processedGroupCount = 0
}
func loadStoredData(timeGroups: [TimeGroupModel], similarGroups: [SimilarGroupModel]) {
self.timeGroups = timeGroups
self.similarGroups = similarGroups
}
}
// 截图状态管理 actor
actor ScreenshotSimilarStateManager {
private var timeGroups: [TimeGroupModel] = []
private var similarGroups: [SimilarGroupModel] = []
private var pendingSimilarGroups: [SimilarGroupModel] = []
private var processedGroupCount: Int = 0
private var assetsImageCache: [String: UIImage] = [:]
private var hashCache: [String: String] = [:]
func appendTimeGroup(_ group: TimeGroupModel) {
timeGroups.append(group)
}
func appendSimilarGroup(_ group: SimilarGroupModel) {
pendingSimilarGroups.append(group)
processedGroupCount += 1
}
func getAllTimeGroups() -> [TimeGroupModel] {
return timeGroups
}
func getpendingSimilarGroups() -> [SimilarGroupModel] {
return pendingSimilarGroups
}
func getAllSimilarGroups() -> [SimilarGroupModel] {
return similarGroups
}
func getCachedImage(for identifier: String) -> UIImage? {
return assetsImageCache[identifier]
}
func setCachedImage(_ image: UIImage, for identifier: String) {
assetsImageCache[identifier] = image
}
func shouldSavePendingGroups() -> Bool {
return processedGroupCount >= 10
}
func getCachedHash(for identifier: String) async -> String? {
return hashCache[identifier]
}
func setCachedHash(_ hash: String, for identifier: String) async {
hashCache[identifier] = hash
}
func savePendingGroups() {
similarGroups.append(contentsOf: pendingSimilarGroups)
pendingSimilarGroups.removeAll()
processedGroupCount = 0
}
func loadStoredData(timeGroups: [TimeGroupModel], similarGroups: [SimilarGroupModel]) {
self.timeGroups = timeGroups
self.similarGroups = similarGroups
}
}
// 视频状态管理 actor
actor VideoSimilarStateManager {
private var timeGroups: [TimeGroupModel] = []
private var similarGroups: [SimilarGroupModel] = []
private var pendingSimilarGroups: [SimilarGroupModel] = []
private var processedGroupCount: Int = 0
private var assetsImageCache: [String: UIImage] = [:]
private var hashCache: [String: String] = [:]
func appendTimeGroup(_ group: TimeGroupModel) {
timeGroups.append(group)
}
func appendSimilarGroup(_ group: SimilarGroupModel) {
pendingSimilarGroups.append(group)
processedGroupCount += 1
}
func getAllTimeGroups() -> [TimeGroupModel] {
return timeGroups
}
func getpendingSimilarGroups() -> [SimilarGroupModel] {
return pendingSimilarGroups
}
func getAllSimilarGroups() -> [SimilarGroupModel] {
return similarGroups
}
func getCachedImage(for identifier: String) -> UIImage? {
return assetsImageCache[identifier]
}
func setCachedImage(_ image: UIImage, for identifier: String) {
assetsImageCache[identifier] = image
}
func shouldSavePendingGroups() -> Bool {
return processedGroupCount >= 10
}
func getCachedHash(for identifier: String) async -> String? {
return hashCache[identifier]
}
func setCachedHash(_ hash: String, for identifier: String) async {
hashCache[identifier] = hash
}
func savePendingGroups() {
similarGroups.append(contentsOf: pendingSimilarGroups)
pendingSimilarGroups.removeAll()
processedGroupCount = 0
}
func loadStoredData(timeGroups: [TimeGroupModel], similarGroups: [SimilarGroupModel]) {
self.timeGroups = timeGroups
self.similarGroups = similarGroups
}
}
actor PhotoDuplicateStateManager {
private var duplicateGroups: [DuplicateGroupModel] = []
private var pendingDuplicateGroups: [DuplicateGroupModel] = []
// 缓存
private var imageCache: [String: UIImage] = [:]
private var hashCache: [String: String] = [:]
// MARK: - 公共方法
func loadStoredData(duplicateGroups: [DuplicateGroupModel]) {
self.duplicateGroups = duplicateGroups
}
func getAllDuplicateGroups() -> [DuplicateGroupModel] {
return duplicateGroups
}
func appendDuplicateGroup(_ group: DuplicateGroupModel) {
pendingDuplicateGroups.append(group)
}
func shouldSavePendingGroups() -> Bool {
return pendingDuplicateGroups.count >= 5
}
func savePendingGroups() {
duplicateGroups.append(contentsOf: pendingDuplicateGroups)
pendingDuplicateGroups.removeAll()
}
func getPendingDuplicateGroups() -> [DuplicateGroupModel] {
return pendingDuplicateGroups
}
// MARK: - 缓存相关
func getCachedImage(for identifier: String) -> UIImage? {
return imageCache[identifier]
}
func setCachedImage(_ image: UIImage, for identifier: String) {
imageCache[identifier] = image
}
func getCachedHash(for identifier: String) -> String? {
return hashCache[identifier]
}
func setCachedHash(_ hash: String, for identifier: String) {
hashCache[identifier] = hash
}
}
This diff is collapsed.
//
// AssetModel.swift
// CleanPhoto
//
// Created by edy on 2025/5/7.
//
import Foundation
struct AssetModel :Codable,Hashable {
var localIdentifier : String
var assetSize : Double
var createDate : Date
var mediaType:Int // 1 图片 2视频
init(localIdentifier: String, assetSize: Double, createDate: Date,mediaType:Int = 1) {
self.localIdentifier = localIdentifier
self.assetSize = assetSize
self.createDate = createDate
self.mediaType = mediaType
}
func hash(into hasher: inout Hasher) {
hasher.combine(localIdentifier)
hasher.combine(assetSize)
hasher.combine(createDate)
hasher.combine(mediaType)
}
static func ==(lhs: AssetModel, rhs: AssetModel) -> Bool {
return lhs.localIdentifier == rhs.localIdentifier &&
lhs.assetSize == rhs.assetSize &&
lhs.createDate == rhs.createDate && lhs.mediaType == rhs.mediaType
}
}
struct AssetFileModel:Codable{
var videoAssets:[AssetModel] = []
var otherAssets:[AssetModel] = []
var screenShotAssets:[AssetModel] = []
var photosAssets:[AssetModel] = []
}
// 添加媒体类型枚举
enum MediaType {
case video
case screenshot
case photo
case other
}
extension MediaType{
var dbValue:String{
switch self {
case .video:
return "video"
case .screenshot:
return "screenshot"
case .photo:
return "photo"
case .other:
return "other"
}
}
}
// 时间组模型
struct TimeGroupModel: Codable {
let groupId: String
let startTime: TimeInterval
let endTime: TimeInterval
var isProcessed: Bool
}
// 相似图片组模型
struct SimilarGroupModel: Codable {
let groupId: String
var assets: [AssetModel]
}
// 重复图片组模型
struct DuplicateGroupModel: Codable {
let groupId: String
let assets: [AssetModel]
}
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment