From c3dff3a950568034fe73912939229fad7069b2b2 Mon Sep 17 00:00:00 2001
From: Hariharan Mudaliar
Date: Fri, 14 Nov 2025 01:46:10 +0530
Subject: [PATCH 1/5] Create Working Focus Detection for all types
---
.../managers/DoNotDisturbManager.swift | 930 +++++++++++++++++-
ReadMe.md | 2 +-
2 files changed, 890 insertions(+), 42 deletions(-)
diff --git a/DynamicIsland/managers/DoNotDisturbManager.swift b/DynamicIsland/managers/DoNotDisturbManager.swift
index ce3ad64..577833a 100644
--- a/DynamicIsland/managers/DoNotDisturbManager.swift
+++ b/DynamicIsland/managers/DoNotDisturbManager.swift
@@ -22,8 +22,14 @@ final class DoNotDisturbManager: ObservableObject {
private let notificationCenter = DistributedNotificationCenter.default()
private let metadataExtractionQueue = DispatchQueue(label: "com.dynamicisland.focus.metadata", qos: .userInitiated)
+ private let focusStateResolver = FocusStateResolver()
+ private let focusLogStream = FocusLogStream()
- private init() {}
+ private init() {
+ focusLogStream.onMetadataUpdate = { [weak self] identifier, name in
+ self?.handleLogMetadataUpdate(identifier: identifier, name: name)
+ }
+ }
deinit {
stopMonitoring()
@@ -48,6 +54,7 @@ final class DoNotDisturbManager: ObservableObject {
suspensionBehavior: .deliverImmediately
)
+ focusLogStream.start()
isMonitoring = true
}
@@ -57,6 +64,7 @@ final class DoNotDisturbManager: ObservableObject {
notificationCenter.removeObserver(self, name: .focusModeEnabled, object: nil)
notificationCenter.removeObserver(self, name: .focusModeDisabled, object: nil)
+ focusLogStream.stop()
isMonitoring = false
DispatchQueue.main.async {
@@ -79,54 +87,83 @@ final class DoNotDisturbManager: ObservableObject {
guard let self = self else { return }
let metadata = self.extractMetadata(from: notification)
+ self.publishMetadata(identifier: metadata.identifier, name: metadata.name, isActive: isActive, source: notification.name.rawValue)
+ }
+ }
- DispatchQueue.main.async {
- let trimmedIdentifier = metadata.identifier?.trimmingCharacters(in: .whitespacesAndNewlines)
- let trimmedName = metadata.name?.trimmingCharacters(in: .whitespacesAndNewlines)
+ private func publishMetadata(identifier: String?, name: String?, isActive: Bool?, source: String) {
+ DispatchQueue.main.async { [weak self] in
+ guard let self = self else { return }
- let resolvedMode = FocusModeType.resolve(identifier: trimmedIdentifier, name: trimmedName)
+ let trimmedIdentifier = identifier?.trimmingCharacters(in: .whitespacesAndNewlines)
+ let trimmedName = name?.trimmingCharacters(in: .whitespacesAndNewlines)
- debugPrint("[DoNotDisturbManager] Focus update -> notification: \(notification.name.rawValue) | identifier: \(trimmedIdentifier ?? "") | name: \(trimmedName ?? "") | resolved: \(resolvedMode.rawValue)")
+ let resolvedMode = FocusModeType.resolve(identifier: trimmedIdentifier, name: trimmedName)
- let finalIdentifier: String
- if let identifier = trimmedIdentifier, !identifier.isEmpty {
- finalIdentifier = identifier
- } else {
- finalIdentifier = resolvedMode.rawValue
- }
+ let finalIdentifier: String
+ if let identifier = trimmedIdentifier, !identifier.isEmpty {
+ finalIdentifier = identifier
+ } else {
+ finalIdentifier = resolvedMode.rawValue
+ }
- if finalIdentifier != self.currentFocusModeIdentifier {
- self.currentFocusModeIdentifier = finalIdentifier
- }
+ let finalName: String
+ if let name = trimmedName, !name.isEmpty {
+ finalName = name
+ } else if !resolvedMode.displayName.isEmpty {
+ finalName = resolvedMode.displayName
+ } else if let identifier = trimmedIdentifier, !identifier.isEmpty {
+ finalName = identifier
+ } else {
+ finalName = "Focus"
+ }
- let finalName: String
- if let name = trimmedName, !name.isEmpty {
- finalName = name
- } else if !resolvedMode.displayName.isEmpty {
- finalName = resolvedMode.displayName
- } else if let identifier = trimmedIdentifier, !identifier.isEmpty {
- finalName = identifier
- } else {
- finalName = "Focus"
- }
+ let previousIdentifier = self.currentFocusModeIdentifier
+ let previousName = self.currentFocusModeName
+ let previousActive = self.isDoNotDisturbActive
- if finalName != self.currentFocusModeName {
- self.currentFocusModeName = finalName
- }
+ let identifierChanged = finalIdentifier != previousIdentifier
+ let nameChanged = finalName != previousName
+ let shouldToggleActive = isActive.map { $0 != previousActive } ?? false
- guard self.isDoNotDisturbActive != isActive else { return }
+ if identifierChanged {
+ self.currentFocusModeIdentifier = finalIdentifier
+ }
- withAnimation(.smooth(duration: 0.25)) {
- self.isDoNotDisturbActive = isActive
- }
+ if nameChanged {
+ self.currentFocusModeName = finalName
+ }
+
+ if identifierChanged || nameChanged || shouldToggleActive {
+ debugPrint("[DoNotDisturbManager] Focus update -> source: \(source) | identifier: \(trimmedIdentifier ?? "") | name: \(trimmedName ?? "") | resolved: \(resolvedMode.rawValue)")
+ }
+
+ guard let isActive = isActive, shouldToggleActive else { return }
+
+ withAnimation(.smooth(duration: 0.25)) {
+ self.isDoNotDisturbActive = isActive
}
}
}
- private func extractMetadata(from notification: Notification) -> (name: String?, identifier: String?) {
- guard let userInfo = notification.userInfo else { return (nil, nil) }
+ private func handleLogMetadataUpdate(identifier: String?, name: String?) {
+ metadataExtractionQueue.async { [weak self] in
+ guard let self = self else { return }
+ let trimmedIdentifier = identifier?.trimmingCharacters(in: .whitespacesAndNewlines)
+ let trimmedName = name?.trimmingCharacters(in: .whitespacesAndNewlines)
- debugPrint("[DoNotDisturbManager] raw focus payload -> name: \(notification.name.rawValue), object: \(String(describing: notification.object)), userInfo: \(userInfo)")
+ let hasIdentifier = (trimmedIdentifier?.isEmpty == false)
+ let hasName = (trimmedName?.isEmpty == false)
+
+ guard hasIdentifier || hasName else { return }
+
+ self.publishMetadata(identifier: trimmedIdentifier, name: trimmedName, isActive: nil, source: "log-stream")
+ }
+ }
+
+ private func extractMetadata(from notification: Notification) -> (name: String?, identifier: String?) {
+ var identifier: String?
+ var name: String?
let identifierKeys = [
"FocusModeIdentifier",
@@ -144,12 +181,121 @@ final class DoNotDisturbManager: ObservableObject {
"focusModeName",
"FocusMode",
"focusMode",
+ "displayName",
+ "display_name",
"name",
"Name"
]
- let identifier = firstMatch(for: identifierKeys, in: userInfo)
- let name = firstMatch(for: nameKeys, in: userInfo)
+ var candidates: [Any] = []
+ if let userInfo = notification.userInfo {
+ candidates.append(userInfo)
+ }
+
+ if let object = notification.object {
+ candidates.append(object)
+ }
+
+ debugPrint("[DoNotDisturbManager] raw focus payload -> name: \(notification.name.rawValue), object: \(String(describing: notification.object)), userInfo: \(String(describing: notification.userInfo))")
+
+ for candidate in candidates {
+ if identifier == nil {
+ identifier = firstMatch(for: identifierKeys, in: candidate)
+ }
+
+ if name == nil {
+ name = firstMatch(for: nameKeys, in: candidate)
+ }
+
+ if identifier != nil && name != nil {
+ break
+ }
+ }
+
+ if identifier == nil || name == nil {
+ for candidate in candidates {
+ if let decoded = decodeFocusPayloadIfNeeded(candidate) {
+ if identifier == nil {
+ identifier = firstMatch(for: identifierKeys, in: decoded)
+ }
+
+ if name == nil {
+ name = firstMatch(for: nameKeys, in: decoded)
+ }
+
+ if identifier != nil && name != nil {
+ break
+ }
+ }
+ }
+ }
+
+ if identifier == nil || name == nil {
+ for candidate in candidates {
+ if let object = candidate as? NSObject {
+ if identifier == nil, let extractedIdentifier = extractIdentifier(fromFocusObject: object) {
+ identifier = extractedIdentifier
+ }
+
+ if name == nil, let extractedName = extractDisplayName(fromFocusObject: object) {
+ name = extractedName
+ }
+
+ if identifier != nil && name != nil {
+ break
+ }
+ }
+ }
+ }
+
+ if identifier == nil || name == nil {
+ var descriptionSources: [Any] = candidates
+
+ for candidate in candidates {
+ if let decoded = decodeFocusPayloadIfNeeded(candidate) {
+ descriptionSources.append(decoded)
+ }
+ }
+
+ for candidate in descriptionSources {
+ let description = String(describing: candidate)
+
+ if identifier == nil, let inferredIdentifier = FocusMetadataDecoder.extractIdentifier(from: description) {
+ identifier = inferredIdentifier
+ }
+
+ if name == nil, let inferredName = FocusMetadataDecoder.extractName(from: description) {
+ name = inferredName
+ }
+
+ if identifier != nil && name != nil {
+ break
+ }
+ }
+ }
+
+ if identifier == nil || name == nil {
+ if let logMetadata = focusLogStream.latestMetadata() {
+ if identifier == nil {
+ identifier = logMetadata.identifier
+ }
+
+ if name == nil {
+ name = logMetadata.name
+ }
+ }
+ }
+
+ if identifier == nil || name == nil {
+ if let fallback = focusStateResolver.loadActiveFocusState(identifierMissing: identifier == nil, nameMissing: name == nil) {
+ if identifier == nil {
+ identifier = fallback.identifier
+ }
+ if name == nil {
+ name = fallback.name
+ }
+ }
+ }
return (name, identifier)
}
@@ -326,17 +472,26 @@ private extension DoNotDisturbManager {
func normalizedString(from value: Any) -> String? {
switch value {
case let string as String:
- let trimmed = string.trimmingCharacters(in: .whitespacesAndNewlines)
- return trimmed.isEmpty ? nil : trimmed
+ let cleaned = FocusMetadataDecoder.cleanedString(string)
+ return cleaned.isEmpty ? nil : cleaned
case let number as NSNumber:
- return number.stringValue
+ return FocusMetadataDecoder.cleanedString(number.stringValue)
case let uuid as UUID:
return uuid.uuidString
case let uuid as NSUUID:
return uuid.uuidString
case let data as Data:
+ if let decoded = decodeFocusPayload(from: data) {
+ if let nested = firstMatch(for: ["identifier", "Identifier", "uuid", "UUID"], in: decoded) {
+ return nested
+ }
+ if let name = firstMatch(for: ["name", "Name", "displayName", "display_name"], in: decoded) {
+ return name
+ }
+ }
if let string = String(data: data, encoding: .utf8) {
- return string.trimmingCharacters(in: .whitespacesAndNewlines)
+ let cleaned = FocusMetadataDecoder.cleanedString(string)
+ return cleaned.isEmpty ? nil : cleaned
}
return nil
case let dict as [AnyHashable: Any]:
@@ -352,4 +507,697 @@ private extension DoNotDisturbManager {
return nil
}
}
+
+ func decodeFocusPayloadIfNeeded(_ value: Any) -> Any? {
+ switch value {
+ case let data as Data:
+ return decodeFocusPayload(from: data)
+ case let data as NSData:
+ return decodeFocusPayload(from: data as Data)
+ default:
+ return nil
+ }
+ }
+
+ func decodeFocusPayload(from data: Data) -> Any? {
+ guard !data.isEmpty else { return nil }
+
+ if let propertyList = try? PropertyListSerialization.propertyList(from: data, options: [], format: nil) {
+ return propertyList
+ }
+
+ if let jsonObject = try? JSONSerialization.jsonObject(with: data, options: [.fragmentsAllowed]) {
+ return jsonObject
+ }
+
+ if let string = String(data: data, encoding: .utf8) {
+ let trimmed = string.trimmingCharacters(in: .whitespacesAndNewlines)
+ return trimmed.isEmpty ? nil : trimmed
+ }
+
+ return nil
+ }
+
+ func extractIdentifier(fromFocusObject object: NSObject) -> String? {
+ if let array = object as? [Any] {
+ for element in array {
+ if let nested = element as? NSObject, let identifier = extractIdentifier(fromFocusObject: nested) {
+ return identifier
+ }
+ }
+ return nil
+ }
+
+ if let identifier = focusString(object, selector: "modeIdentifier") {
+ return identifier
+ }
+
+ if let identifier = focusString(object, selector: "identifier") {
+ return identifier
+ }
+
+ if let details = focusObject(object, selector: "details"), let identifier = extractIdentifier(fromFocusObject: details) {
+ return identifier
+ }
+
+ if let metadata = focusObject(object, selector: "activeModeAssertionMetadata"), let identifier = extractIdentifier(fromFocusObject: metadata) {
+ return identifier
+ }
+
+ if let configuration = focusObject(object, selector: "activeModeConfiguration"), let identifier = extractIdentifier(fromFocusObject: configuration) {
+ return identifier
+ }
+
+ if let modeConfiguration = focusObject(object, selector: "modeConfiguration"), let identifier = extractIdentifier(fromFocusObject: modeConfiguration) {
+ return identifier
+ }
+
+ if let mode = focusObject(object, selector: "mode") {
+ return extractIdentifier(fromFocusObject: mode)
+ }
+
+ if let identifiers = focusObject(object, selector: "activeModeIdentifiers") {
+ if let stringArray = identifiers as? [String] {
+ if let first = stringArray.compactMap({ FocusMetadataDecoder.cleanedString($0) }).first(where: { !$0.isEmpty }) {
+ return first
+ }
+ } else if let array = identifiers as? NSArray {
+ for case let string as String in array {
+ let trimmed = FocusMetadataDecoder.cleanedString(string)
+ if !trimmed.isEmpty {
+ return trimmed
+ }
+ }
+ }
+ }
+
+ return nil
+ }
+
+ func extractDisplayName(fromFocusObject object: NSObject) -> String? {
+ if let array = object as? [Any] {
+ for element in array {
+ if let nested = element as? NSObject, let name = extractDisplayName(fromFocusObject: nested) {
+ return name
+ }
+ }
+ return nil
+ }
+
+ if let name = focusString(object, selector: "name") {
+ return name
+ }
+
+ if let name = focusString(object, selector: "displayName") {
+ return name
+ }
+
+ if let name = focusString(object, selector: "activityDisplayName") {
+ return name
+ }
+
+ if let descriptor = focusObject(object, selector: "symbolDescriptor"), let name = focusString(descriptor, selector: "name") {
+ return name
+ }
+
+ if let mode = focusObject(object, selector: "mode"), let name = extractDisplayName(fromFocusObject: mode) {
+ return name
+ }
+
+ if let details = focusObject(object, selector: "details"), let name = extractDisplayName(fromFocusObject: details) {
+ return name
+ }
+
+ if let configuration = focusObject(object, selector: "modeConfiguration"), let name = extractDisplayName(fromFocusObject: configuration) {
+ return name
+ }
+
+ return nil
+ }
+
+ func focusObject(_ object: NSObject, selector selectorName: String) -> NSObject? {
+ let selector = NSSelectorFromString(selectorName)
+ guard object.responds(to: selector) else { return nil }
+ guard let value = object.perform(selector)?.takeUnretainedValue() else { return nil }
+ return value as? NSObject
+ }
+
+ func focusString(_ object: NSObject, selector selectorName: String) -> String? {
+ let selector = NSSelectorFromString(selectorName)
+ guard object.responds(to: selector) else { return nil }
+ guard let value = object.perform(selector)?.takeUnretainedValue() else { return nil }
+
+ switch value {
+ case let string as String:
+ return FocusMetadataDecoder.cleanedString(string)
+ case let string as NSString:
+ return FocusMetadataDecoder.cleanedString(string as String)
+ case let number as NSNumber:
+ return FocusMetadataDecoder.cleanedString(number.stringValue)
+ default:
+ return nil
+ }
+ }
+
+}
+
+// MARK: - Filesystem fallback for Focus metadata
+
+private final class FocusStateResolver {
+ private struct CacheKey: Hashable { let url: URL }
+ private struct FocusRecord { let identifier: String?; let name: String? }
+
+ private let fileManager = FileManager.default
+
+ private let identifierKeys = [
+ "FocusModeIdentifier",
+ "focusModeIdentifier",
+ "FocusModeUUID",
+ "focusModeUUID",
+ "UUID",
+ "uuid",
+ "identifier",
+ "Identifier",
+ "activeModeIdentifier",
+ "activeModeUUID",
+ "modeIdentifier",
+ "modeUUID"
+ ]
+
+ private let nameKeys = [
+ "FocusModeName",
+ "focusModeName",
+ "FocusMode",
+ "focusMode",
+ "displayName",
+ "display_name",
+ "name",
+ "Name",
+ "modeName"
+ ]
+
+ private var cache: [CacheKey: (modified: Date, records: [FocusRecord])] = [:]
+
+ private lazy var activeStateURLs: [URL] = {
+ let home = fileManager.homeDirectoryForCurrentUser
+ return [
+ "Library/DoNotDisturb/ActiveState.plist",
+ "Library/DoNotDisturb/DB/ActiveState.plist",
+ "Library/Focus/ActiveState.plist"
+ ].map { home.appendingPathComponent($0) }
+ }()
+
+ private lazy var catalogURLs: [URL] = {
+ let home = fileManager.homeDirectoryForCurrentUser
+ return [
+ "Library/DoNotDisturb/DB/FocusPreferences.plist",
+ "Library/DoNotDisturb/DB/FocusModeDefaults.plist",
+ "Library/DoNotDisturb/DB/FocusModes.plist",
+ "Library/Focus/FocusModeDefaults.plist",
+ "Library/Preferences/com.apple.controlcenter.plist"
+ ].map { home.appendingPathComponent($0) }
+ }()
+
+ func loadActiveFocusState(identifierMissing: Bool, nameMissing: Bool) -> (identifier: String?, name: String?)? {
+ guard identifierMissing || nameMissing else { return nil }
+
+ let activeRecords = loadRecords(from: activeStateURLs)
+ let catalogRecords = loadRecords(from: catalogURLs)
+ let allRecords = activeRecords + catalogRecords
+
+ guard !allRecords.isEmpty else { return nil }
+
+ var identifier: String?
+ var name: String?
+
+ if let record = activeRecords.first(where: { record in
+ guard let id = record.identifier, !id.isEmpty else { return false }
+ guard let name = record.name, !name.isEmpty else { return false }
+ return true
+ }) {
+ identifier = record.identifier
+ name = record.name
+ }
+
+ if identifierMissing && (identifier == nil || identifier?.isEmpty == true) {
+ identifier = activeRecords.compactMap { $0.identifier?.trimmingCharacters(in: .whitespacesAndNewlines) }
+ .first(where: { !$0.isEmpty })
+
+ if identifier == nil {
+ identifier = catalogRecords.compactMap { $0.identifier?.trimmingCharacters(in: .whitespacesAndNewlines) }
+ .first(where: { !$0.isEmpty })
+ }
+ }
+
+ if nameMissing && (name == nil || name?.isEmpty == true) {
+ if let id = identifier {
+ name = allRecords.first(where: {
+ guard let candidateIdentifier = $0.identifier else { return false }
+ return candidateIdentifier.compare(id, options: [.caseInsensitive, .diacriticInsensitive]) == .orderedSame && ($0.name?.isEmpty == false)
+ })?.name
+ }
+
+ if name == nil {
+ name = activeRecords.compactMap { $0.name?.trimmingCharacters(in: .whitespacesAndNewlines) }
+ .first(where: { !$0.isEmpty })
+ }
+
+ if name == nil {
+ name = catalogRecords.compactMap { $0.name?.trimmingCharacters(in: .whitespacesAndNewlines) }
+ .first(where: { !$0.isEmpty })
+ }
+ }
+
+ if identifier == nil && name == nil {
+ return nil
+ }
+
+ return (identifierMissing ? identifier : nil, nameMissing ? name : nil)
+ }
+
+ private func loadRecords(from urls: [URL]) -> [FocusRecord] {
+ var records: [FocusRecord] = []
+
+ for url in urls {
+ guard fileManager.fileExists(atPath: url.path) else { continue }
+
+ if let cached = cachedRecords(for: url) {
+ records.append(contentsOf: cached)
+ continue
+ }
+
+ guard let attributes = try? fileManager.attributesOfItem(atPath: url.path),
+ let modified = attributes[.modificationDate] as? Date,
+ let data = try? Data(contentsOf: url) else {
+ continue
+ }
+
+ let decoded = decodeData(data)
+ let extracted = extractRecords(from: decoded)
+
+ cache[CacheKey(url: url)] = (modified, extracted)
+ records.append(contentsOf: extracted)
+ }
+
+ return records
+ }
+
+ private func cachedRecords(for url: URL) -> [FocusRecord]? {
+ let key = CacheKey(url: url)
+ guard let cached = cache[key],
+ let attributes = try? fileManager.attributesOfItem(atPath: url.path),
+ let modified = attributes[.modificationDate] as? Date,
+ cached.modified == modified else {
+ cache.removeValue(forKey: key)
+ return nil
+ }
+
+ return cached.records
+ }
+
+ private func decodeData(_ data: Data) -> Any {
+ if let propertyList = try? PropertyListSerialization.propertyList(from: data, options: [], format: nil) {
+ return propertyList
+ }
+
+ if let unarchived = try? NSKeyedUnarchiver.unarchivedObject(ofClasses: [NSDictionary.self, NSArray.self, NSString.self, NSNumber.self, NSData.self], from: data) {
+ return unarchived as Any
+ }
+
+ if let unarchived = try? NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(data) {
+ return unarchived as Any
+ }
+
+ if let jsonObject = try? JSONSerialization.jsonObject(with: data, options: [.fragmentsAllowed]) {
+ return jsonObject
+ }
+
+ if let string = String(data: data, encoding: .utf8) {
+ return string
+ }
+
+ return [:]
+ }
+
+ private func extractRecords(from value: Any) -> [FocusRecord] {
+ var results: [FocusRecord] = []
+
+ if let dictionary = value as? [AnyHashable: Any] {
+ let identifier = directMatch(in: dictionary, keys: identifierKeys)
+ let name = directMatch(in: dictionary, keys: nameKeys)
+
+ if identifier != nil || name != nil {
+ results.append(FocusRecord(identifier: identifier, name: name))
+ }
+
+ for nested in dictionary.values {
+ results.append(contentsOf: extractRecords(from: nested))
+ }
+ } else if let array = value as? [Any] {
+ for element in array {
+ results.append(contentsOf: extractRecords(from: element))
+ }
+ } else if let data = value as? Data {
+ let decoded = decodeData(data)
+ results.append(contentsOf: extractRecords(from: decoded))
+ } else if let data = value as? NSData {
+ let decoded = decodeData(data as Data)
+ results.append(contentsOf: extractRecords(from: decoded))
+ } else if let string = value as? String {
+ let cleaned = FocusMetadataDecoder.cleanedString(string)
+ if !cleaned.isEmpty {
+ let identifier = matchIdentifier(in: cleaned)
+ let name = identifier == nil ? matchName(in: cleaned) : matchName(in: cleaned, matching: identifier)
+
+ if identifier != nil || name != nil {
+ results.append(FocusRecord(identifier: identifier, name: name))
+ }
+ }
+ }
+
+ return results
+ }
+
+ private func directMatch(in dictionary: [AnyHashable: Any], keys: [String]) -> String? {
+ for key in keys {
+ if let candidate = dictionary[key], let string = normalizedString(from: candidate) {
+ return string
+ }
+ }
+ return nil
+ }
+
+ private func normalizedString(from value: Any) -> String? {
+ switch value {
+ case let string as String:
+ return FocusMetadataDecoder.cleanedString(string)
+ case let string as NSString:
+ return FocusMetadataDecoder.cleanedString(string as String)
+ case let number as NSNumber:
+ return FocusMetadataDecoder.cleanedString(number.stringValue)
+ case let data as Data:
+ if let string = String(data: data, encoding: .utf8) {
+ return FocusMetadataDecoder.cleanedString(string)
+ }
+ return nil
+ case let data as NSData:
+ return normalizedString(from: data as Data)
+ default:
+ return nil
+ }
+ }
+
+ private func matchIdentifier(in string: String) -> String? {
+ FocusMetadataDecoder.extractIdentifier(from: string)
+ }
+
+ private func matchName(in string: String, matching identifier: String? = nil) -> String? {
+ if let name = FocusMetadataDecoder.extractName(from: string) {
+ return name
+ }
+
+ if let identifier = identifier {
+ let components = identifier.split(separator: ".")
+ if let last = components.last {
+ let derived = FocusMetadataDecoder.cleanedString(String(last).replacingOccurrences(of: "_", with: " "))
+ if !derived.isEmpty {
+ return derived.capitalized
+ }
+ }
+ }
+
+ return nil
+ }
+}
+
+private final class FocusLogStream {
+ private let queue = DispatchQueue(label: "com.dynamicisland.focus.logstream", qos: .utility)
+ private var process: Process?
+ private var pipe: Pipe?
+ private var buffer = Data()
+ private var isRunning = false
+
+ private let metadataLock = NSLock()
+ private var lastIdentifier: String?
+ private var lastName: String?
+
+ var onMetadataUpdate: ((String?, String?) -> Void)?
+
+ func start() {
+ queue.async { [weak self] in
+ guard let self = self else { return }
+ guard !self.isRunning else { return }
+
+ let process = Process()
+ process.executableURL = URL(fileURLWithPath: "/usr/bin/log")
+ process.arguments = [
+ "stream",
+ "--style",
+ "compact",
+ "--level",
+ "info",
+ "--predicate",
+ "subsystem == \"com.apple.focus\""
+ ]
+
+ let pipe = Pipe()
+ process.standardOutput = pipe
+ process.standardError = pipe
+
+ pipe.fileHandleForReading.readabilityHandler = { [weak self] handle in
+ guard let self else { return }
+ let data = handle.availableData
+
+ if data.isEmpty {
+ self.queue.async { [weak self] in
+ self?.handleTermination()
+ }
+ return
+ }
+
+ self.queue.async { [weak self] in
+ self?.handleIncomingData(data)
+ }
+ }
+
+ process.terminationHandler = { [weak self] _ in
+ self?.queue.async {
+ self?.handleTermination()
+ }
+ }
+
+ do {
+ try process.run()
+ self.process = process
+ self.pipe = pipe
+ self.isRunning = true
+ debugPrint("[FocusLogStream] Started unified log tail for com.apple.focus")
+ } catch {
+ debugPrint("[FocusLogStream] Failed to start log stream: \(error)")
+ pipe.fileHandleForReading.readabilityHandler = nil
+ self.process = nil
+ self.pipe = nil
+ }
+ }
+ }
+
+ func stop() {
+ queue.async { [weak self] in
+ guard let self else { return }
+ guard self.isRunning else { return }
+ self.handleTermination(terminateProcess: true)
+ }
+ }
+
+ func latestMetadata() -> (identifier: String?, name: String?)? {
+ metadataLock.lock()
+ let identifier = lastIdentifier?.trimmingCharacters(in: .whitespacesAndNewlines)
+ let name = lastName?.trimmingCharacters(in: .whitespacesAndNewlines)
+ metadataLock.unlock()
+
+ let normalizedIdentifier = (identifier?.isEmpty == false) ? identifier : nil
+ let normalizedName = (name?.isEmpty == false) ? name : nil
+
+ if normalizedIdentifier == nil && normalizedName == nil {
+ return nil
+ }
+
+ return (normalizedIdentifier, normalizedName)
+ }
+
+ private func handleIncomingData(_ data: Data) {
+ buffer.append(data)
+
+ let newline: UInt8 = 0x0A
+
+ while let newlineIndex = buffer.firstIndex(of: newline) {
+ let lineData = buffer.prefix(upTo: newlineIndex)
+ buffer.removeSubrange(buffer.startIndex...newlineIndex)
+
+ let trimmedLineData: Data
+ if let lastByte = lineData.last, lastByte == 0x0D {
+ trimmedLineData = lineData.dropLast()
+ } else {
+ trimmedLineData = lineData
+ }
+
+ guard !trimmedLineData.isEmpty,
+ let line = String(data: trimmedLineData, encoding: .utf8) else {
+ continue
+ }
+
+ processLine(line)
+ }
+ }
+
+ private func processLine(_ line: String) {
+ let trimmed = line.trimmingCharacters(in: .whitespacesAndNewlines)
+ guard !trimmed.isEmpty else { return }
+
+ if trimmed.hasPrefix("Filtering the log data") || trimmed.hasPrefix("Timestamp") {
+ return
+ }
+
+ if trimmed.contains("active mode assertion: (null)") || trimmed.contains("active activity: (null)") {
+ clearMetadata()
+ return
+ }
+
+ var updatedIdentifier: String?
+ var updatedName: String?
+
+ if let identifier = FocusMetadataDecoder.extractIdentifier(from: trimmed), !identifier.isEmpty {
+ updatedIdentifier = identifier
+ }
+
+ if let name = FocusMetadataDecoder.extractName(from: trimmed), !name.isEmpty {
+ updatedName = name
+ }
+
+ guard updatedIdentifier != nil || updatedName != nil else { return }
+
+ var identifierToSend: String?
+ var nameToSend: String?
+
+ metadataLock.lock()
+ if let identifier = updatedIdentifier, !identifier.isEmpty {
+ lastIdentifier = identifier
+ }
+
+ if let name = updatedName, !name.isEmpty {
+ lastName = name
+ }
+
+ identifierToSend = lastIdentifier
+ nameToSend = lastName
+ metadataLock.unlock()
+
+ notifyMetadataUpdate(identifier: identifierToSend, name: nameToSend)
+ }
+
+ private func clearMetadata() {
+ metadataLock.lock()
+ lastIdentifier = nil
+ lastName = nil
+ metadataLock.unlock()
+ notifyMetadataUpdate(identifier: nil, name: nil)
+ }
+
+ private func handleTermination(terminateProcess: Bool = false) {
+ if terminateProcess, let process, process.isRunning {
+ process.terminate()
+ }
+
+ pipe?.fileHandleForReading.readabilityHandler = nil
+ pipe?.fileHandleForReading.closeFile()
+ pipe = nil
+
+ process = nil
+ buffer.removeAll(keepingCapacity: false)
+ isRunning = false
+ clearMetadata()
+ debugPrint("[FocusLogStream] Stopped unified log tail for com.apple.focus")
+ }
+
+ private func notifyMetadataUpdate(identifier: String?, name: String?) {
+ guard let handler = onMetadataUpdate else { return }
+ handler(identifier, name)
+ }
+}
+
+private enum FocusNotificationParsing {
+ static let identifierPattern: NSRegularExpression? = {
+ let pattern = "com\\.apple\\.(?:focus|donotdisturb)[A-Za-z0-9_.-]*"
+ return try? NSRegularExpression(pattern: pattern, options: [])
+ }()
+
+ static let identifierDetailPatterns: [NSRegularExpression] = {
+ let patterns = [
+ "modeIdentifier:\\s*'([^'\\s]+)'",
+ "activityIdentifier:\\s*([A-Za-z0-9._-]+)"
+ ]
+ return patterns.compactMap { try? NSRegularExpression(pattern: $0, options: []) }
+ }()
+
+ static let namePatterns: [NSRegularExpression] = {
+ let patterns = [
+ "(?i)(?:focusModeName|focusMode|displayName|name)\\s*=\\s*\"([^\"]+)\"",
+ "(?i)(?:focusModeName|focusMode|displayName|name)\\s*=\\s*([^;\\n]+)",
+ "activityDisplayName:\\s*([^;>\\n]+)",
+ "modeIdentifier:\\s*'com\\.apple\\.focus\\.([A-Za-z0-9._-]+)'"
+ ]
+ return patterns.compactMap { try? NSRegularExpression(pattern: $0, options: []) }
+ }()
+}
+
+private enum FocusMetadataDecoder {
+ static func cleanedString(_ string: String) -> String {
+ var trimmed = string.trimmingCharacters(in: .whitespacesAndNewlines)
+ trimmed = trimmed.trimmingCharacters(in: CharacterSet(charactersIn: "'\""))
+ return trimmed
+ }
+
+ static func extractIdentifier(from description: String) -> String? {
+ let fullRange = NSRange(description.startIndex.. 0,
+ let identifierRange = Range(match.range(at: 0), in: description) {
+ let candidate = cleanedString(String(description[identifierRange]))
+ if !candidate.isEmpty {
+ return candidate
+ }
+ }
+
+ for regex in FocusNotificationParsing.identifierDetailPatterns {
+ if let match = regex.firstMatch(in: description, options: [], range: fullRange),
+ match.numberOfRanges > 1,
+ let identifierRange = Range(match.range(at: 1), in: description) {
+ let candidate = cleanedString(String(description[identifierRange]))
+ if !candidate.isEmpty {
+ return candidate
+ }
+ }
+ }
+
+ return nil
+ }
+
+ static func extractName(from description: String) -> String? {
+ let fullRange = NSRange(description.startIndex.. 1,
+ let nameRange = Range(match.range(at: 1), in: description) {
+ let candidate = cleanedString(String(description[nameRange]))
+ if !candidate.isEmpty {
+ return candidate
+ }
+ }
+ }
+
+ return nil
+ }
}
diff --git a/ReadMe.md b/ReadMe.md
index 031788d..0ae4746 100644
--- a/ReadMe.md
+++ b/ReadMe.md
@@ -13,7 +13,7 @@
-
+
From 5b081ec8a742c25ed4fb734bc19499c7d7912f07 Mon Sep 17 00:00:00 2001
From: Hariharan Mudaliar
Date: Fri, 14 Nov 2025 02:30:51 +0530
Subject: [PATCH 2/5] Update Icons
---
.../managers/DoNotDisturbManager.swift | 295 +-----------------
1 file changed, 7 insertions(+), 288 deletions(-)
diff --git a/DynamicIsland/managers/DoNotDisturbManager.swift b/DynamicIsland/managers/DoNotDisturbManager.swift
index 577833a..135a845 100644
--- a/DynamicIsland/managers/DoNotDisturbManager.swift
+++ b/DynamicIsland/managers/DoNotDisturbManager.swift
@@ -22,7 +22,6 @@ final class DoNotDisturbManager: ObservableObject {
private let notificationCenter = DistributedNotificationCenter.default()
private let metadataExtractionQueue = DispatchQueue(label: "com.dynamicisland.focus.metadata", qos: .userInitiated)
- private let focusStateResolver = FocusStateResolver()
private let focusLogStream = FocusLogStream()
private init() {
@@ -286,17 +285,6 @@ final class DoNotDisturbManager: ObservableObject {
}
}
- if identifier == nil || name == nil {
- if let fallback = focusStateResolver.loadActiveFocusState(identifierMissing: identifier == nil, nameMissing: name == nil) {
- if identifier == nil {
- identifier = fallback.identifier
- }
- if name == nil {
- name = fallback.name
- }
- }
- }
-
return (name, identifier)
}
@@ -347,8 +335,8 @@ enum FocusModeType: String, CaseIterable {
case .driving: return "car.fill"
case .fitness: return "figure.run"
case .gaming: return "gamecontroller.fill"
- case .mindfulness: return "brain.head.profile"
- case .reading: return "book.fill"
+ case .mindfulness: return "circle.hexagongrid"
+ case .reading: return "book.closed.fill"
case .custom: return "app.badge"
case .unknown: return "moon.fill"
}
@@ -359,9 +347,9 @@ enum FocusModeType: String, CaseIterable {
case .doNotDisturb:
return Color(red: 0.370, green: 0.360, blue: 0.902)
case .work:
- return Color(red: 0.133, green: 0.475, blue: 0.992)
+ return Color(red: 0.414, green: 0.769, blue: 0.863, opacity: 1.0)
case .personal:
- return Color(red: 0.937, green: 0.282, blue: 0.624)
+ return Color(red: 0.748, green: 0.354, blue: 0.948, opacity: 1.0)
case .sleep:
return Color(red: 0.341, green: 0.384, blue: 0.980)
case .driving:
@@ -369,11 +357,11 @@ enum FocusModeType: String, CaseIterable {
case .fitness:
return Color(red: 0.176, green: 0.804, blue: 0.459)
case .gaming:
- return Color(red: 0.639, green: 0.329, blue: 0.937)
+ return Color(red: 0.043, green: 0.518, blue: 1.000, opacity: 1.0)
case .mindfulness:
- return Color(red: 0.239, green: 0.718, blue: 0.682)
+ return Color(red: 0.361, green: 0.898, blue: 0.883, opacity: 1.0)
case .reading:
- return Color(red: 0.239, green: 0.596, blue: 0.965)
+ return Color(red: 1.000, green: 0.622, blue: 0.044, opacity: 1.0)
case .custom:
return Color(red: 0.513, green: 0.478, blue: 0.965)
case .unknown:
@@ -661,275 +649,6 @@ private extension DoNotDisturbManager {
}
-// MARK: - Filesystem fallback for Focus metadata
-
-private final class FocusStateResolver {
- private struct CacheKey: Hashable { let url: URL }
- private struct FocusRecord { let identifier: String?; let name: String? }
-
- private let fileManager = FileManager.default
-
- private let identifierKeys = [
- "FocusModeIdentifier",
- "focusModeIdentifier",
- "FocusModeUUID",
- "focusModeUUID",
- "UUID",
- "uuid",
- "identifier",
- "Identifier",
- "activeModeIdentifier",
- "activeModeUUID",
- "modeIdentifier",
- "modeUUID"
- ]
-
- private let nameKeys = [
- "FocusModeName",
- "focusModeName",
- "FocusMode",
- "focusMode",
- "displayName",
- "display_name",
- "name",
- "Name",
- "modeName"
- ]
-
- private var cache: [CacheKey: (modified: Date, records: [FocusRecord])] = [:]
-
- private lazy var activeStateURLs: [URL] = {
- let home = fileManager.homeDirectoryForCurrentUser
- return [
- "Library/DoNotDisturb/ActiveState.plist",
- "Library/DoNotDisturb/DB/ActiveState.plist",
- "Library/Focus/ActiveState.plist"
- ].map { home.appendingPathComponent($0) }
- }()
-
- private lazy var catalogURLs: [URL] = {
- let home = fileManager.homeDirectoryForCurrentUser
- return [
- "Library/DoNotDisturb/DB/FocusPreferences.plist",
- "Library/DoNotDisturb/DB/FocusModeDefaults.plist",
- "Library/DoNotDisturb/DB/FocusModes.plist",
- "Library/Focus/FocusModeDefaults.plist",
- "Library/Preferences/com.apple.controlcenter.plist"
- ].map { home.appendingPathComponent($0) }
- }()
-
- func loadActiveFocusState(identifierMissing: Bool, nameMissing: Bool) -> (identifier: String?, name: String?)? {
- guard identifierMissing || nameMissing else { return nil }
-
- let activeRecords = loadRecords(from: activeStateURLs)
- let catalogRecords = loadRecords(from: catalogURLs)
- let allRecords = activeRecords + catalogRecords
-
- guard !allRecords.isEmpty else { return nil }
-
- var identifier: String?
- var name: String?
-
- if let record = activeRecords.first(where: { record in
- guard let id = record.identifier, !id.isEmpty else { return false }
- guard let name = record.name, !name.isEmpty else { return false }
- return true
- }) {
- identifier = record.identifier
- name = record.name
- }
-
- if identifierMissing && (identifier == nil || identifier?.isEmpty == true) {
- identifier = activeRecords.compactMap { $0.identifier?.trimmingCharacters(in: .whitespacesAndNewlines) }
- .first(where: { !$0.isEmpty })
-
- if identifier == nil {
- identifier = catalogRecords.compactMap { $0.identifier?.trimmingCharacters(in: .whitespacesAndNewlines) }
- .first(where: { !$0.isEmpty })
- }
- }
-
- if nameMissing && (name == nil || name?.isEmpty == true) {
- if let id = identifier {
- name = allRecords.first(where: {
- guard let candidateIdentifier = $0.identifier else { return false }
- return candidateIdentifier.compare(id, options: [.caseInsensitive, .diacriticInsensitive]) == .orderedSame && ($0.name?.isEmpty == false)
- })?.name
- }
-
- if name == nil {
- name = activeRecords.compactMap { $0.name?.trimmingCharacters(in: .whitespacesAndNewlines) }
- .first(where: { !$0.isEmpty })
- }
-
- if name == nil {
- name = catalogRecords.compactMap { $0.name?.trimmingCharacters(in: .whitespacesAndNewlines) }
- .first(where: { !$0.isEmpty })
- }
- }
-
- if identifier == nil && name == nil {
- return nil
- }
-
- return (identifierMissing ? identifier : nil, nameMissing ? name : nil)
- }
-
- private func loadRecords(from urls: [URL]) -> [FocusRecord] {
- var records: [FocusRecord] = []
-
- for url in urls {
- guard fileManager.fileExists(atPath: url.path) else { continue }
-
- if let cached = cachedRecords(for: url) {
- records.append(contentsOf: cached)
- continue
- }
-
- guard let attributes = try? fileManager.attributesOfItem(atPath: url.path),
- let modified = attributes[.modificationDate] as? Date,
- let data = try? Data(contentsOf: url) else {
- continue
- }
-
- let decoded = decodeData(data)
- let extracted = extractRecords(from: decoded)
-
- cache[CacheKey(url: url)] = (modified, extracted)
- records.append(contentsOf: extracted)
- }
-
- return records
- }
-
- private func cachedRecords(for url: URL) -> [FocusRecord]? {
- let key = CacheKey(url: url)
- guard let cached = cache[key],
- let attributes = try? fileManager.attributesOfItem(atPath: url.path),
- let modified = attributes[.modificationDate] as? Date,
- cached.modified == modified else {
- cache.removeValue(forKey: key)
- return nil
- }
-
- return cached.records
- }
-
- private func decodeData(_ data: Data) -> Any {
- if let propertyList = try? PropertyListSerialization.propertyList(from: data, options: [], format: nil) {
- return propertyList
- }
-
- if let unarchived = try? NSKeyedUnarchiver.unarchivedObject(ofClasses: [NSDictionary.self, NSArray.self, NSString.self, NSNumber.self, NSData.self], from: data) {
- return unarchived as Any
- }
-
- if let unarchived = try? NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(data) {
- return unarchived as Any
- }
-
- if let jsonObject = try? JSONSerialization.jsonObject(with: data, options: [.fragmentsAllowed]) {
- return jsonObject
- }
-
- if let string = String(data: data, encoding: .utf8) {
- return string
- }
-
- return [:]
- }
-
- private func extractRecords(from value: Any) -> [FocusRecord] {
- var results: [FocusRecord] = []
-
- if let dictionary = value as? [AnyHashable: Any] {
- let identifier = directMatch(in: dictionary, keys: identifierKeys)
- let name = directMatch(in: dictionary, keys: nameKeys)
-
- if identifier != nil || name != nil {
- results.append(FocusRecord(identifier: identifier, name: name))
- }
-
- for nested in dictionary.values {
- results.append(contentsOf: extractRecords(from: nested))
- }
- } else if let array = value as? [Any] {
- for element in array {
- results.append(contentsOf: extractRecords(from: element))
- }
- } else if let data = value as? Data {
- let decoded = decodeData(data)
- results.append(contentsOf: extractRecords(from: decoded))
- } else if let data = value as? NSData {
- let decoded = decodeData(data as Data)
- results.append(contentsOf: extractRecords(from: decoded))
- } else if let string = value as? String {
- let cleaned = FocusMetadataDecoder.cleanedString(string)
- if !cleaned.isEmpty {
- let identifier = matchIdentifier(in: cleaned)
- let name = identifier == nil ? matchName(in: cleaned) : matchName(in: cleaned, matching: identifier)
-
- if identifier != nil || name != nil {
- results.append(FocusRecord(identifier: identifier, name: name))
- }
- }
- }
-
- return results
- }
-
- private func directMatch(in dictionary: [AnyHashable: Any], keys: [String]) -> String? {
- for key in keys {
- if let candidate = dictionary[key], let string = normalizedString(from: candidate) {
- return string
- }
- }
- return nil
- }
-
- private func normalizedString(from value: Any) -> String? {
- switch value {
- case let string as String:
- return FocusMetadataDecoder.cleanedString(string)
- case let string as NSString:
- return FocusMetadataDecoder.cleanedString(string as String)
- case let number as NSNumber:
- return FocusMetadataDecoder.cleanedString(number.stringValue)
- case let data as Data:
- if let string = String(data: data, encoding: .utf8) {
- return FocusMetadataDecoder.cleanedString(string)
- }
- return nil
- case let data as NSData:
- return normalizedString(from: data as Data)
- default:
- return nil
- }
- }
-
- private func matchIdentifier(in string: String) -> String? {
- FocusMetadataDecoder.extractIdentifier(from: string)
- }
-
- private func matchName(in string: String, matching identifier: String? = nil) -> String? {
- if let name = FocusMetadataDecoder.extractName(from: string) {
- return name
- }
-
- if let identifier = identifier {
- let components = identifier.split(separator: ".")
- if let last = components.last {
- let derived = FocusMetadataDecoder.cleanedString(String(last).replacingOccurrences(of: "_", with: " "))
- if !derived.isEmpty {
- return derived.capitalized
- }
- }
- }
-
- return nil
- }
-}
-
private final class FocusLogStream {
private let queue = DispatchQueue(label: "com.dynamicisland.focus.logstream", qos: .utility)
private var process: Process?
From 45df2bce99195b9289d6097122802e21eca0165b Mon Sep 17 00:00:00 2001
From: Hariharan Mudaliar
Date: Fri, 14 Nov 2025 11:22:16 +0530
Subject: [PATCH 3/5] Added Private Icons
---
.../Focus/DoNotDisturbLiveActivity.swift | 10 +++++-----
.../components/UI/FocusIndicator.swift | 6 +++---
.../extensions/Image+InternalSystemName.swift | 14 ++++++++++++++
.../managers/DoNotDisturbManager.swift | 18 ++++++++++++++++++
4 files changed, 40 insertions(+), 8 deletions(-)
create mode 100644 DynamicIsland/extensions/Image+InternalSystemName.swift
diff --git a/DynamicIsland/components/Focus/DoNotDisturbLiveActivity.swift b/DynamicIsland/components/Focus/DoNotDisturbLiveActivity.swift
index c01aca0..962111e 100644
--- a/DynamicIsland/components/Focus/DoNotDisturbLiveActivity.swift
+++ b/DynamicIsland/components/Focus/DoNotDisturbLiveActivity.swift
@@ -110,13 +110,13 @@ struct DoNotDisturbLiveActivity: View {
}
}
- private var currentIconName: String {
+ private var currentIcon: Image {
if manager.isDoNotDisturbActive {
- return focusMode.sfSymbol
+ return focusMode.activeIcon
} else if showInactiveIcon {
- return focusMode.inactiveSymbol
+ return Image(systemName: focusMode.inactiveSymbol)
} else {
- return focusMode.sfSymbol
+ return focusMode.activeIcon
}
}
@@ -136,7 +136,7 @@ struct DoNotDisturbLiveActivity: View {
Color.clear
.overlay(alignment: .center) {
if iconWingWidth > 0 {
- Image(systemName: currentIconName)
+ currentIcon
.font(.system(size: 13, weight: .semibold))
.foregroundColor(currentIconColor)
.contentTransition(.opacity)
diff --git a/DynamicIsland/components/UI/FocusIndicator.swift b/DynamicIsland/components/UI/FocusIndicator.swift
index f887d13..0430cb9 100644
--- a/DynamicIsland/components/UI/FocusIndicator.swift
+++ b/DynamicIsland/components/UI/FocusIndicator.swift
@@ -14,7 +14,7 @@ struct FocusIndicator: View {
Capsule()
.fill(Color.black)
.overlay {
- Image(systemName: focusSymbol)
+ focusIcon
.font(.system(size: 12, weight: .semibold))
.foregroundStyle(accentColor)
.frame(width: 24, height: 24)
@@ -30,8 +30,8 @@ struct FocusIndicator: View {
)
}
- private var focusSymbol: String {
- focusMode.sfSymbol
+ private var focusIcon: Image {
+ focusMode.activeIcon
}
private var accentColor: Color {
diff --git a/DynamicIsland/extensions/Image+InternalSystemName.swift b/DynamicIsland/extensions/Image+InternalSystemName.swift
new file mode 100644
index 0000000..1fccf44
--- /dev/null
+++ b/DynamicIsland/extensions/Image+InternalSystemName.swift
@@ -0,0 +1,14 @@
+import SwiftUI
+
+@_silgen_name("$s7SwiftUI5ImageV19_internalSystemNameACSS_tcfC")
+private func _swiftUI_image(internalSystemName: String) -> Image?
+
+extension Image {
+ init?(internalSystemName systemName: String) {
+ guard let systemImage = _swiftUI_image(internalSystemName: systemName) else {
+ return nil
+ }
+
+ self = systemImage
+ }
+}
diff --git a/DynamicIsland/managers/DoNotDisturbManager.swift b/DynamicIsland/managers/DoNotDisturbManager.swift
index 135a845..e323fed 100644
--- a/DynamicIsland/managers/DoNotDisturbManager.swift
+++ b/DynamicIsland/managers/DoNotDisturbManager.swift
@@ -342,6 +342,24 @@ enum FocusModeType: String, CaseIterable {
}
}
+ var internalSymbolName: String? {
+ switch self {
+ case .work: return "person.lanyardcard.fill"
+ case .mindfulness: return "apple.mindfulness"
+ case .gaming: return "rocket.fill"
+ default: return nil
+ }
+ }
+
+ var activeIcon: Image {
+ if let internalSymbolName,
+ let image = Image(internalSystemName: internalSymbolName) {
+ return image
+ }
+
+ return Image(systemName: sfSymbol)
+ }
+
var accentColor: Color {
switch self {
case .doNotDisturb:
From cb57da47e25d92aaee3d574a634c58d10ba7fd2e Mon Sep 17 00:00:00 2001
From: Hariharan Mudaliar
Date: Fri, 14 Nov 2025 11:36:08 +0530
Subject: [PATCH 4/5] Support for custom focus
---
.../components/Focus/DoNotDisturbLiveActivity.swift | 13 ++++++++-----
1 file changed, 8 insertions(+), 5 deletions(-)
diff --git a/DynamicIsland/components/Focus/DoNotDisturbLiveActivity.swift b/DynamicIsland/components/Focus/DoNotDisturbLiveActivity.swift
index 962111e..a4b3f9e 100644
--- a/DynamicIsland/components/Focus/DoNotDisturbLiveActivity.swift
+++ b/DynamicIsland/components/Focus/DoNotDisturbLiveActivity.swift
@@ -66,7 +66,13 @@ struct DoNotDisturbLiveActivity: View {
private var desiredLabelWidth: CGFloat {
let measuredWidth = labelIntrinsicWidth + 8 // horizontal padding inside the label
let fallbackWidth = max(vm.closedNotchSize.width * 0.52, 136)
- return max(measuredWidth, fallbackWidth)
+ var width = max(measuredWidth, fallbackWidth)
+
+ if focusMode == .doNotDisturb && shouldShowLabel {
+ width = max(width, 164)
+ }
+
+ return width
}
private var shouldShowLabel: Bool {
@@ -89,15 +95,12 @@ struct DoNotDisturbLiveActivity: View {
private var labelText: String {
let trimmed = manager.currentFocusModeName.trimmingCharacters(in: .whitespacesAndNewlines)
if !trimmed.isEmpty {
- if focusMode == .doNotDisturb {
- return "Focus"
- }
return trimmed
}
let fallback = focusMode.displayName
if focusMode == .doNotDisturb {
- return "Focus"
+ return "Do Not Disturb"
}
return fallback.isEmpty ? "Focus" : fallback
}
From 8de7d8b0334eaf778966c47e0b6592424e011cb9 Mon Sep 17 00:00:00 2001
From: Hariharan Mudaliar
Date: Fri, 14 Nov 2025 12:35:27 +0530
Subject: [PATCH 5/5] BugFix: Pulsating causing high energy usage
---
.../components/Privacy/PrivacyLiveActivity.swift | 12 +++---------
1 file changed, 3 insertions(+), 9 deletions(-)
diff --git a/DynamicIsland/components/Privacy/PrivacyLiveActivity.swift b/DynamicIsland/components/Privacy/PrivacyLiveActivity.swift
index 380c6f6..a28b992 100644
--- a/DynamicIsland/components/Privacy/PrivacyLiveActivity.swift
+++ b/DynamicIsland/components/Privacy/PrivacyLiveActivity.swift
@@ -110,8 +110,7 @@ struct PrivacyLiveActivity: View {
// Individual privacy icon component
struct PrivacyIcon: View {
let type: PrivacyIndicatorType
- @State private var isPulsing = false
-
+
// Color based on type: camera = #26CC41, mic = #FF9402
private var iconColor: Color {
type == .camera ? Color(red: 0.152, green: 0.804, blue: 0.256) : Color(red: 1.000, green: 0.584, blue: 0.010)
@@ -123,12 +122,7 @@ struct PrivacyIcon: View {
.font(.system(size: 13, weight: .semibold))
.foregroundColor(iconColor)
.frame(width: 24, height: 24)
- .scaleEffect(isPulsing ? 1.1 : 1.0)
- .opacity(isPulsing ? 1.0 : 0.85)
- .onAppear {
- withAnimation(.easeInOut(duration: 1.5).repeatForever(autoreverses: true)) {
- isPulsing = true
- }
- }
+ .scaleEffect(1.0)
+ .opacity(1.0)
}
}