NuvioStreaming/ios/LiveActivity/LiveActivityWidget.swift
2026-01-24 13:41:36 +05:30

169 lines
5.3 KiB
Swift

import ActivityKit
import SwiftUI
import WidgetKit
struct LiveActivityAttributes: ActivityAttributes {
struct ContentState: Codable, Hashable {
var title: String
var subtitle: String?
var timerEndDateInMilliseconds: Double?
var progress: Double?
var imageName: String?
var dynamicIslandImageName: String?
}
var name: String
var backgroundColor: String?
var titleColor: String?
var subtitleColor: String?
var progressViewTint: String?
var progressViewLabelColor: String?
var deepLinkUrl: String?
var timerType: DynamicIslandTimerType?
var padding: Int?
var paddingDetails: PaddingDetails?
var imagePosition: String?
var imageWidth: Int?
var imageHeight: Int?
var imageWidthPercent: Double?
var imageHeightPercent: Double?
var imageAlign: String?
var contentFit: String?
enum DynamicIslandTimerType: String, Codable {
case circular
case digital
}
struct PaddingDetails: Codable, Hashable {
var top: Int?
var bottom: Int?
var left: Int?
var right: Int?
var vertical: Int?
var horizontal: Int?
}
}
struct LiveActivityWidget: Widget {
var body: some WidgetConfiguration {
ActivityConfiguration(for: LiveActivityAttributes.self) { context in
LiveActivityView(contentState: context.state, attributes: context.attributes)
.activityBackgroundTint(
context.attributes.backgroundColor.map { Color(hex: $0) }
)
.activitySystemActionForegroundColor(Color.black)
.applyWidgetURL(from: context.attributes.deepLinkUrl)
} dynamicIsland: { context in
DynamicIsland {
DynamicIslandExpandedRegion(.leading, priority: 1) {
dynamicIslandExpandedLeading(title: context.state.title, subtitle: context.state.subtitle)
.dynamicIsland(verticalPlacement: .belowIfTooWide)
.padding(.leading, 5)
.applyWidgetURL(from: context.attributes.deepLinkUrl)
}
DynamicIslandExpandedRegion(.trailing) {
if let imageName = context.state.imageName {
dynamicIslandExpandedTrailing(imageName: imageName)
.padding(.trailing, 5)
.applyWidgetURL(from: context.attributes.deepLinkUrl)
}
}
DynamicIslandExpandedRegion(.bottom) {
if let date = context.state.timerEndDateInMilliseconds {
dynamicIslandExpandedBottom(
endDate: date, progressViewTint: context.attributes.progressViewTint
)
.padding(.horizontal, 5)
.applyWidgetURL(from: context.attributes.deepLinkUrl)
}
}
} compactLeading: {
if let dynamicIslandImageName = context.state.dynamicIslandImageName {
resizableImage(imageName: dynamicIslandImageName)
.frame(maxWidth: 23, maxHeight: 23)
.applyWidgetURL(from: context.attributes.deepLinkUrl)
}
} compactTrailing: {
if let date = context.state.timerEndDateInMilliseconds {
compactTimer(
endDate: date,
timerType: context.attributes.timerType ?? .circular,
progressViewTint: context.attributes.progressViewTint
).applyWidgetURL(from: context.attributes.deepLinkUrl)
}
} minimal: {
if let date = context.state.timerEndDateInMilliseconds {
compactTimer(
endDate: date,
timerType: context.attributes.timerType ?? .circular,
progressViewTint: context.attributes.progressViewTint
).applyWidgetURL(from: context.attributes.deepLinkUrl)
}
}
}
}
@ViewBuilder
private func compactTimer(
endDate: Double,
timerType: LiveActivityAttributes.DynamicIslandTimerType,
progressViewTint: String?
) -> some View {
if timerType == .digital {
Text(timerInterval: Date.toTimerInterval(miliseconds: endDate))
.font(.system(size: 15))
.minimumScaleFactor(0.8)
.fontWeight(.semibold)
.frame(maxWidth: 60)
.multilineTextAlignment(.trailing)
} else {
circularTimer(endDate: endDate)
.tint(progressViewTint.map { Color(hex: $0) })
}
}
private func dynamicIslandExpandedLeading(title: String, subtitle: String?) -> some View {
VStack(alignment: .leading) {
Spacer()
Text(title)
.font(.title2)
.foregroundStyle(.white)
.fontWeight(.semibold)
if let subtitle {
Text(subtitle)
.font(.title3)
.minimumScaleFactor(0.8)
.foregroundStyle(.white.opacity(0.75))
}
Spacer()
}
}
private func dynamicIslandExpandedTrailing(imageName: String) -> some View {
VStack {
Spacer()
resizableImage(imageName: imageName)
Spacer()
}
}
private func dynamicIslandExpandedBottom(endDate: Double, progressViewTint: String?) -> some View {
ProgressView(timerInterval: Date.toTimerInterval(miliseconds: endDate))
.foregroundStyle(.white)
.tint(progressViewTint.map { Color(hex: $0) })
.padding(.top, 5)
}
private func circularTimer(endDate: Double) -> some View {
ProgressView(
timerInterval: Date.toTimerInterval(miliseconds: endDate),
countsDown: false,
label: { EmptyView() },
currentValueLabel: {
EmptyView()
}
)
.progressViewStyle(.circular)
}
}