From 28320e4cb81ae8229003255685d4f14702e26ebf Mon Sep 17 00:00:00 2001 From: Hariharan Mudaliar Date: Fri, 20 Mar 2026 20:24:08 +0530 Subject: [PATCH] HotFix: Remove Unnecessary Mach service for Stats --- DynamicIsland.xcodeproj/project.pbxproj | 4 +- DynamicIsland/Localizable.xcstrings | 4 + DynamicIsland/components/AnimatedFace.swift | 98 ------------------- DynamicIsland/components/EmptyState.swift | 6 +- .../components/IdleAnimationView.swift | 18 +--- .../Settings/AnimationEditorView.swift | 15 +-- .../IdleAnimationsSettingsSection.swift | 12 +-- .../components/Settings/SettingsView.swift | 6 +- .../managers/IdleAnimationManager.swift | 40 +++++--- DynamicIsland/managers/StatsManager.swift | 14 ++- DynamicIsland/models/Constants.swift | 2 - 11 files changed, 58 insertions(+), 161 deletions(-) delete mode 100644 DynamicIsland/components/AnimatedFace.swift diff --git a/DynamicIsland.xcodeproj/project.pbxproj b/DynamicIsland.xcodeproj/project.pbxproj index 1234a33..1abb11f 100644 --- a/DynamicIsland.xcodeproj/project.pbxproj +++ b/DynamicIsland.xcodeproj/project.pbxproj @@ -433,7 +433,7 @@ "CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development"; CODE_SIGN_STYLE = Manual; COMBINE_HIDPI_IMAGES = YES; - CURRENT_PROJECT_VERSION = 8; + CURRENT_PROJECT_VERSION = 9; DEVELOPMENT_ASSET_PATHS = "\"DynamicIsland/Preview Content\""; DEVELOPMENT_TEAM = ""; "DEVELOPMENT_TEAM[sdk=macosx*]" = 9Y64TRM77N; @@ -505,7 +505,7 @@ "CODE_SIGN_IDENTITY[sdk=macosx*]" = "Developer ID Application"; CODE_SIGN_STYLE = Manual; COMBINE_HIDPI_IMAGES = YES; - CURRENT_PROJECT_VERSION = 8; + CURRENT_PROJECT_VERSION = 9; DEVELOPMENT_ASSET_PATHS = "\"DynamicIsland/Preview Content\""; DEVELOPMENT_TEAM = ""; "DEVELOPMENT_TEAM[sdk=macosx*]" = 9Y64TRM77N; diff --git a/DynamicIsland/Localizable.xcstrings b/DynamicIsland/Localizable.xcstrings index 13c3eed..94d116b 100644 --- a/DynamicIsland/Localizable.xcstrings +++ b/DynamicIsland/Localizable.xcstrings @@ -9907,9 +9907,13 @@ } } } + }, + "Enable \"Idle Animation\" to customize animations" : { + }, "Enable \"Show cool face animation while inactivity\" to customize animations" : { "comment" : "A message displayed below the section header in the IdleAnimationsSettingsSection, explaining that users can customize animations by enabling this feature.", + "extractionState" : "stale", "isCommentAutoGenerated" : true, "localizations" : { "pt-BR" : { diff --git a/DynamicIsland/components/AnimatedFace.swift b/DynamicIsland/components/AnimatedFace.swift deleted file mode 100644 index 66d4b65..0000000 --- a/DynamicIsland/components/AnimatedFace.swift +++ /dev/null @@ -1,98 +0,0 @@ -/* - * Atoll (DynamicIsland) - * Copyright (C) 2024-2026 Atoll Contributors - * - * Originally from boring.notch project - * Modified and adapted for Atoll (DynamicIsland) - * See NOTICE for details. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -import SwiftUI - -struct MinimalFaceFeatures: View { - @State private var isBlinking = false - @State var height:CGFloat = 20; - @State var width:CGFloat = 30; - - var body: some View { - VStack(spacing: 4) { // Adjusted spacing to fit within 30x30 - // Eyes - HStack(spacing: 4) { // Adjusted spacing to fit within 30x30 - Eye(isBlinking: $isBlinking) - Eye(isBlinking: $isBlinking) - } - - // Nose and mouth combined - VStack(spacing: 2) { // Adjusted spacing to fit within 30x30 - // Nose - RoundedRectangle(cornerRadius: 2) - .fill(Color.white) - .frame(width: 3, height: 4) - - // Mouth (happy) - GeometryReader { geometry in - Path { path in - let width = geometry.size.width - let height = geometry.size.height - path.move(to: CGPoint(x: 0, y: height / 2)) - path.addQuadCurve(to: CGPoint(x: width, y: height / 2), control: CGPoint(x: width / 2, y: height)) - } - .stroke(Color.white, lineWidth: 2) - } - .frame(width: 14, height: 10) - } - } - .frame(width: self.width, height: self.height) // Maximum size of face - .onAppear { - startBlinking() - } - } - - func startBlinking() { - Timer.scheduledTimer(withTimeInterval: 3, repeats: true) { _ in - withAnimation(.spring(duration: 0.2)) { - isBlinking = true - } - DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { - withAnimation(.spring(duration: 0.2)) { - isBlinking = false - } - } - } - } -} - -struct Eye: View { - @Binding var isBlinking: Bool - - var body: some View { - RoundedRectangle(cornerRadius: 10) - .fill(Color.white) - .frame(width: 4, height: isBlinking ? 1 : 4) - .frame(maxWidth: 15, maxHeight: 15) // Adjusted max size - .animation(.easeInOut(duration: 0.1), value: isBlinking) - } -} - -struct MinimalFaceFeatures_Previews: PreviewProvider { - static var previews: some View { - ZStack { - Color.black - MinimalFaceFeatures() - } - .previewLayout(.fixed(width: 60, height: 60)) // Adjusted preview size for better visibility - } -} diff --git a/DynamicIsland/components/EmptyState.swift b/DynamicIsland/components/EmptyState.swift index c372a66..1da3b4e 100644 --- a/DynamicIsland/components/EmptyState.swift +++ b/DynamicIsland/components/EmptyState.swift @@ -28,8 +28,10 @@ struct EmptyStateView: View { var body: some View { HStack { - MinimalFaceFeatures( - height: 70, width: 80) + Image(systemName: "music.note") + .font(.system(size: 24)) + .foregroundColor(.gray) + .frame(width: 80, height: 70) Text(message) .font(.system(size:14)) .foregroundColor(.gray) diff --git a/DynamicIsland/components/IdleAnimationView.swift b/DynamicIsland/components/IdleAnimationView.swift index 935c714..41015a9 100644 --- a/DynamicIsland/components/IdleAnimationView.swift +++ b/DynamicIsland/components/IdleAnimationView.swift @@ -31,8 +31,8 @@ struct IdleAnimationView: View { AnimationContentView(animation: animation) .id("\(animation.id)-\(overrides[animation.id.uuidString]?.hashValue ?? 0)") // Force recreation when override changes } else { - // Fallback to original face if nothing selected - MinimalFaceFeatures(height: 20, width: 30) + // No animation selected + EmptyView() } } } @@ -85,20 +85,6 @@ private struct AnimationContentView: View { .padding(.bottom, config.paddingBottom) .frame(width: config.expandWithAnimation ? nil : 30, height: 20) .clipped() - - case .builtInFace: - // Apply transforms to built-in face too! - MinimalFaceFeatures(height: 20, width: 30) - .frame( - width: config.cropWidth * config.scale, - height: config.cropHeight * config.scale - ) - .offset(x: config.offsetX, y: config.offsetY) - .rotationEffect(.degrees(config.rotation)) - .opacity(config.opacity) - .padding(.bottom, config.paddingBottom) - .frame(width: config.expandWithAnimation ? nil : 30, height: 20) - .clipped() } } } diff --git a/DynamicIsland/components/Settings/AnimationEditorView.swift b/DynamicIsland/components/Settings/AnimationEditorView.swift index 1f6ebf1..9480841 100644 --- a/DynamicIsland/components/Settings/AnimationEditorView.swift +++ b/DynamicIsland/components/Settings/AnimationEditorView.swift @@ -170,16 +170,11 @@ struct AnimationEditorView: View { .frame(width: 30 * previewScale, height: 20 * previewScale) Group { - if let existing = existingAnimation, case .builtInFace = existing.source { - MinimalFaceFeatures(height: 20, width: 30) - .scaleEffect(previewScale) - } else { - LottieView(state: LUStateData( - type: .loadedFrom(sourceURL), - speed: speed, - loopMode: loopMode.lottieLoopMode - )) - } + LottieView(state: LUStateData( + type: .loadedFrom(sourceURL), + speed: speed, + loopMode: loopMode.lottieLoopMode + )) } .frame(width: cropWidth * scale * previewScale, height: cropHeight * scale * previewScale) .offset(x: offsetX * previewScale, y: offsetY * previewScale) diff --git a/DynamicIsland/components/Settings/IdleAnimationsSettingsSection.swift b/DynamicIsland/components/Settings/IdleAnimationsSettingsSection.swift index be45da7..fa587bc 100644 --- a/DynamicIsland/components/Settings/IdleAnimationsSettingsSection.swift +++ b/DynamicIsland/components/Settings/IdleAnimationsSettingsSection.swift @@ -87,12 +87,6 @@ struct IdleAnimationsSettingsSection: View { print("🔧 [Edit] Lottie URL: \(url)") editorSourceURL = url editorIsRemote = true - - case .builtInFace: - print("🔧 [Edit] Built-in face - using dummy URL") - // Use a dummy URL for built-in face (editor only needs URL for preview) - editorSourceURL = URL(string: "builtin://face")! - editorIsRemote = false } // Show editor with slight delay to ensure state is set @@ -118,7 +112,7 @@ struct IdleAnimationsSettingsSection: View { } .frame(height: 140) } else { - Text("Enable \"Show cool face animation while inactivity\" to customize animations") + Text("Enable \"Idle Animation\" to customize animations") .font(.caption) .foregroundStyle(.secondary) .padding(.vertical, 8) @@ -388,10 +382,6 @@ struct AnimationPreview: View { loopMode: .loop )) .frame(width: 60, height: 40) - - case .builtInFace: - MinimalFaceFeatures(height: 40, width: 60) - .scaleEffect(2.0) // Scale up the face for preview } } } diff --git a/DynamicIsland/components/Settings/SettingsView.swift b/DynamicIsland/components/Settings/SettingsView.swift index 718ccc2..ae7a5ec 100644 --- a/DynamicIsland/components/Settings/SettingsView.swift +++ b/DynamicIsland/components/Settings/SettingsView.swift @@ -807,7 +807,7 @@ struct SettingsView: View { SettingsSearchEntry(tab: .appearance, title: "Slider color", keywords: ["slider", "accent"], highlightID: SettingsTab.appearance.highlightID(for: "Slider color")), SettingsSearchEntry(tab: .appearance, title: "Enable Dynamic mirror", keywords: ["mirror", "reflection"], highlightID: SettingsTab.appearance.highlightID(for: "Enable Dynamic mirror")), SettingsSearchEntry(tab: .appearance, title: "Mirror shape", keywords: ["mirror shape", "circle", "rectangle"], highlightID: SettingsTab.appearance.highlightID(for: "Mirror shape")), - SettingsSearchEntry(tab: .appearance, title: "Show cool face animation while inactivity", keywords: ["face animation", "idle"], highlightID: SettingsTab.appearance.highlightID(for: "Show cool face animation while inactivity")), + SettingsSearchEntry(tab: .appearance, title: "Idle Animation", keywords: ["face animation", "idle", "cool face"], highlightID: SettingsTab.appearance.highlightID(for: "Idle Animation")), SettingsSearchEntry(tab: .appearance, title: "App icon", keywords: ["app icon", "custom icon"], highlightID: SettingsTab.appearance.highlightID(for: "App icon")), // Lock Screen @@ -3951,8 +3951,8 @@ struct Appearance: View { .tag(MirrorShapeEnum.rectangle) } .settingsHighlight(id: highlightID("Mirror shape")) - Defaults.Toggle("Show cool face animation while inactivity", key: .showNotHumanFace) - .settingsHighlight(id: highlightID("Show cool face animation while inactivity")) + Defaults.Toggle("Idle Animation", key: .showNotHumanFace) + .settingsHighlight(id: highlightID("Idle Animation")) } header: { HStack { Text("Additional features") diff --git a/DynamicIsland/managers/IdleAnimationManager.swift b/DynamicIsland/managers/IdleAnimationManager.swift index 3bb02cc..ed1d4f6 100644 --- a/DynamicIsland/managers/IdleAnimationManager.swift +++ b/DynamicIsland/managers/IdleAnimationManager.swift @@ -39,19 +39,10 @@ class IdleAnimationManager { // MARK: - Initialization - /// Load bundled animations from the LottieAnimations folder and built-in face + /// Load bundled animations from the LottieAnimations folder func initializeDefaultAnimations() { var animations: [CustomIdleAnimation] = [] - // Add built-in face as first option - let builtInFace = CustomIdleAnimation( - name: "Classic Face", - source: .builtInFace, - speed: 1.0, - isBuiltIn: true - ) - animations.append(builtInFace) - // Load bundled Lottie files if let bundledAnimations = loadBundledAnimations() { animations.append(contentsOf: bundledAnimations) @@ -60,14 +51,21 @@ class IdleAnimationManager { // Get existing animations var existing = Defaults[.customIdleAnimations] + // Remove any legacy builtInFace entries that may be stored from older versions + existing.removeAll { animation in + if case .lottieFile(let url) = animation.source, url.absoluteString == "builtin://face" { + return true + } + return false + } + if existing.isEmpty { // First launch - set everything Defaults[.customIdleAnimations] = animations - Defaults[.selectedIdleAnimation] = builtInFace + Defaults[.selectedIdleAnimation] = animations.first print("✅ [IdleAnimationManager] First launch: Initialized with \(animations.count) animations") } else { // Subsequent launch - ensure all bundled animations are present - let existingIDs = Set(existing.map { $0.id }) let existingNames = Set(existing.filter { $0.isBuiltIn }.map { $0.name }) // Add any missing bundled animations @@ -79,7 +77,23 @@ class IdleAnimationManager { } Defaults[.customIdleAnimations] = existing - print("✅ [IdleAnimationManager] Subsequent launch: \(existing.count) total animations (\(animations.count - 1) bundled + built-in face)") + + // If current selection was the old built-in face (no longer valid), select first available + if let selected = Defaults[.selectedIdleAnimation] { + let isValid: Bool + switch selected.source { + case .lottieFile(let url): + isValid = url.absoluteString != "builtin://face" + case .lottieURL: + isValid = true + } + if !isValid { + Defaults[.selectedIdleAnimation] = existing.first + print("🔄 [IdleAnimationManager] Migrated selection from legacy face to: \(existing.first?.name ?? "nil")") + } + } + + print("✅ [IdleAnimationManager] Subsequent launch: \(existing.count) total animations") } } diff --git a/DynamicIsland/managers/StatsManager.swift b/DynamicIsland/managers/StatsManager.swift index 8bcef43..7abe47e 100644 --- a/DynamicIsland/managers/StatsManager.swift +++ b/DynamicIsland/managers/StatsManager.swift @@ -414,14 +414,20 @@ class StatsManager: ObservableObject { private var delayedStopTimer: Timer? private var delayedStartTimer: Timer? private let maxHistoryPoints = 30 + /// Cached host port to avoid leaking Mach send rights. + /// Every call to `mach_host_self()` acquires a new send right that must be + /// explicitly deallocated; caching it once prevents port exhaustion over time. + private let hostPort: mach_port_t = mach_host_self() private let totalPhysicalMemory: UInt64 = { var stats = host_basic_info() var count = mach_msg_type_number_t(MemoryLayout.size / MemoryLayout.size) + let initHostPort = mach_host_self() let result = withUnsafeMutablePointer(to: &stats) { $0.withMemoryRebound(to: integer_t.self, capacity: Int(count)) { - host_info(mach_host_self(), HOST_BASIC_INFO, $0, &count) + host_info(initHostPort, HOST_BASIC_INFO, $0, &count) } } + mach_port_deallocate(mach_task_self_, initHostPort) if result == KERN_SUCCESS { return UInt64(stats.max_mem) } @@ -786,7 +792,7 @@ class StatsManager: ObservableObject { let result = withUnsafeMutablePointer(to: &info) { $0.withMemoryRebound(to: integer_t.self, capacity: Int(count)) { - host_statistics(mach_host_self(), HOST_CPU_LOAD_INFO, $0, &count) + host_statistics(hostPort, HOST_CPU_LOAD_INFO, $0, &count) } } @@ -858,7 +864,7 @@ class StatsManager: ObservableObject { let vmResult = withUnsafeMutablePointer(to: &vmStatistics) { $0.withMemoryRebound(to: integer_t.self, capacity: Int(size)) { - host_statistics64(mach_host_self(), HOST_VM_INFO64, $0, &size) + host_statistics64(hostPort, HOST_VM_INFO64, $0, &size) } } @@ -978,7 +984,7 @@ class StatsManager: ObservableObject { var cpuInfo: processor_info_array_t? var numCpuInfo: mach_msg_type_number_t = 0 var numCPUsU: natural_t = 0 - let result = host_processor_info(mach_host_self(), PROCESSOR_CPU_LOAD_INFO, &numCPUsU, &cpuInfo, &numCpuInfo) + let result = host_processor_info(hostPort, PROCESSOR_CPU_LOAD_INFO, &numCPUsU, &cpuInfo, &numCpuInfo) guard result == KERN_SUCCESS, let cpuInfo else { return cpuCoreUsage } diff --git a/DynamicIsland/models/Constants.swift b/DynamicIsland/models/Constants.swift index 01cb6d0..8ae20b6 100644 --- a/DynamicIsland/models/Constants.swift +++ b/DynamicIsland/models/Constants.swift @@ -128,13 +128,11 @@ enum AnimationLoopMode: String, Codable, CaseIterable { enum AnimationSource: Codable, Hashable, Equatable { case lottieFile(URL) // Local file (in app support or bundle) case lottieURL(URL) // Remote URL - case builtInFace // Original MinimalFaceFeatures var displayType: String { switch self { case .lottieFile: return "Local" case .lottieURL: return "Remote" - case .builtInFace: return "Built-in" } } }