mirror of
https://github.com/home-assistant/iOS.git
synced 2026-05-05 04:44:57 -05:00
Fix video notification for watchOS (#3846)
<!-- Thank you for submitting a Pull Request and helping to improve Home Assistant. Please complete the following sections to help the processing and review of your changes. Please do not delete anything from this template. --> ## Summary <!-- Provide a brief summary of the changes you have made and most importantly what they aim to achieve --> ## Screenshots <!-- If this is a user-facing change not in the frontend, please include screenshots in light and dark mode. --> ## Link to pull request in Documentation repository <!-- Pull requests that add, change or remove functionality must have a corresponding pull request in the Companion App Documentation repository (https://github.com/home-assistant/companion.home-assistant). Please add the number of this pull request after the "#" --> Documentation: home-assistant/companion.home-assistant# ## Any other notes <!-- If there is any other information of note, like if this Pull Request is part of a bigger change, please include it here. -->
This commit is contained in:
committed by
GitHub
parent
e01318be23
commit
6e6722317a
@@ -6,6 +6,10 @@ import WatchKit
|
||||
final class DynamicNotificationHostingController: WKUserNotificationHostingController<DynamicNotificationView> {
|
||||
private let viewModel = DynamicNotificationViewModel()
|
||||
|
||||
override class var sashColor: Color? { .haPrimary }
|
||||
override class var wantsSashBlur: Bool { true }
|
||||
override class var isInteractive: Bool { true }
|
||||
|
||||
override var body: DynamicNotificationView {
|
||||
DynamicNotificationView(viewModel: viewModel)
|
||||
}
|
||||
|
||||
@@ -9,12 +9,14 @@ import UserNotifications
|
||||
struct DynamicNotificationView: View {
|
||||
@ObservedObject var viewModel: DynamicNotificationViewModel
|
||||
|
||||
@State private var player = AVPlayer()
|
||||
private let dynamicContentHeight: CGFloat = 150
|
||||
|
||||
var body: some View {
|
||||
VStack(alignment: .leading, spacing: DesignSystem.Spaces.two) {
|
||||
textContent
|
||||
// Dynamic content is presented first to align with watchOS default notification style
|
||||
dynamicContentView
|
||||
textContent
|
||||
loader
|
||||
errorView
|
||||
}
|
||||
@@ -55,19 +57,18 @@ struct DynamicNotificationView: View {
|
||||
}
|
||||
|
||||
private var textContent: some View {
|
||||
VStack(alignment: .leading, spacing: DesignSystem.Spaces.half) {
|
||||
VStack(alignment: .leading, spacing: .zero) {
|
||||
if !viewModel.title.isEmpty {
|
||||
Text(viewModel.title)
|
||||
.font(.headline)
|
||||
}
|
||||
if !viewModel.subtitle.isEmpty {
|
||||
Text(viewModel.subtitle)
|
||||
.font(.subheadline)
|
||||
.foregroundStyle(.secondary)
|
||||
.font(.headline)
|
||||
}
|
||||
if !viewModel.body.isEmpty {
|
||||
Text(viewModel.body)
|
||||
.font(.footnote)
|
||||
.font(.body)
|
||||
}
|
||||
}
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
@@ -81,14 +82,24 @@ struct DynamicNotificationView: View {
|
||||
.clipShape(RoundedRectangle(cornerRadius: 6))
|
||||
}
|
||||
|
||||
@ViewBuilder
|
||||
private func videoPlayer(url: URL) -> some View {
|
||||
VideoPlayer(player: AVPlayer(url: url))
|
||||
VideoPlayer(player: player)
|
||||
.frame(maxWidth: .infinity)
|
||||
.frame(height: dynamicContentHeight)
|
||||
.clipShape(RoundedRectangle(cornerRadius: DesignSystem.CornerRadius.one))
|
||||
.onAppear {
|
||||
// Autoplay to mimic WKInterfaceInlineMovie.play()
|
||||
AVPlayer(url: url).play()
|
||||
// Build a new item each time we appear and autoplay shortly after.
|
||||
let item = AVPlayerItem(url: url)
|
||||
player.replaceCurrentItem(with: item)
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
|
||||
player.play()
|
||||
}
|
||||
}
|
||||
.onDisappear {
|
||||
// Release the item so file handles are closed; the view model owns security-scope.
|
||||
player.pause()
|
||||
player.replaceCurrentItem(with: nil)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -135,34 +135,29 @@ final class DynamicNotificationViewModel: ObservableObject {
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Media handling
|
||||
|
||||
private func handleMediaURL(_ url: URL) {
|
||||
let didStart = url.startAccessingSecurityScopedResource()
|
||||
if didStart {
|
||||
securityScopedURL = url
|
||||
}
|
||||
|
||||
// Attempt to decode as image; otherwise treat as video
|
||||
do {
|
||||
let data = try Data(contentsOf: url, options: .alwaysMapped)
|
||||
if let img = UIImage(data: data) {
|
||||
DispatchQueue.main.async {
|
||||
self.content = .image(img)
|
||||
self.isLoading = false
|
||||
}
|
||||
} else {
|
||||
DispatchQueue.main.async {
|
||||
self.content = .video(url)
|
||||
self.isLoading = false
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
if let img = UIImage(contentsOfFile: url.path) {
|
||||
DispatchQueue.main.async {
|
||||
self.errorMessage = error.localizedDescription
|
||||
self.content = .image(img)
|
||||
self.isLoading = false
|
||||
}
|
||||
} else {
|
||||
DispatchQueue.main.async {
|
||||
self.content = .video(url)
|
||||
self.isLoading = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Map functions
|
||||
|
||||
private static func parseDegrees(_ any: Any?) -> CLLocationDegrees? {
|
||||
if let d = any as? Double { return d }
|
||||
if let n = any as? NSNumber { return n.doubleValue }
|
||||
|
||||
Reference in New Issue
Block a user