| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339 |
- //
- // BackgroundTasks.swift
- // map_mapkit_ios
- //
- // Created by 诺诺诺的言 on 2025/7/14.
- //
- import BackgroundTasks
- import Combine
- @available(iOS 13.0, *)
- class BackgroundTaskManager {
- static let shared = BackgroundTaskManager()
- private let backgroundTaskIdentifier = "com.your.app.screenlock.monitor"
-
- func registerBackgroundTask() {
- BGTaskScheduler.shared.register(
- forTaskWithIdentifier: backgroundTaskIdentifier,
- using: nil
- ) { task in
- self.handleBackgroundTask(task: task as! BGProcessingTask)
- }
- }
-
- func scheduleBackgroundTask() {
- let request = BGProcessingTaskRequest(identifier: backgroundTaskIdentifier)
- request.requiresNetworkConnectivity = false
- request.requiresExternalPower = false
- request.earliestBeginDate = Date(timeIntervalSinceNow: 60) // 1分钟后
-
- do {
- try BGTaskScheduler.shared.submit(request)
- } catch {
- print("无法提交后台任务: \(error.localizedDescription)")
- }
- }
-
- private func handleBackgroundTask(task: BGProcessingTask) {
- // 设置任务过期处理
- task.expirationHandler = {
- task.setTaskCompleted(success: false)
- }
-
- // 开始监测
- ScreenLockMonitor.shared.startMonitoringInBackground()
-
- // 安排下一个任务
- self.scheduleBackgroundTask()
-
- task.setTaskCompleted(success: true)
- }
- }
- import Foundation
- // 定义屏幕事件类型
- struct ScreenEvent: Codable {
- let timestamp: Date
- let eventType: EventType
-
- enum EventType: String, Codable {
- case lock = "锁屏"
- case unlock = "解锁"
- }
- }
- @available(iOS 13.0, *)
- class ScreenLockMonitor {
- static let shared = ScreenLockMonitor()
- private var lastState: Bool?
- private let stateChangeSubject = PassthroughSubject<ScreenEvent, Never>()
-
- var stateChangePublisher: AnyPublisher<ScreenEvent, Never> {
- stateChangeSubject.eraseToAnyPublisher()
- }
-
- func startMonitoringInBackground() {
- NotificationCenter.default.addObserver(
- self,
- selector: #selector(protectedDataWillBecomeUnavailable),
- name: UIApplication.protectedDataWillBecomeUnavailableNotification,
- object: nil
- )
-
- NotificationCenter.default.addObserver(
- self,
- selector: #selector(protectedDataDidBecomeAvailable),
- name: UIApplication.protectedDataDidBecomeAvailableNotification,
- object: nil
- )
-
- // 启动时检查当前状态
- checkInitialState()
- }
-
- private func checkInitialState() {
- let isProtectedDataAvailable = UIApplication.shared.isProtectedDataAvailable
- if lastState != isProtectedDataAvailable {
- lastState = isProtectedDataAvailable
- let event = ScreenEvent(
- timestamp: Date(),
- eventType: isProtectedDataAvailable ? .unlock : .lock
- )
- persistEvent(event: event)
- }
- }
-
- @objc private func protectedDataWillBecomeUnavailable() {
- recordEvent(eventType: .lock)
- }
-
- @objc private func protectedDataDidBecomeAvailable() {
- recordEvent(eventType: .unlock)
- }
-
- private func recordEvent(eventType: ScreenEvent.EventType) {
- let event = ScreenEvent(
- timestamp: Date(),
- eventType: eventType
- )
- persistEvent(event: event)
- stateChangeSubject.send(event)
- }
-
- private func persistEvent(event: ScreenEvent) {
- // 使用CoreData持久化存储
- //CoreDataManager.shared.saveScreenEvent(event)
- print("eventsfsfdsd---\(event.timestamp)---\(event.eventType)")
-
- // 同时写入文件备份
- FileStorageManager.shared.saveEvent(event)
- }
- }
- import CoreData
- class CoreDataManager {
- static let shared = CoreDataManager()
- private let persistentContainer: NSPersistentContainer
-
- private init() {
- persistentContainer = NSPersistentContainer(name: "ScreenEventsModel")
- persistentContainer.loadPersistentStores { description, error in
- if let error = error {
- fatalError("无法加载CoreData存储: \(error)")
- }
- }
- }
-
- func getEventsBetween(startDate: Date, endDate: Date) -> [ScreenEvent] {
- let request: NSFetchRequest<ScreenEventEntity> = ScreenEventEntity.fetchRequest()
-
- // 设置时间范围谓词
- request.predicate = NSPredicate(
- format: "timestamp >= %@ AND timestamp <= %@",
- startDate as NSDate,
- endDate as NSDate
- )
-
- // 按时间升序排列
- request.sortDescriptors = [NSSortDescriptor(key: "timestamp", ascending: true)]
-
- do {
- let results = try persistentContainer.viewContext.fetch(request)
- return results.compactMap {
- guard let timestamp = $0.timestamp else { return nil }
- return ScreenEvent(
- timestamp: timestamp,
- eventType: ScreenEvent.EventType(rawValue: $0.eventType ?? "lock") ?? .lock
- )
- }
- } catch {
- print("查询事件失败: \(error)")
- return []
- }
- }
-
- func saveScreenEvent(_ event: ScreenEvent) {
- let context = persistentContainer.viewContext
- let entity = ScreenEventEntity(context: context)
- entity.timestamp = event.timestamp
- entity.eventType = event.eventType.rawValue
-
- do {
- try context.save()
- } catch {
- print("保存事件失败: \(error)")
- }
- }
-
- func getAllEvents() -> [ScreenEvent] {
- let request: NSFetchRequest<ScreenEventEntity> = ScreenEventEntity.fetchRequest()
- request.sortDescriptors = [NSSortDescriptor(key: "timestamp", ascending: false)]
-
- do {
- let results = try persistentContainer.viewContext.fetch(request)
- return results.map {
- ScreenEvent(
- timestamp: $0.timestamp ?? Date(),
- eventType: ScreenEvent.EventType(rawValue: $0.eventType ?? "lock") ?? .lock
- )
- }
- } catch {
- print("获取事件失败: \(error)")
- return []
- }
- }
- }
- // CoreData实体
- public class ScreenEventEntity: NSManagedObject {
- @NSManaged public var timestamp: Date?
- @NSManaged public var eventType: String?
- }
- extension ScreenEventEntity {
- @nonobjc public class func fetchRequest() -> NSFetchRequest<ScreenEventEntity> {
- return NSFetchRequest<ScreenEventEntity>(entityName: "ScreenEventEntity")
- }
- }
- class FileStorageManager {
- static let shared = FileStorageManager()
- private let fileURL = FileManager.default
- .urls(for: .documentDirectory, in: .userDomainMask)[0]
- .appendingPathComponent("screenEventsBackup.json")
-
- func saveEvent(_ event: ScreenEvent) {
- var existingEvents = loadEvents()
- existingEvents.append(event)
-
- do {
- let data = try JSONEncoder().encode(existingEvents)
- try data.write(to: fileURL)
- } catch {
- print("文件存储失败: \(error)")
- }
- }
-
- func loadEvents() -> [ScreenEvent] {
- do {
- let data = try Data(contentsOf: fileURL)
- return try JSONDecoder().decode([ScreenEvent].self, from: data)
- } catch {
- return []
- }
- }
-
- /// 从JSON文件中获取时间范围内的事件
- func getEventsBetween(startDate: Date, endDate: Date) -> [ScreenEvent] {
- let allEvents = loadEvents()
- return allEvents.filter {
- $0.timestamp >= startDate && $0.timestamp <= endDate
- }.sorted {
- $0.timestamp < $1.timestamp
- }
- }
- }
- class EventQueryService {
- static let shared = EventQueryService()
-
- /// 获取时间范围内的事件并转换为字典列表
- func getEventPairsBetween(startDate: Date, endDate: Date) -> [[String: Any]] {
- // 获取原始事件(已按时间排序)
- let events = getOrderedEventsBetween(startDate: startDate, endDate: endDate)
- var result = [[String: Any]]()
- var currentLockEvent: ScreenEvent?
-
- for event in events {
- switch event.eventType {
- case .lock:
- // 如果遇到新的锁屏事件而前一个未配对,则保存前一个不完整记录
- if let lockEvent = currentLockEvent {
- result.append([
- "startTime": lockEvent.timestamp,
- "endTime": event.timestamp // 异常情况:两个锁屏事件之间没有解锁
- ])
- }
- currentLockEvent = event
-
- case .unlock:
- if let lockEvent = currentLockEvent {
- // 正常情况:锁屏-解锁配对
- result.append([
- "startTime": lockEvent.timestamp,
- "endTime": event.timestamp
- ])
- currentLockEvent = nil
- } else {
- // 异常情况:第一个事件是解锁事件
- result.append([
- "startTime": startDate, // 用查询开始时间作为默认值
- "endTime": event.timestamp
- ])
- }
- }
- }
-
- // 处理最后一个未配对的锁屏事件
- if let lockEvent = currentLockEvent {
- result.append([
- "startTime": lockEvent.timestamp,
- "endTime": 0 // 用查询结束时间作为默认值
- ])
- }
-
- return result
- }
-
- /// 私有方法:获取排序后的事件列表
- private func getOrderedEventsBetween(startDate: Date, endDate: Date) -> [ScreenEvent] {
- // 从CoreData获取
- // let coreDataEvents = CoreDataManager.shared.getEventsBetween(
- // startDate: startDate,
- // endDate: endDate
- // )
-
- // 从文件备份获取
- let fileEvents = FileStorageManager.shared.getEventsBetween(
- startDate: startDate,
- endDate: endDate
- )
-
- // 合并、去重并排序
- var allEvents = fileEvents;//Array<Any>(Set.init(from: fileEvents)) // 去重
- allEvents.sort { $0.timestamp < $1.timestamp } // 按时间升序
-
- return allEvents
- }
- }
|