import ActivityKit import WidgetKit import SwiftUI struct WalkLiveActivity: Widget { var body: some WidgetConfiguration { ActivityConfiguration(for: WalkActivityAttributes.self) { context in // Lock-Screen / Banner view lockScreen(state: context.state) .activityBackgroundTint(Color.accentColor.opacity(0.12)) .activitySystemActionForegroundColor(Color.accentColor) } dynamicIsland: { context in DynamicIsland { DynamicIslandExpandedRegion(.leading) { VStack(alignment: .leading) { Label("Distanz", systemImage: "ruler") .font(.caption2) .foregroundStyle(.secondary) Text(distanceLabel(context.state.distanceMeters)) .font(.title3.bold().monospacedDigit()) } } DynamicIslandExpandedRegion(.trailing) { VStack(alignment: .trailing) { Label("Dauer", systemImage: "clock") .font(.caption2) .foregroundStyle(.secondary) Text(durationLabel(context.state.elapsedSeconds)) .font(.title3.bold().monospacedDigit()) } } DynamicIslandExpandedRegion(.center) { Image(systemName: pawIcon(for: context.state)) .font(.title2) .foregroundStyle(Color.accentColor) } DynamicIslandExpandedRegion(.bottom) { if context.state.isPaused { statusPill("Pause", color: .orange) } else if context.state.isAutoPaused { statusPill("Auto-Pause", color: .gray) } else { Text("\(context.state.pointCount) Punkte aufgezeichnet") .font(.caption) .foregroundStyle(.secondary) } } } compactLeading: { Image(systemName: pawIcon(for: context.state)) .foregroundStyle(Color.accentColor) } compactTrailing: { Text(distanceLabel(context.state.distanceMeters)) .font(.caption2.monospacedDigit()) } minimal: { Image(systemName: pawIcon(for: context.state)) .foregroundStyle(Color.accentColor) } .widgetURL(URL(string: "banyarogo://walk")) .keylineTint(Color.accentColor) } } private func lockScreen(state: WalkActivityAttributes.WalkActivityState) -> some View { HStack(spacing: 16) { Image(systemName: pawIcon(for: state)) .font(.system(size: 36)) .foregroundStyle(Color.accentColor) .frame(width: 56, height: 56) .background(Color.accentColor.opacity(0.15), in: Circle()) VStack(alignment: .leading, spacing: 6) { HStack(spacing: 8) { Text("Ban Yaro Go") .font(.headline) if state.isPaused { statusPill("Pause", color: .orange) } else if state.isAutoPaused { statusPill("Auto-Pause", color: .gray) } else { statusPill("Live", color: .green) } } HStack(spacing: 16) { statColumn( value: distanceLabel(state.distanceMeters), label: "Distanz" ) Divider().frame(height: 28) statColumn( value: durationLabel(state.elapsedSeconds), label: "Dauer" ) Divider().frame(height: 28) statColumn( value: "\(state.pointCount)", label: "Punkte" ) } } Spacer(minLength: 0) } .padding() } private func statColumn(value: String, label: String) -> some View { VStack(alignment: .leading, spacing: 2) { Text(value) .font(.subheadline.bold().monospacedDigit()) Text(label) .font(.caption2) .foregroundStyle(.secondary) } } private func statusPill(_ text: String, color: Color) -> some View { Text(text) .font(.caption2.bold()) .foregroundStyle(.white) .padding(.horizontal, 8) .padding(.vertical, 2) .background(color, in: Capsule()) } private func pawIcon(for state: WalkActivityAttributes.WalkActivityState) -> String { if state.isPaused || state.isAutoPaused { return "pause.circle.fill" } return "pawprint.fill" } private func distanceLabel(_ meters: Double) -> String { if meters >= 1000 { return String(format: "%.2f km", meters / 1000) } return "\(Int(meters)) m" } private func durationLabel(_ seconds: Int) -> String { let h = seconds / 3600 let m = (seconds % 3600) / 60 let s = seconds % 60 if h > 0 { return String(format: "%d:%02d:%02d", h, m, s) } return String(format: "%d:%02d", m, s) } }