diff --git a/DynamicIsland/components/LockScreen/LockScreenLiveActivity.swift b/DynamicIsland/components/LockScreen/LockScreenLiveActivity.swift index 53b75d2..830a09f 100644 --- a/DynamicIsland/components/LockScreen/LockScreenLiveActivity.swift +++ b/DynamicIsland/components/LockScreen/LockScreenLiveActivity.swift @@ -16,6 +16,14 @@ struct LockScreenLiveActivity: View { @State private var gestureProgress: CGFloat = 0 @State private var isExpanded: Bool = false + private var expandAnimation: Animation { + .smooth(duration: LockScreenAnimationTimings.lockExpand) + } + + private var collapseAnimation: Animation { + .smooth(duration: LockScreenAnimationTimings.unlockCollapse) + } + private var iconColor: Color { .white } @@ -48,9 +56,9 @@ struct LockScreenLiveActivity: View { .frame(height: vm.effectiveClosedNotchHeight + (isHovering ? 8 : 0)) .onAppear { iconAnimator.update(isLocked: lockScreenManager.isLocked, animated: false) - // Expand immediately without animation to avoid conflicts - withAnimation(.smooth(duration: 0.4)) { - isExpanded = true + let shouldStartExpanded = lockScreenManager.isLocked || !lockScreenManager.isLockIdle + withAnimation(expandAnimation) { + isExpanded = shouldStartExpanded } } .onDisappear { @@ -59,17 +67,26 @@ struct LockScreenLiveActivity: View { } .onChange(of: lockScreenManager.isLockIdle) { _, newValue in if newValue { - withAnimation(.smooth(duration: 0.4)) { + withAnimation(collapseAnimation) { isExpanded = false } - } else { - withAnimation(.smooth(duration: 0.4)) { + } else if lockScreenManager.isLocked { + withAnimation(expandAnimation) { isExpanded = true } } } .onChange(of: lockScreenManager.isLocked) { _, newValue in iconAnimator.update(isLocked: newValue) + if newValue { + withAnimation(expandAnimation) { + isExpanded = true + } + } else { + withAnimation(collapseAnimation) { + isExpanded = false + } + } } .animation(.spring(response: 0.5, dampingFraction: 0.85), value: lockScreenManager.isLocked) .animation(.easeOut(duration: 0.25), value: isExpanded) diff --git a/DynamicIsland/managers/LockScreenLiveActivityWindowManager.swift b/DynamicIsland/managers/LockScreenLiveActivityWindowManager.swift index 09be4fe..2a50070 100644 --- a/DynamicIsland/managers/LockScreenLiveActivityWindowManager.swift +++ b/DynamicIsland/managers/LockScreenLiveActivityWindowManager.swift @@ -9,6 +9,7 @@ import AppKit import Defaults import SkyLightWindow import SwiftUI +import QuartzCore @MainActor class LockScreenLiveActivityWindowManager { @@ -165,9 +166,7 @@ class LockScreenLiveActivityWindowManager { overlayAnimator.update(isLocked: false) hideTask = Task { [weak self] in - //try? await Task.sleep(for: .milliseconds(450)) - try? await Task.sleep(for: .seconds(max(Defaults[.waitInterval], 0.7))) - //try? await Task.sleep(for: .seconds(1.0)) + try? await Task.sleep(for: .seconds(LockScreenAnimationTimings.unlockCollapse)) guard let self, !Task.isCancelled else { return } await MainActor.run { self.hideWithAnimation() @@ -185,17 +184,18 @@ class LockScreenLiveActivityWindowManager { private func hideWithAnimation() { guard let window else { return } - withAnimation(.easeInOut(duration: 0.18)) { + withAnimation(.smooth(duration: LockScreenAnimationTimings.unlockCollapse)) { overlayModel.opacity = 0 overlayModel.scale = 0.7 } NSAnimationContext.runAnimationGroup { context in - context.duration = 0.2 + context.duration = LockScreenAnimationTimings.unlockCollapse + context.timingFunction = CAMediaTimingFunction(name: .easeInEaseOut) window.animator().alphaValue = 0 } - DispatchQueue.main.asyncAfter(deadline: .now() + 0.22) { + DispatchQueue.main.asyncAfter(deadline: .now() + LockScreenAnimationTimings.unlockCollapse + 0.02) { window.orderOut(nil) } diff --git a/DynamicIsland/managers/LockScreenManager.swift b/DynamicIsland/managers/LockScreenManager.swift index 3064522..2e0cca5 100644 --- a/DynamicIsland/managers/LockScreenManager.swift +++ b/DynamicIsland/managers/LockScreenManager.swift @@ -12,6 +12,11 @@ import AppKit import Defaults import SwiftUI +enum LockScreenAnimationTimings { + static let lockExpand: TimeInterval = 0.45 + static let unlockCollapse: TimeInterval = 0.82 +} + @MainActor class LockScreenManager: ObservableObject { static let shared = LockScreenManager() @@ -125,7 +130,7 @@ class LockScreenManager: ObservableObject { if Defaults[.enableLockScreenLiveActivity] { collapseTask?.cancel() collapseTask = Task { [weak self] in - try? await Task.sleep(for: .milliseconds(380)) + try? await Task.sleep(for: .seconds(LockScreenAnimationTimings.unlockCollapse)) guard let self = self, !Task.isCancelled else { return } await MainActor.run { self.coordinator.toggleExpandingView(status: false, type: .lockScreen) @@ -134,7 +139,7 @@ class LockScreenManager: ObservableObject { } self.lastUpdated = Date() - self.updateIdleState(locked: false) + self.updateIdleState(locked: false) self.isLocked = false print("[\(self.timestamp())] LockScreenManager: ✅ Lock screen deactivated") @@ -150,11 +155,13 @@ class LockScreenManager: ObservableObject { } else { debounceIdleTask?.cancel() debounceIdleTask = Task { [weak self] in - try? await Task.sleep(for: .seconds(Defaults[.waitInterval])) + let configuredInterval = max(Defaults[.waitInterval], 0) + let idleDelay = min(max(configuredInterval, 0.2), LockScreenAnimationTimings.unlockCollapse) + try? await Task.sleep(for: .seconds(idleDelay)) guard let self = self, !Task.isCancelled else { return } await MainActor.run { - if self.lastUpdated.timeIntervalSinceNow < -Defaults[.waitInterval] { - withAnimation { + if self.lastUpdated.timeIntervalSinceNow < -idleDelay { + withAnimation(.smooth(duration: 0.3)) { self.isLockIdle = !self.isLocked } }