- RuleUsage + UsageLedger: per-rule, per-day minutes/opens in app-group defaults (monotonic minutes, incrementing opens, midnight rollover by day-keying); MockUsageLedger for tests and seeded UI scenarios - usage-aware status: a limit rule whose daily budget is spent on an enabled day is active (blocking) until next midnight; Hard Mode gating and app-list locking honor it; soft unblock pauses until midnight - RuleEnforcer shields spent limit rules (always Block mode) while the app runs - new Usage section under Blocked Apps: '18m of 45m used today · 27m left', '2 of 5 opens today · 3 opens left', 'Blocked until tomorrow' - new 'limits' seed scenario + UI tests
50 lines
1.9 KiB
Swift
50 lines
1.9 KiB
Swift
//
|
|
// LaunchConfiguration.swift
|
|
// OpenAppLock
|
|
//
|
|
|
|
import Foundation
|
|
|
|
/// Launch-argument configuration used by UI tests: in-memory storage, mocked
|
|
/// Screen Time authorization, forced onboarding state, and seeded scenarios.
|
|
struct LaunchConfiguration: Equatable {
|
|
enum SeedScenario: String {
|
|
/// One actively blocking rule ("Work Time") and one upcoming rule ("Sleep").
|
|
case standard
|
|
/// An actively blocking Hard Mode rule ("Locked In") plus an upcoming rule.
|
|
case hardModeActive = "hard-mode-active"
|
|
/// Limit rules with seeded usage: "Time Keeper" (18m of 45m),
|
|
/// "Gate Keeper" (2 of 5 opens), and "Doom Scroll" (budget spent →
|
|
/// blocked until tomorrow).
|
|
case limits
|
|
}
|
|
|
|
var isUITesting = false
|
|
/// Forces the onboarding-completed flag at launch. Nil leaves stored state alone.
|
|
var onboardingCompleted: Bool?
|
|
var seedScenario: SeedScenario?
|
|
|
|
static let uiTestingFlag = "-ui-testing"
|
|
static let onboardingCompletedFlag = "-onboarding-completed"
|
|
static let onboardingRequiredFlag = "-onboarding-required"
|
|
static let seedScenarioPrefix = "-seed-scenario="
|
|
|
|
static func parse(arguments: [String]) -> LaunchConfiguration {
|
|
var config = LaunchConfiguration()
|
|
config.isUITesting = arguments.contains(uiTestingFlag)
|
|
if arguments.contains(onboardingCompletedFlag) {
|
|
config.onboardingCompleted = true
|
|
} else if arguments.contains(onboardingRequiredFlag) {
|
|
config.onboardingCompleted = false
|
|
}
|
|
if let seedArgument = arguments.first(where: { $0.hasPrefix(seedScenarioPrefix) }) {
|
|
config.seedScenario = SeedScenario(
|
|
rawValue: String(seedArgument.dropFirst(seedScenarioPrefix.count))
|
|
)
|
|
}
|
|
return config
|
|
}
|
|
|
|
static let current = parse(arguments: ProcessInfo.processInfo.arguments)
|
|
}
|