Initial commit
Whisper model weights excluded from git — auto-downloaded at first Xcode build via Scripts/download_whisper_model.sh (~600 MB, one-time).
This commit is contained in:
13
LockInBroShieldAction/Info.plist
Normal file
13
LockInBroShieldAction/Info.plist
Normal file
@@ -0,0 +1,13 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>NSExtension</key>
|
||||
<dict>
|
||||
<key>NSExtensionPointIdentifier</key>
|
||||
<string>com.apple.ManagedSettings.shield-action-service</string>
|
||||
<key>NSExtensionPrincipalClass</key>
|
||||
<string>$(PRODUCT_MODULE_NAME).ShieldActionExtension</string>
|
||||
</dict>
|
||||
</dict>
|
||||
</plist>
|
||||
12
LockInBroShieldAction/LockInBroShieldAction.entitlements
Normal file
12
LockInBroShieldAction/LockInBroShieldAction.entitlements
Normal file
@@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>com.apple.developer.family-controls</key>
|
||||
<true/>
|
||||
<key>com.apple.security.application-groups</key>
|
||||
<array>
|
||||
<string>group.com.adipu.LockInBroMobile</string>
|
||||
</array>
|
||||
</dict>
|
||||
</plist>
|
||||
114
LockInBroShieldAction/ShieldActionExtension.swift
Normal file
114
LockInBroShieldAction/ShieldActionExtension.swift
Normal file
@@ -0,0 +1,114 @@
|
||||
//
|
||||
// ShieldActionExtension.swift
|
||||
// LockInBroShieldAction
|
||||
//
|
||||
// Handles shield button taps:
|
||||
// Primary "Back to Focus" → closes the distraction app
|
||||
// Secondary "X more min" → dismisses the shield (user can keep using the app;
|
||||
// the shield will reappear on the next monitoring interval reset)
|
||||
//
|
||||
|
||||
import ManagedSettings
|
||||
import DeviceActivity
|
||||
import FamilyControls
|
||||
import Foundation
|
||||
|
||||
class ShieldActionExtension: ShieldActionDelegate {
|
||||
|
||||
private let store = ManagedSettingsStore(named: .lockinbro)
|
||||
|
||||
override func handle(
|
||||
action: ShieldAction,
|
||||
for application: ApplicationToken,
|
||||
completionHandler: @escaping (ShieldActionResponse) -> Void
|
||||
) {
|
||||
switch action {
|
||||
case .primaryButtonPressed:
|
||||
// "Back to Focus" — close the distraction app
|
||||
completionHandler(.close)
|
||||
case .secondaryButtonPressed:
|
||||
store.shield.applications = nil
|
||||
store.shield.applicationCategories = nil
|
||||
grantOneMoreMinute()
|
||||
completionHandler(.none)
|
||||
default:
|
||||
completionHandler(.close)
|
||||
}
|
||||
}
|
||||
|
||||
override func handle(
|
||||
action: ShieldAction,
|
||||
for webDomain: WebDomainToken,
|
||||
completionHandler: @escaping (ShieldActionResponse) -> Void
|
||||
) {
|
||||
switch action {
|
||||
case .primaryButtonPressed:
|
||||
completionHandler(.close)
|
||||
case .secondaryButtonPressed:
|
||||
store.shield.applications = nil
|
||||
store.shield.applicationCategories = nil
|
||||
grantOneMoreMinute()
|
||||
completionHandler(.none)
|
||||
default:
|
||||
completionHandler(.close)
|
||||
}
|
||||
}
|
||||
|
||||
override func handle(
|
||||
action: ShieldAction,
|
||||
for category: ActivityCategoryToken,
|
||||
completionHandler: @escaping (ShieldActionResponse) -> Void
|
||||
) {
|
||||
switch action {
|
||||
case .primaryButtonPressed:
|
||||
completionHandler(.close)
|
||||
case .secondaryButtonPressed:
|
||||
store.shield.applications = nil
|
||||
store.shield.applicationCategories = nil
|
||||
grantOneMoreMinute()
|
||||
completionHandler(.none)
|
||||
default:
|
||||
completionHandler(.close)
|
||||
}
|
||||
}
|
||||
private func grantOneMoreMinute() {
|
||||
let defaults = UserDefaults(suiteName: "group.com.adipu.LockInBroMobile")
|
||||
guard let data = defaults?.data(forKey: "screenTimeSelection"),
|
||||
let selection = try? JSONDecoder().decode(FamilyActivitySelection.self, from: data) else {
|
||||
return
|
||||
}
|
||||
|
||||
let center = DeviceActivityCenter()
|
||||
let now = Date()
|
||||
var startComp = Calendar.current.dateComponents([.hour, .minute], from: now)
|
||||
if startComp.hour == 23 && startComp.minute == 59 {
|
||||
startComp.hour = 0
|
||||
startComp.minute = 0
|
||||
}
|
||||
|
||||
let schedule = DeviceActivitySchedule(
|
||||
intervalStart: startComp,
|
||||
intervalEnd: DateComponents(hour: 23, minute: 59),
|
||||
repeats: false
|
||||
)
|
||||
|
||||
var events: [DeviceActivityEvent.Name: DeviceActivityEvent] = [:]
|
||||
let threshold = DateComponents(minute: 1)
|
||||
|
||||
for token in selection.applicationTokens {
|
||||
let eventName = DeviceActivityEvent.Name("dist_ext_\(token.hashValue)")
|
||||
events[eventName] = DeviceActivityEvent(applications: [token], threshold: threshold)
|
||||
}
|
||||
for token in selection.categoryTokens {
|
||||
let eventName = DeviceActivityEvent.Name("dist_cat_ext_\(token.hashValue)")
|
||||
events[eventName] = DeviceActivityEvent(categories: [token], threshold: threshold)
|
||||
}
|
||||
|
||||
let activityName = DeviceActivityName("lockinbro_extension_1m")
|
||||
try? center.startMonitoring(activityName, during: schedule, events: events)
|
||||
}
|
||||
}
|
||||
|
||||
extension ManagedSettingsStore.Name {
|
||||
static let lockinbro = ManagedSettingsStore.Name("lockinbro")
|
||||
}
|
||||
Reference in New Issue
Block a user