- Shared/ layer compiled into the app and three new extension targets: rule snapshots in the app group, the usage ledger, monitoring-plan naming, and LimitEnforcement (shared, unit-tested event reactions) - RuleScheduler mirrors rules to the app group and reconciles DeviceActivity monitoring: one daily 00:00-23:59 activity per limit rule, with a cumulative usage-threshold event per budget minute for time limits; activities restart only when their configuration changes (a restart resets threshold accounting) - OpenAppLockMonitor (DeviceActivityMonitor): midnight budget resets, records usage minutes, shields at the budget, re-shields when a granted open session ends - OpenAppLockShieldConfig: open-limit shields show 'Opened X of N times today' with an 'Open (Y left)' secondary button - OpenAppLockShieldAction: an Open press spends one open, lifts the rule's shield, and starts the ~15-minute one-shot session - extensions are classic NSExtension app extensions (the ExtensionKit product type expects an @main entry and made the app fail to install); shield-store tracking moved to app-group defaults so the app and extensions see one consistent set - maps iOS 26's new .approvedWithDataAccess authorization status to approved (it previously fell through @unknown default to notDetermined) - shares the OpenAppLock scheme (Xcode dropped the autocreated one when targets were added) Co-Authored-By: Claude <noreply@anthropic.com>
47 lines
1.7 KiB
Swift
47 lines
1.7 KiB
Swift
//
|
|
// DeviceActivityMonitorExtension.swift
|
|
// OpenAppLockMonitor
|
|
//
|
|
|
|
import DeviceActivity
|
|
import Foundation
|
|
|
|
/// Background half of limit-rule enforcement. The app schedules a daily
|
|
/// activity per limit rule (with per-minute usage checkpoints for time
|
|
/// limits); this extension reacts: it resets shields at midnight, records
|
|
/// usage minutes, blocks at the budget, and ends granted open sessions.
|
|
final class DeviceActivityMonitorExtension: DeviceActivityMonitor {
|
|
private var enforcement: LimitEnforcement {
|
|
LimitEnforcement(
|
|
snapshots: RuleSnapshotStore(),
|
|
ledger: UsageLedger(),
|
|
shields: ManagedSettingsShieldController()
|
|
)
|
|
}
|
|
|
|
override func intervalDidStart(for activity: DeviceActivityName) {
|
|
super.intervalDidStart(for: activity)
|
|
if let ruleID = MonitoringPlan.ruleID(fromDailyActivityName: activity.rawValue) {
|
|
enforcement.handleDayStart(ruleID: ruleID)
|
|
}
|
|
}
|
|
|
|
override func intervalDidEnd(for activity: DeviceActivityName) {
|
|
super.intervalDidEnd(for: activity)
|
|
if let ruleID = MonitoringPlan.ruleID(fromSessionActivityName: activity.rawValue) {
|
|
enforcement.handleOpenSessionEnded(ruleID: ruleID)
|
|
DeviceActivityCenter().stopMonitoring([activity])
|
|
}
|
|
}
|
|
|
|
override func eventDidReachThreshold(
|
|
_ event: DeviceActivityEvent.Name, activity: DeviceActivityName
|
|
) {
|
|
super.eventDidReachThreshold(event, activity: activity)
|
|
guard let ruleID = MonitoringPlan.ruleID(fromDailyActivityName: activity.rawValue),
|
|
let minutes = MonitoringPlan.minutes(fromEventName: event.rawValue)
|
|
else { return }
|
|
enforcement.handleUsageMinutes(minutes, ruleID: ruleID)
|
|
}
|
|
}
|