diff --git a/DynamicIsland/ContentView.swift b/DynamicIsland/ContentView.swift index bbbcb2e..66be883 100644 --- a/DynamicIsland/ContentView.swift +++ b/DynamicIsland/ContentView.swift @@ -587,56 +587,6 @@ struct ContentView: View { .frame(height: vm.effectiveClosedNotchHeight + (isHovering ? 8 : 0), alignment: .center) } - func RecordingLiveActivity() -> some View { - HStack { - // Left side - Recording icon (EXACT same structure as music album art) - HStack { - Color.clear - .aspectRatio(1, contentMode: .fit) - .background( - ZStack { - RoundedRectangle(cornerRadius: MusicPlayerImageSizes.cornerRadiusInset.closed) - .fill(Color.red.opacity(0.1)) - - Circle() - .fill(Color.red) - .frame(width: 12, height: 12) - .modifier(PulsingModifier()) - } - ) - .frame(width: max(0, vm.effectiveClosedNotchHeight - 12), height: max(0, vm.effectiveClosedNotchHeight - 12)) - } - .frame(width: max(0, vm.effectiveClosedNotchHeight - (isHovering ? 0 : 12) + gestureProgress / 2), - height: max(0, vm.effectiveClosedNotchHeight - (isHovering ? 0 : 12))) - - // Center - Fixed width (SAME as music) - Rectangle() - .fill(.black) - .frame(width: vm.closedNotchSize.width + (isHovering ? 8 : 0)) - - // Right side - Empty space to balance (SAME structure as music visualizer) - HStack { - // Empty - no content - } - .frame(width: max(0, vm.effectiveClosedNotchHeight - (isHovering ? 0 : 12) + gestureProgress / 2), - height: max(0, vm.effectiveClosedNotchHeight - (isHovering ? 0 : 12)), alignment: .center) - } - .frame(height: vm.effectiveClosedNotchHeight + (isHovering ? 8 : 0), alignment: .center) - } - - // Helper function to format recording duration - private func formatDuration(_ duration: TimeInterval) -> String { - let hours = Int(duration) / 3600 - let minutes = Int(duration) / 60 % 60 - let seconds = Int(duration) % 60 - - if hours > 0 { - return String(format: "%d:%02d:%02d", hours, minutes, seconds) - } else { - return String(format: "%d:%02d", minutes, seconds) - } - } - @ViewBuilder var dragDetector: some View { if Defaults[.dynamicShelf] { @@ -907,19 +857,3 @@ struct FullScreenDropDelegate: DropDelegate { return true } } - -// Pulsing animation modifier for recording indicator -struct PulsingModifier: ViewModifier { - @State private var isPulsing = false - - func body(content: Content) -> some View { - content - .scaleEffect(isPulsing ? 1.2 : 1.0) - .opacity(isPulsing ? 0.7 : 1.0) - .onAppear { - withAnimation(.easeInOut(duration: 0.8).repeatForever(autoreverses: true)) { - isPulsing = true - } - } - } -} diff --git a/DynamicIsland/components/Recording/RecordingLiveActivity.swift b/DynamicIsland/components/Recording/RecordingLiveActivity.swift new file mode 100644 index 0000000..6c4edd3 --- /dev/null +++ b/DynamicIsland/components/Recording/RecordingLiveActivity.swift @@ -0,0 +1,84 @@ +// +// RecordingLiveActivity.swift +// DynamicIsland +// +// Created for screen recording live activity +// + +import SwiftUI +import Defaults + +struct RecordingLiveActivity: View { + @EnvironmentObject var vm: DynamicIslandViewModel + @ObservedObject var recordingManager = ScreenRecordingManager.shared + @State private var isHovering: Bool = false + @State private var gestureProgress: CGFloat = 0 + @State private var isExpanded: Bool = false + + var body: some View { + HStack(spacing: 0) { + // Left - Red circle with animation + Color.clear + .background { + if isExpanded { + HStack { + ZStack { + RoundedRectangle(cornerRadius: 6) + .fill(Color.red.opacity(0.15)) + + Circle() + .fill(Color.red) + .frame(width: 10, height: 10) + .modifier(PulsingModifier()) + } + .frame(width: vm.effectiveClosedNotchHeight - 12, height: vm.effectiveClosedNotchHeight - 12) + } + .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .leading) + } + } + .frame(width: isExpanded ? max(0, vm.effectiveClosedNotchHeight - (isHovering ? 0 : 12) + gestureProgress / 2) : 0, height: vm.effectiveClosedNotchHeight - (isHovering ? 0 : 12)) + + // Center - Black fill + Rectangle() + .fill(.black) + .frame(width: vm.closedNotchSize.width + (isHovering ? 8 : 0)) + + // Right - Empty for symmetry with animation + Color.clear + .frame(width: isExpanded ? max(0, vm.effectiveClosedNotchHeight - (isHovering ? 0 : 12) + gestureProgress / 2) : 0, height: vm.effectiveClosedNotchHeight - (isHovering ? 0 : 12)) + } + .frame(height: vm.effectiveClosedNotchHeight + (isHovering ? 8 : 0)) + .onAppear { + withAnimation(.smooth(duration: 0.4)) { + isExpanded = true + } + } + .onChange(of: recordingManager.isRecording) { _, newValue in + if !newValue { + withAnimation(.smooth(duration: 0.4)) { + isExpanded = false + } + } else { + withAnimation(.smooth(duration: 0.4)) { + isExpanded = true + } + } + } + } +} + +// Pulsing animation modifier for recording indicator +struct PulsingModifier: ViewModifier { + @State private var isPulsing = false + + func body(content: Content) -> some View { + content + .scaleEffect(isPulsing ? 1.2 : 1.0) + .opacity(isPulsing ? 0.7 : 1.0) + .onAppear { + withAnimation(.easeInOut(duration: 0.8).repeatForever(autoreverses: true)) { + isPulsing = true + } + } + } +}