diff --git a/Sources/App/AppDelegate.swift b/Sources/App/AppDelegate.swift index f27aba01d..de91440a1 100644 --- a/Sources/App/AppDelegate.swift +++ b/Sources/App/AppDelegate.swift @@ -374,7 +374,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate { } private func setupLiveActivityReattachment() { - #if canImport(ActivityKit) + #if os(iOS) && !targetEnvironment(macCatalyst) if #available(iOS 17.2, *) { // Pre-warm the registry on the main thread before spawning background Tasks. // This avoids a lazy-init race if a push notification handler accesses it diff --git a/Sources/App/Resources/en.lproj/Localizable.strings b/Sources/App/Resources/en.lproj/Localizable.strings index 5b92bb348..621438d0a 100644 --- a/Sources/App/Resources/en.lproj/Localizable.strings +++ b/Sources/App/Resources/en.lproj/Localizable.strings @@ -513,8 +513,15 @@ This server requires a client certificate (mTLS) but the operation was cancelled "live_activity.status.enabled" = "Enabled"; "live_activity.status.not_supported" = "Not available on iPad"; "live_activity.status.open_settings" = "Open Settings"; +"live_activity.end_all.button" = "End All Activities"; +"live_activity.end_all.confirm.title" = "End all Live Activities?"; +"live_activity.end_all.confirm.button" = "End All"; +"live_activity.frequent_updates.footer" = "Allows Home Assistant to update Live Activities up to once per second. Enable in Settings › %@ › Live Activities."; +"live_activity.frequent_updates.title" = "Frequent Updates"; +"live_activity.privacy.message" = "Live Activity content is visible on your Lock Screen and Dynamic Island without Face ID or Touch ID. Choose what you display carefully."; "live_activity.subtitle" = "Real-time Home Assistant updates on your Lock Screen and Dynamic Island."; "live_activity.title" = "Live Activities"; + "location_change_notification.app_shortcut.body" = "Location updated via App Shortcut"; "location_change_notification.background_fetch.body" = "Current location delivery triggered via background fetch"; "location_change_notification.beacon_region_enter.body" = "%@ entered via iBeacon"; diff --git a/Sources/App/Settings/LiveActivity/LiveActivitySettingsView.swift b/Sources/App/Settings/LiveActivity/LiveActivitySettingsView.swift index 6a7015406..97be42038 100644 --- a/Sources/App/Settings/LiveActivity/LiveActivitySettingsView.swift +++ b/Sources/App/Settings/LiveActivity/LiveActivitySettingsView.swift @@ -1,3 +1,4 @@ +#if os(iOS) && !targetEnvironment(macCatalyst) import ActivityKit import Shared import SwiftUI @@ -685,3 +686,4 @@ private struct ActivitySnapshot: Identifiable { self.message = activity.content.state.message } } +#endif diff --git a/Sources/App/Settings/Settings/SettingsItem.swift b/Sources/App/Settings/Settings/SettingsItem.swift index bb352dd3d..16741478e 100644 --- a/Sources/App/Settings/Settings/SettingsItem.swift +++ b/Sources/App/Settings/Settings/SettingsItem.swift @@ -112,9 +112,13 @@ enum SettingsItem: String, Hashable, CaseIterable { case .notifications: SettingsNotificationsView() case .liveActivities: + #if os(iOS) && !targetEnvironment(macCatalyst) if #available(iOS 17.2, *) { LiveActivitySettingsView() } + #else + EmptyView() + #endif case .sensors: SensorListView() case .nfc: diff --git a/Sources/Extensions/Widgets/LiveActivity/HADynamicIslandView.swift b/Sources/Extensions/Widgets/LiveActivity/HADynamicIslandView.swift index 469bfecfb..fb8ab0ace 100644 --- a/Sources/Extensions/Widgets/LiveActivity/HADynamicIslandView.swift +++ b/Sources/Extensions/Widgets/LiveActivity/HADynamicIslandView.swift @@ -1,3 +1,4 @@ +#if os(iOS) && !targetEnvironment(macCatalyst) import ActivityKit import Shared import SwiftUI @@ -161,3 +162,4 @@ struct HAExpandedBottomView: View { return .haPrimary } } +#endif diff --git a/Sources/Extensions/Widgets/LiveActivity/HALiveActivityConfiguration.swift b/Sources/Extensions/Widgets/LiveActivity/HALiveActivityConfiguration.swift index fc75a5d2c..927fe03d4 100644 --- a/Sources/Extensions/Widgets/LiveActivity/HALiveActivityConfiguration.swift +++ b/Sources/Extensions/Widgets/LiveActivity/HALiveActivityConfiguration.swift @@ -1,3 +1,4 @@ +#if os(iOS) && !targetEnvironment(macCatalyst) import ActivityKit import Shared import SwiftUI @@ -21,3 +22,4 @@ struct HALiveActivityConfiguration: Widget { } } } +#endif diff --git a/Sources/Extensions/Widgets/LiveActivity/HALockScreenView.swift b/Sources/Extensions/Widgets/LiveActivity/HALockScreenView.swift index 72f3552c0..3867c004f 100644 --- a/Sources/Extensions/Widgets/LiveActivity/HALockScreenView.swift +++ b/Sources/Extensions/Widgets/LiveActivity/HALockScreenView.swift @@ -1,3 +1,4 @@ +#if os(iOS) && !targetEnvironment(macCatalyst) import ActivityKit import Shared import SwiftUI @@ -83,3 +84,4 @@ struct HALockScreenView: View { return .haPrimary } } +#endif diff --git a/Sources/Extensions/Widgets/Widgets.swift b/Sources/Extensions/Widgets/Widgets.swift index a7a332cb7..10253bd9d 100644 --- a/Sources/Extensions/Widgets/Widgets.swift +++ b/Sources/Extensions/Widgets/Widgets.swift @@ -21,9 +21,11 @@ struct WidgetsBundleLegacy: WidgetBundle { } var body: some Widget { + #if os(iOS) && !targetEnvironment(macCatalyst) if #available(iOSApplicationExtension 17.2, *) { HALiveActivityConfiguration() } + #endif WidgetAssist() LegacyWidgetActions() WidgetOpenPage() @@ -37,9 +39,11 @@ struct WidgetsBundle17: WidgetBundle { } var body: some Widget { + #if os(iOS) && !targetEnvironment(macCatalyst) if #available(iOSApplicationExtension 17.2, *) { HALiveActivityConfiguration() } + #endif WidgetCommonlyUsedEntities() WidgetCustom() WidgetAssist() @@ -60,7 +64,9 @@ struct WidgetsBundle18: WidgetBundle { } var body: some Widget { + #if os(iOS) && !targetEnvironment(macCatalyst) HALiveActivityConfiguration() + #endif // Controls ControlAssist() diff --git a/Sources/Shared/API/HAAPI.swift b/Sources/Shared/API/HAAPI.swift index da9dbf82b..f4c456b18 100644 --- a/Sources/Shared/API/HAAPI.swift +++ b/Sources/Shared/API/HAAPI.swift @@ -11,7 +11,7 @@ import Version #if os(iOS) import Reachability #endif -#if os(iOS) && canImport(ActivityKit) +#if os(iOS) && !targetEnvironment(macCatalyst) import ActivityKit #endif @@ -562,7 +562,7 @@ public class HomeAssistantAPI { "push_token": pushID, ] - #if os(iOS) && canImport(ActivityKit) + #if os(iOS) && !targetEnvironment(macCatalyst) if #available(iOS 17.2, *) { // Advertise Live Activity support so HA can gate the UI and send // activity push tokens back to the relay server. diff --git a/Sources/Shared/Environment/Environment.swift b/Sources/Shared/Environment/Environment.swift index 19fbeba19..4abc79852 100644 --- a/Sources/Shared/Environment/Environment.swift +++ b/Sources/Shared/Environment/Environment.swift @@ -141,7 +141,7 @@ public class AppEnvironment { } #if os(iOS) - #if canImport(ActivityKit) + #if !targetEnvironment(macCatalyst) /// Call `_ = Current.liveActivityRegistry` on the main thread at launch (before any /// background thread can access it) to avoid a lazy-init race between concurrent callers. public lazy var liveActivityRegistry: LiveActivityRegistryProtocol? = { diff --git a/Sources/Shared/LiveActivity/HALiveActivityAttributes.swift b/Sources/Shared/LiveActivity/HALiveActivityAttributes.swift index a82abfc5f..0339e5df8 100644 --- a/Sources/Shared/LiveActivity/HALiveActivityAttributes.swift +++ b/Sources/Shared/LiveActivity/HALiveActivityAttributes.swift @@ -1,4 +1,4 @@ -#if canImport(ActivityKit) +#if os(iOS) && !targetEnvironment(macCatalyst) import ActivityKit import SwiftUI diff --git a/Sources/Shared/LiveActivity/LiveActivityRegistry.swift b/Sources/Shared/LiveActivity/LiveActivityRegistry.swift index b4cf05f92..c0775fe81 100644 --- a/Sources/Shared/LiveActivity/LiveActivityRegistry.swift +++ b/Sources/Shared/LiveActivity/LiveActivityRegistry.swift @@ -1,4 +1,4 @@ -#if canImport(ActivityKit) +#if os(iOS) && !targetEnvironment(macCatalyst) import ActivityKit import Foundation diff --git a/Sources/Shared/Notifications/NotificationCommands/HandlerLiveActivity.swift b/Sources/Shared/Notifications/NotificationCommands/HandlerLiveActivity.swift index f511ba569..0d710150f 100644 --- a/Sources/Shared/Notifications/NotificationCommands/HandlerLiveActivity.swift +++ b/Sources/Shared/Notifications/NotificationCommands/HandlerLiveActivity.swift @@ -1,4 +1,4 @@ -#if canImport(ActivityKit) +#if os(iOS) && !targetEnvironment(macCatalyst) import ActivityKit import Foundation import PromiseKit diff --git a/Sources/Shared/Notifications/NotificationCommands/NotificationsCommandManager.swift b/Sources/Shared/Notifications/NotificationCommands/NotificationsCommandManager.swift index 98d89d90b..9760f49b2 100644 --- a/Sources/Shared/Notifications/NotificationCommands/NotificationsCommandManager.swift +++ b/Sources/Shared/Notifications/NotificationCommands/NotificationsCommandManager.swift @@ -22,7 +22,7 @@ public class NotificationCommandManager { register(command: "clear_notification", handler: HandlerClearNotification()) #if os(iOS) register(command: "update_complications", handler: HandlerUpdateComplications()) - #if canImport(ActivityKit) + #if !targetEnvironment(macCatalyst) if #available(iOS 17.2, *) { register(command: "live_activity", handler: HandlerStartOrUpdateLiveActivity()) register(command: "end_live_activity", handler: HandlerEndLiveActivity()) @@ -48,7 +48,7 @@ public class NotificationCommandManager { // Support data.live_update: true — the same field Android uses for Live Updates. // A single YAML automation can target both platforms with no platform-specific keys. - #if canImport(ActivityKit) + #if os(iOS) && !targetEnvironment(macCatalyst) if #available(iOS 17.2, *), hadict["live_update"] as? Bool == true, let handler = commands["live_activity"] { return handler.handle(hadict) @@ -111,8 +111,8 @@ private struct HandlerClearNotification: NotificationCommandHandler { // Also end any Live Activity whose tag matches — same YAML works on both iOS and Android. // Bridged into the returned Promise so the background fetch window stays open until // the activity is actually dismissed (prevents the OS suspending mid-dismiss). - // ActivityKit is unavailable in the PushProvider extension, so guard accordingly. - #if os(iOS) && canImport(ActivityKit) + // ActivityKit is unavailable in the PushProvider extension and Mac Catalyst, so guard accordingly. + #if os(iOS) && !targetEnvironment(macCatalyst) if #available(iOS 17.2, *), !Current.isAppExtension, let tag = payload["tag"] as? String { return Promise { seal in Task { diff --git a/Sources/Shared/Resources/Swiftgen/Strings.swift b/Sources/Shared/Resources/Swiftgen/Strings.swift index 462f2ede0..d4daea96e 100644 --- a/Sources/Shared/Resources/Swiftgen/Strings.swift +++ b/Sources/Shared/Resources/Swiftgen/Strings.swift @@ -1874,7 +1874,7 @@ public enum L10n { } } public enum FrequentUpdates { - /// Allows Home Assistant to update Live Activities up to once per second. Enable in Settings u203A %@ u203A Live Activities. + /// Allows Home Assistant to update Live Activities up to once per second. Enable in Settings › %@ › Live Activities. public static func footer(_ p1: Any) -> String { return L10n.tr("Localizable", "live_activity.frequent_updates.footer", String(describing: p1)) }