mirror of
https://github.com/FBukovina/Atoll.git
synced 2026-10-27 15:14:33 +00:00
Merge pull request #137 from Ebullioscopic/revamp/essentials
Update Live Activities Tab
This commit is contained in:
commit
af46e988dc
2 changed files with 198 additions and 140 deletions
|
|
@ -5692,6 +5692,10 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"Configure countdown style and lock screen widgets in the Calendar tab." : {
|
||||||
|
"comment" : "A footer for the \"Enable reminder live activity\" toggle in the Live Activities settings.",
|
||||||
|
"isCommentAutoGenerated" : true
|
||||||
|
},
|
||||||
"Configure how the timer looks inside the closed notch. Progress can render as a ring around the icon or as horizontal bars." : {
|
"Configure how the timer looks inside the closed notch. Progress can render as a ring around the icon or as horizontal bars." : {
|
||||||
|
|
||||||
},
|
},
|
||||||
|
|
@ -10831,6 +10835,10 @@
|
||||||
"comment" : "A label indicating that the music is live.",
|
"comment" : "A label indicating that the music is live.",
|
||||||
"isCommentAutoGenerated" : true
|
"isCommentAutoGenerated" : true
|
||||||
},
|
},
|
||||||
|
"Live Activities" : {
|
||||||
|
"comment" : "The title of the \"Live Activities\" screen.",
|
||||||
|
"isCommentAutoGenerated" : true
|
||||||
|
},
|
||||||
"Live Activity & Feedback" : {
|
"Live Activity & Feedback" : {
|
||||||
"comment" : "The header for the \"Live Activity & Feedback\" section in the lock screen settings.",
|
"comment" : "The header for the \"Live Activity & Feedback\" section in the lock screen settings.",
|
||||||
"isCommentAutoGenerated" : true
|
"isCommentAutoGenerated" : true
|
||||||
|
|
@ -11944,6 +11952,10 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"Media Live Activity" : {
|
||||||
|
"comment" : "A section header that lists the media live activity settings.",
|
||||||
|
"isCommentAutoGenerated" : true
|
||||||
|
},
|
||||||
"Media output" : {
|
"Media output" : {
|
||||||
"comment" : "Accessibility label describing the control that opens the media output selector.",
|
"comment" : "Accessibility label describing the control that opens the media output selector.",
|
||||||
"isCommentAutoGenerated" : true
|
"isCommentAutoGenerated" : true
|
||||||
|
|
@ -19687,6 +19699,10 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"Use the Media tab to configure sneak peek, lyrics, and floating media controls." : {
|
||||||
|
"comment" : "A footer for the \"Media Live Activity\" section, explaining how to configure the media controls.",
|
||||||
|
"isCommentAutoGenerated" : true
|
||||||
|
},
|
||||||
"Used" : {
|
"Used" : {
|
||||||
"comment" : "A label describing the percentage of memory that is currently in use.",
|
"comment" : "A label describing the percentage of memory that is currently in use.",
|
||||||
"isCommentAutoGenerated" : true,
|
"isCommentAutoGenerated" : true,
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,7 @@ import UniformTypeIdentifiers
|
||||||
|
|
||||||
private enum SettingsTab: String, CaseIterable, Identifiable {
|
private enum SettingsTab: String, CaseIterable, Identifiable {
|
||||||
case general
|
case general
|
||||||
|
case liveActivities
|
||||||
case appearance
|
case appearance
|
||||||
case lockScreen
|
case lockScreen
|
||||||
case media
|
case media
|
||||||
|
|
@ -41,6 +42,7 @@ private enum SettingsTab: String, CaseIterable, Identifiable {
|
||||||
var title: String {
|
var title: String {
|
||||||
switch self {
|
switch self {
|
||||||
case .general: return "General"
|
case .general: return "General"
|
||||||
|
case .liveActivities: return "Live Activities"
|
||||||
case .appearance: return "Appearance"
|
case .appearance: return "Appearance"
|
||||||
case .lockScreen: return "Lock Screen"
|
case .lockScreen: return "Lock Screen"
|
||||||
case .media: return "Media"
|
case .media: return "Media"
|
||||||
|
|
@ -64,6 +66,7 @@ private enum SettingsTab: String, CaseIterable, Identifiable {
|
||||||
var systemImage: String {
|
var systemImage: String {
|
||||||
switch self {
|
switch self {
|
||||||
case .general: return "gear"
|
case .general: return "gear"
|
||||||
|
case .liveActivities: return "waveform.path.ecg"
|
||||||
case .appearance: return "paintpalette"
|
case .appearance: return "paintpalette"
|
||||||
case .lockScreen: return "lock.laptopcomputer"
|
case .lockScreen: return "lock.laptopcomputer"
|
||||||
case .media: return "play.laptopcomputer"
|
case .media: return "play.laptopcomputer"
|
||||||
|
|
@ -87,6 +90,7 @@ private enum SettingsTab: String, CaseIterable, Identifiable {
|
||||||
var tint: Color {
|
var tint: Color {
|
||||||
switch self {
|
switch self {
|
||||||
case .general: return .blue
|
case .general: return .blue
|
||||||
|
case .liveActivities: return .pink
|
||||||
case .appearance: return .purple
|
case .appearance: return .purple
|
||||||
case .lockScreen: return .orange
|
case .lockScreen: return .orange
|
||||||
case .media: return .green
|
case .media: return .green
|
||||||
|
|
@ -362,6 +366,7 @@ struct SettingsView: View {
|
||||||
private var availableTabs: [SettingsTab] {
|
private var availableTabs: [SettingsTab] {
|
||||||
let ordered: [SettingsTab] = [
|
let ordered: [SettingsTab] = [
|
||||||
.general,
|
.general,
|
||||||
|
.liveActivities,
|
||||||
.appearance,
|
.appearance,
|
||||||
.lockScreen,
|
.lockScreen,
|
||||||
.media,
|
.media,
|
||||||
|
|
@ -554,13 +559,6 @@ struct SettingsView: View {
|
||||||
SettingsSearchEntry(tab: .general, title: "Show on a specific display", keywords: ["preferred screen", "display picker"], highlightID: SettingsTab.general.highlightID(for: "Show on a specific display")),
|
SettingsSearchEntry(tab: .general, title: "Show on a specific display", keywords: ["preferred screen", "display picker"], highlightID: SettingsTab.general.highlightID(for: "Show on a specific display")),
|
||||||
SettingsSearchEntry(tab: .general, title: "Automatically switch displays", keywords: ["auto switch", "displays"], highlightID: SettingsTab.general.highlightID(for: "Automatically switch displays")),
|
SettingsSearchEntry(tab: .general, title: "Automatically switch displays", keywords: ["auto switch", "displays"], highlightID: SettingsTab.general.highlightID(for: "Automatically switch displays")),
|
||||||
SettingsSearchEntry(tab: .general, title: "Hide Dynamic Island during screenshots & recordings", keywords: ["privacy", "screenshot", "recording"], highlightID: SettingsTab.general.highlightID(for: "Hide Dynamic Island during screenshots & recordings")),
|
SettingsSearchEntry(tab: .general, title: "Hide Dynamic Island during screenshots & recordings", keywords: ["privacy", "screenshot", "recording"], highlightID: SettingsTab.general.highlightID(for: "Hide Dynamic Island during screenshots & recordings")),
|
||||||
SettingsSearchEntry(tab: .general, title: "Enable Screen Recording Detection", keywords: ["screen recording", "indicator"], highlightID: SettingsTab.general.highlightID(for: "Enable Screen Recording Detection")),
|
|
||||||
SettingsSearchEntry(tab: .general, title: "Show Recording Indicator", keywords: ["recording indicator", "red dot"], highlightID: SettingsTab.general.highlightID(for: "Show Recording Indicator")),
|
|
||||||
SettingsSearchEntry(tab: .general, title: "Enable Focus Detection", keywords: ["focus", "do not disturb", "dnd"], highlightID: SettingsTab.general.highlightID(for: "Enable Focus Detection")),
|
|
||||||
SettingsSearchEntry(tab: .general, title: "Show Focus Indicator", keywords: ["focus icon", "moon"], highlightID: SettingsTab.general.highlightID(for: "Show Focus Indicator")),
|
|
||||||
SettingsSearchEntry(tab: .general, title: "Show Focus Label", keywords: ["focus label", "text"], highlightID: SettingsTab.general.highlightID(for: "Show Focus Label")),
|
|
||||||
SettingsSearchEntry(tab: .general, title: "Enable Camera Detection", keywords: ["camera", "privacy indicator"], highlightID: SettingsTab.general.highlightID(for: "Enable Camera Detection")),
|
|
||||||
SettingsSearchEntry(tab: .general, title: "Enable Microphone Detection", keywords: ["microphone", "privacy"], highlightID: SettingsTab.general.highlightID(for: "Enable Microphone Detection")),
|
|
||||||
SettingsSearchEntry(tab: .general, title: "Enable gestures", keywords: ["gestures", "trackpad"], highlightID: SettingsTab.general.highlightID(for: "Enable gestures")),
|
SettingsSearchEntry(tab: .general, title: "Enable gestures", keywords: ["gestures", "trackpad"], highlightID: SettingsTab.general.highlightID(for: "Enable gestures")),
|
||||||
SettingsSearchEntry(tab: .general, title: "Close gesture", keywords: ["pinch", "swipe"], highlightID: SettingsTab.general.highlightID(for: "Close gesture")),
|
SettingsSearchEntry(tab: .general, title: "Close gesture", keywords: ["pinch", "swipe"], highlightID: SettingsTab.general.highlightID(for: "Close gesture")),
|
||||||
SettingsSearchEntry(tab: .general, title: "Extend hover area", keywords: ["hover", "cursor"], highlightID: SettingsTab.general.highlightID(for: "Extend hover area")),
|
SettingsSearchEntry(tab: .general, title: "Extend hover area", keywords: ["hover", "cursor"], highlightID: SettingsTab.general.highlightID(for: "Extend hover area")),
|
||||||
|
|
@ -568,6 +566,17 @@ struct SettingsView: View {
|
||||||
SettingsSearchEntry(tab: .general, title: "Open notch on hover", keywords: ["hover to open", "auto open"], highlightID: SettingsTab.general.highlightID(for: "Open notch on hover")),
|
SettingsSearchEntry(tab: .general, title: "Open notch on hover", keywords: ["hover to open", "auto open"], highlightID: SettingsTab.general.highlightID(for: "Open notch on hover")),
|
||||||
SettingsSearchEntry(tab: .general, title: "Notch display height", keywords: ["display height", "menu bar size"], highlightID: SettingsTab.general.highlightID(for: "Notch display height")),
|
SettingsSearchEntry(tab: .general, title: "Notch display height", keywords: ["display height", "menu bar size"], highlightID: SettingsTab.general.highlightID(for: "Notch display height")),
|
||||||
|
|
||||||
|
// Live Activities
|
||||||
|
SettingsSearchEntry(tab: .liveActivities, title: "Enable Screen Recording Detection", keywords: ["screen recording", "indicator"], highlightID: SettingsTab.liveActivities.highlightID(for: "Enable Screen Recording Detection")),
|
||||||
|
SettingsSearchEntry(tab: .liveActivities, title: "Show Recording Indicator", keywords: ["recording indicator", "red dot"], highlightID: SettingsTab.liveActivities.highlightID(for: "Show Recording Indicator")),
|
||||||
|
SettingsSearchEntry(tab: .liveActivities, title: "Enable Focus Detection", keywords: ["focus", "do not disturb", "dnd"], highlightID: SettingsTab.liveActivities.highlightID(for: "Enable Focus Detection")),
|
||||||
|
SettingsSearchEntry(tab: .liveActivities, title: "Show Focus Indicator", keywords: ["focus icon", "moon"], highlightID: SettingsTab.liveActivities.highlightID(for: "Show Focus Indicator")),
|
||||||
|
SettingsSearchEntry(tab: .liveActivities, title: "Show Focus Label", keywords: ["focus label", "text"], highlightID: SettingsTab.liveActivities.highlightID(for: "Show Focus Label")),
|
||||||
|
SettingsSearchEntry(tab: .liveActivities, title: "Enable Camera Detection", keywords: ["camera", "privacy indicator"], highlightID: SettingsTab.liveActivities.highlightID(for: "Enable Camera Detection")),
|
||||||
|
SettingsSearchEntry(tab: .liveActivities, title: "Enable Microphone Detection", keywords: ["microphone", "privacy"], highlightID: SettingsTab.liveActivities.highlightID(for: "Enable Microphone Detection")),
|
||||||
|
SettingsSearchEntry(tab: .liveActivities, title: "Enable music live activity", keywords: ["music", "now playing"], highlightID: SettingsTab.liveActivities.highlightID(for: "Enable music live activity")),
|
||||||
|
SettingsSearchEntry(tab: .liveActivities, title: "Enable reminder live activity", keywords: ["reminder", "live activity"], highlightID: SettingsTab.liveActivities.highlightID(for: "Enable reminder live activity")),
|
||||||
|
|
||||||
// Battery (Charge)
|
// Battery (Charge)
|
||||||
SettingsSearchEntry(tab: .battery, title: "Show battery indicator", keywords: ["battery hud", "charge"], highlightID: SettingsTab.battery.highlightID(for: "Show battery indicator")),
|
SettingsSearchEntry(tab: .battery, title: "Show battery indicator", keywords: ["battery hud", "charge"], highlightID: SettingsTab.battery.highlightID(for: "Show battery indicator")),
|
||||||
SettingsSearchEntry(tab: .battery, title: "Show battery percentage", keywords: ["battery percent"], highlightID: SettingsTab.battery.highlightID(for: "Show battery percentage")),
|
SettingsSearchEntry(tab: .battery, title: "Show battery percentage", keywords: ["battery percent"], highlightID: SettingsTab.battery.highlightID(for: "Show battery percentage")),
|
||||||
|
|
@ -719,6 +728,10 @@ struct SettingsView: View {
|
||||||
SettingsForm(tab: .general) {
|
SettingsForm(tab: .general) {
|
||||||
GeneralSettings()
|
GeneralSettings()
|
||||||
}
|
}
|
||||||
|
case .liveActivities:
|
||||||
|
SettingsForm(tab: .liveActivities) {
|
||||||
|
LiveActivitiesSettings()
|
||||||
|
}
|
||||||
case .appearance:
|
case .appearance:
|
||||||
SettingsForm(tab: .appearance) {
|
SettingsForm(tab: .appearance) {
|
||||||
Appearance()
|
Appearance()
|
||||||
|
|
@ -819,10 +832,6 @@ struct GeneralSettings: View {
|
||||||
@State private var screens: [String] = NSScreen.screens.compactMap { $0.localizedName }
|
@State private var screens: [String] = NSScreen.screens.compactMap { $0.localizedName }
|
||||||
@EnvironmentObject var vm: DynamicIslandViewModel
|
@EnvironmentObject var vm: DynamicIslandViewModel
|
||||||
@ObservedObject var coordinator = DynamicIslandViewCoordinator.shared
|
@ObservedObject var coordinator = DynamicIslandViewCoordinator.shared
|
||||||
@ObservedObject var recordingManager = ScreenRecordingManager.shared
|
|
||||||
@ObservedObject var privacyManager = PrivacyIndicatorManager.shared
|
|
||||||
@ObservedObject var doNotDisturbManager = DoNotDisturbManager.shared
|
|
||||||
|
|
||||||
@Default(.mirrorShape) var mirrorShape
|
@Default(.mirrorShape) var mirrorShape
|
||||||
@Default(.showEmojis) var showEmojis
|
@Default(.showEmojis) var showEmojis
|
||||||
@Default(.gestureSensitivity) var gestureSensitivity
|
@Default(.gestureSensitivity) var gestureSensitivity
|
||||||
|
|
@ -835,11 +844,6 @@ struct GeneralSettings: View {
|
||||||
@Default(.automaticallySwitchDisplay) var automaticallySwitchDisplay
|
@Default(.automaticallySwitchDisplay) var automaticallySwitchDisplay
|
||||||
@Default(.enableGestures) var enableGestures
|
@Default(.enableGestures) var enableGestures
|
||||||
@Default(.openNotchOnHover) var openNotchOnHover
|
@Default(.openNotchOnHover) var openNotchOnHover
|
||||||
@Default(.enableScreenRecordingDetection) var enableScreenRecordingDetection
|
|
||||||
@Default(.showRecordingIndicator) var showRecordingIndicator
|
|
||||||
@Default(.enableDoNotDisturbDetection) var enableDoNotDisturbDetection
|
|
||||||
@Default(.showDoNotDisturbIndicator) var showDoNotDisturbIndicator
|
|
||||||
@Default(.showDoNotDisturbLabel) var showDoNotDisturbLabel
|
|
||||||
@Default(.enableMinimalisticUI) var enableMinimalisticUI
|
@Default(.enableMinimalisticUI) var enableMinimalisticUI
|
||||||
|
|
||||||
private func highlightID(_ title: String) -> String {
|
private func highlightID(_ title: String) -> String {
|
||||||
|
|
@ -897,130 +901,6 @@ struct GeneralSettings: View {
|
||||||
Text("System features")
|
Text("System features")
|
||||||
}
|
}
|
||||||
|
|
||||||
Section {
|
|
||||||
Defaults.Toggle("Enable Screen Recording Detection", key: .enableScreenRecordingDetection)
|
|
||||||
.settingsHighlight(id: highlightID("Enable Screen Recording Detection"))
|
|
||||||
|
|
||||||
Defaults.Toggle("Show Recording Indicator", key: .showRecordingIndicator)
|
|
||||||
.disabled(!enableScreenRecordingDetection)
|
|
||||||
.settingsHighlight(id: highlightID("Show Recording Indicator"))
|
|
||||||
|
|
||||||
// Note: Polling removed - now uses event-driven private API detection
|
|
||||||
|
|
||||||
if recordingManager.isMonitoring {
|
|
||||||
HStack {
|
|
||||||
Text("Detection Status")
|
|
||||||
Spacer()
|
|
||||||
if recordingManager.isRecording {
|
|
||||||
HStack(spacing: 4) {
|
|
||||||
Circle()
|
|
||||||
.fill(Color.red)
|
|
||||||
.frame(width: 8, height: 8)
|
|
||||||
Text("Recording Detected")
|
|
||||||
.foregroundColor(.red)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Text("Active - No Recording")
|
|
||||||
.foregroundColor(.green)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} header: {
|
|
||||||
Text("Screen Recording")
|
|
||||||
} footer: {
|
|
||||||
Text("Uses event-driven private API for real-time screen recording detection")
|
|
||||||
}
|
|
||||||
|
|
||||||
Section {
|
|
||||||
Defaults.Toggle("Enable Focus Detection", key: .enableDoNotDisturbDetection)
|
|
||||||
.settingsHighlight(id: highlightID("Enable Focus Detection"))
|
|
||||||
|
|
||||||
Defaults.Toggle("Show Focus Indicator", key: .showDoNotDisturbIndicator)
|
|
||||||
.disabled(!enableDoNotDisturbDetection)
|
|
||||||
.settingsHighlight(id: highlightID("Show Focus Indicator"))
|
|
||||||
|
|
||||||
Defaults.Toggle("Show Focus Label", key: .showDoNotDisturbLabel)
|
|
||||||
.disabled(!enableDoNotDisturbDetection)
|
|
||||||
.settingsHighlight(id: highlightID("Show Focus Label"))
|
|
||||||
|
|
||||||
if doNotDisturbManager.isMonitoring {
|
|
||||||
HStack {
|
|
||||||
Text("Focus Status")
|
|
||||||
Spacer()
|
|
||||||
if doNotDisturbManager.isDoNotDisturbActive {
|
|
||||||
HStack(spacing: 4) {
|
|
||||||
Circle()
|
|
||||||
.fill(Color.purple)
|
|
||||||
.frame(width: 8, height: 8)
|
|
||||||
Text(doNotDisturbManager.currentFocusModeName.isEmpty ? "Focus Enabled" : doNotDisturbManager.currentFocusModeName)
|
|
||||||
.foregroundColor(.purple)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Text("Active - No Focus")
|
|
||||||
.foregroundColor(.green)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
HStack {
|
|
||||||
Text("Focus Status")
|
|
||||||
Spacer()
|
|
||||||
Text("Disabled")
|
|
||||||
.foregroundColor(.secondary)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} header: {
|
|
||||||
Text("Do Not Disturb")
|
|
||||||
} footer: {
|
|
||||||
Text("Listens for Focus session changes via distributed notifications")
|
|
||||||
}
|
|
||||||
|
|
||||||
Section {
|
|
||||||
Defaults.Toggle("Enable Camera Detection", key: .enableCameraDetection)
|
|
||||||
.settingsHighlight(id: highlightID("Enable Camera Detection"))
|
|
||||||
Defaults.Toggle("Enable Microphone Detection", key: .enableMicrophoneDetection)
|
|
||||||
.settingsHighlight(id: highlightID("Enable Microphone Detection"))
|
|
||||||
|
|
||||||
if privacyManager.isMonitoring {
|
|
||||||
HStack {
|
|
||||||
Text("Camera Status")
|
|
||||||
Spacer()
|
|
||||||
if privacyManager.cameraActive {
|
|
||||||
HStack(spacing: 4) {
|
|
||||||
Circle()
|
|
||||||
.fill(Color.green)
|
|
||||||
.frame(width: 8, height: 8)
|
|
||||||
Text("Camera Active")
|
|
||||||
.foregroundColor(.green)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Text("Inactive")
|
|
||||||
.foregroundColor(.secondary)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
HStack {
|
|
||||||
Text("Microphone Status")
|
|
||||||
Spacer()
|
|
||||||
if privacyManager.microphoneActive {
|
|
||||||
HStack(spacing: 4) {
|
|
||||||
Circle()
|
|
||||||
.fill(Color.yellow)
|
|
||||||
.frame(width: 8, height: 8)
|
|
||||||
Text("Microphone Active")
|
|
||||||
.foregroundColor(.yellow)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Text("Inactive")
|
|
||||||
.foregroundColor(.secondary)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} header: {
|
|
||||||
Text("Privacy Indicators")
|
|
||||||
} footer: {
|
|
||||||
Text("Shows green camera icon and yellow microphone icon when in use. Uses event-driven CoreAudio and CoreMediaIO APIs.")
|
|
||||||
}
|
|
||||||
|
|
||||||
Section {
|
Section {
|
||||||
Picker(selection: $notchHeightMode, label:
|
Picker(selection: $notchHeightMode, label:
|
||||||
Text("Notch display height")) {
|
Text("Notch display height")) {
|
||||||
|
|
@ -2167,6 +2047,168 @@ struct Extensions: View {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct LiveActivitiesSettings: View {
|
||||||
|
@ObservedObject var coordinator = DynamicIslandViewCoordinator.shared
|
||||||
|
@ObservedObject var recordingManager = ScreenRecordingManager.shared
|
||||||
|
@ObservedObject var privacyManager = PrivacyIndicatorManager.shared
|
||||||
|
@ObservedObject var doNotDisturbManager = DoNotDisturbManager.shared
|
||||||
|
|
||||||
|
@Default(.enableScreenRecordingDetection) var enableScreenRecordingDetection
|
||||||
|
@Default(.enableDoNotDisturbDetection) var enableDoNotDisturbDetection
|
||||||
|
|
||||||
|
private func highlightID(_ title: String) -> String {
|
||||||
|
SettingsTab.liveActivities.highlightID(for: title)
|
||||||
|
}
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
Form {
|
||||||
|
Section {
|
||||||
|
Defaults.Toggle("Enable Screen Recording Detection", key: .enableScreenRecordingDetection)
|
||||||
|
.settingsHighlight(id: highlightID("Enable Screen Recording Detection"))
|
||||||
|
|
||||||
|
Defaults.Toggle("Show Recording Indicator", key: .showRecordingIndicator)
|
||||||
|
.disabled(!enableScreenRecordingDetection)
|
||||||
|
.settingsHighlight(id: highlightID("Show Recording Indicator"))
|
||||||
|
|
||||||
|
if recordingManager.isMonitoring {
|
||||||
|
HStack {
|
||||||
|
Text("Detection Status")
|
||||||
|
Spacer()
|
||||||
|
if recordingManager.isRecording {
|
||||||
|
HStack(spacing: 4) {
|
||||||
|
Circle()
|
||||||
|
.fill(Color.red)
|
||||||
|
.frame(width: 8, height: 8)
|
||||||
|
Text("Recording Detected")
|
||||||
|
.foregroundColor(.red)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Text("Active - No Recording")
|
||||||
|
.foregroundColor(.green)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} header: {
|
||||||
|
Text("Screen Recording")
|
||||||
|
} footer: {
|
||||||
|
Text("Uses event-driven private API for real-time screen recording detection")
|
||||||
|
}
|
||||||
|
|
||||||
|
Section {
|
||||||
|
Defaults.Toggle("Enable Focus Detection", key: .enableDoNotDisturbDetection)
|
||||||
|
.settingsHighlight(id: highlightID("Enable Focus Detection"))
|
||||||
|
|
||||||
|
Defaults.Toggle("Show Focus Indicator", key: .showDoNotDisturbIndicator)
|
||||||
|
.disabled(!enableDoNotDisturbDetection)
|
||||||
|
.settingsHighlight(id: highlightID("Show Focus Indicator"))
|
||||||
|
|
||||||
|
Defaults.Toggle("Show Focus Label", key: .showDoNotDisturbLabel)
|
||||||
|
.disabled(!enableDoNotDisturbDetection)
|
||||||
|
.settingsHighlight(id: highlightID("Show Focus Label"))
|
||||||
|
|
||||||
|
if doNotDisturbManager.isMonitoring {
|
||||||
|
HStack {
|
||||||
|
Text("Focus Status")
|
||||||
|
Spacer()
|
||||||
|
if doNotDisturbManager.isDoNotDisturbActive {
|
||||||
|
HStack(spacing: 4) {
|
||||||
|
Circle()
|
||||||
|
.fill(Color.purple)
|
||||||
|
.frame(width: 8, height: 8)
|
||||||
|
Text(doNotDisturbManager.currentFocusModeName.isEmpty ? "Focus Enabled" : doNotDisturbManager.currentFocusModeName)
|
||||||
|
.foregroundColor(.purple)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Text("Active - No Focus")
|
||||||
|
.foregroundColor(.green)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
HStack {
|
||||||
|
Text("Focus Status")
|
||||||
|
Spacer()
|
||||||
|
Text("Disabled")
|
||||||
|
.foregroundColor(.secondary)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} header: {
|
||||||
|
Text("Do Not Disturb")
|
||||||
|
} footer: {
|
||||||
|
Text("Listens for Focus session changes via distributed notifications")
|
||||||
|
}
|
||||||
|
|
||||||
|
Section {
|
||||||
|
Defaults.Toggle("Enable Camera Detection", key: .enableCameraDetection)
|
||||||
|
.settingsHighlight(id: highlightID("Enable Camera Detection"))
|
||||||
|
Defaults.Toggle("Enable Microphone Detection", key: .enableMicrophoneDetection)
|
||||||
|
.settingsHighlight(id: highlightID("Enable Microphone Detection"))
|
||||||
|
|
||||||
|
if privacyManager.isMonitoring {
|
||||||
|
HStack {
|
||||||
|
Text("Camera Status")
|
||||||
|
Spacer()
|
||||||
|
if privacyManager.cameraActive {
|
||||||
|
HStack(spacing: 4) {
|
||||||
|
Circle()
|
||||||
|
.fill(Color.green)
|
||||||
|
.frame(width: 8, height: 8)
|
||||||
|
Text("Camera Active")
|
||||||
|
.foregroundColor(.green)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Text("Inactive")
|
||||||
|
.foregroundColor(.secondary)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
HStack {
|
||||||
|
Text("Microphone Status")
|
||||||
|
Spacer()
|
||||||
|
if privacyManager.microphoneActive {
|
||||||
|
HStack(spacing: 4) {
|
||||||
|
Circle()
|
||||||
|
.fill(Color.yellow)
|
||||||
|
.frame(width: 8, height: 8)
|
||||||
|
Text("Microphone Active")
|
||||||
|
.foregroundColor(.yellow)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Text("Inactive")
|
||||||
|
.foregroundColor(.secondary)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} header: {
|
||||||
|
Text("Privacy Indicators")
|
||||||
|
} footer: {
|
||||||
|
Text("Shows green camera icon and yellow microphone icon when in use. Uses event-driven CoreAudio and CoreMediaIO APIs.")
|
||||||
|
}
|
||||||
|
|
||||||
|
Section {
|
||||||
|
Toggle(
|
||||||
|
"Enable music live activity",
|
||||||
|
isOn: $coordinator.musicLiveActivityEnabled.animation()
|
||||||
|
)
|
||||||
|
.settingsHighlight(id: highlightID("Enable music live activity"))
|
||||||
|
} header: {
|
||||||
|
Text("Media Live Activity")
|
||||||
|
} footer: {
|
||||||
|
Text("Use the Media tab to configure sneak peek, lyrics, and floating media controls.")
|
||||||
|
}
|
||||||
|
|
||||||
|
Section {
|
||||||
|
Defaults.Toggle("Enable reminder live activity", key: .enableReminderLiveActivity)
|
||||||
|
.settingsHighlight(id: highlightID("Enable reminder live activity"))
|
||||||
|
} header: {
|
||||||
|
Text("Reminder Live Activity")
|
||||||
|
} footer: {
|
||||||
|
Text("Configure countdown style and lock screen widgets in the Calendar tab.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.navigationTitle("Live Activities")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
struct Appearance: View {
|
struct Appearance: View {
|
||||||
@ObservedObject var coordinator = DynamicIslandViewCoordinator.shared
|
@ObservedObject var coordinator = DynamicIslandViewCoordinator.shared
|
||||||
@Default(.mirrorShape) var mirrorShape
|
@Default(.mirrorShape) var mirrorShape
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue