From dfe2e6c8fd6f676552f3877897b4d6c28fa92fe5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bruno=20Pantale=C3=A3o?= <5808343+bgoncal@users.noreply.github.com> Date: Thu, 23 Apr 2026 15:16:24 +0200 Subject: [PATCH 01/15] Move live activity settings back to root settings and add samples --- Sources/App/Settings/DebugView.swift | 15 -- .../LiveActivitySettingsView.swift | 244 +++++++++--------- .../App/Settings/Settings/SettingsItem.swift | 24 +- .../App/Settings/Settings/SettingsView.swift | 2 +- 4 files changed, 139 insertions(+), 146 deletions(-) diff --git a/Sources/App/Settings/DebugView.swift b/Sources/App/Settings/DebugView.swift index b0f8df1cd9..f20b80e1fa 100644 --- a/Sources/App/Settings/DebugView.swift +++ b/Sources/App/Settings/DebugView.swift @@ -145,21 +145,6 @@ struct DebugView: View { #endif } - #if os(iOS) && !targetEnvironment(macCatalyst) - if #available(iOS 17.2, *) { - Section { - NavigationLink { - LiveActivitySettingsView() - } label: { - linkContent( - image: .init(systemSymbol: .livephoto), - title: L10n.LiveActivity.title - ) - } - } - } - #endif - criticalSection if tapsOnCasitaLogo < 10 { diff --git a/Sources/App/Settings/LiveActivity/LiveActivitySettingsView.swift b/Sources/App/Settings/LiveActivity/LiveActivitySettingsView.swift index 97be420385..7c564fa7c6 100644 --- a/Sources/App/Settings/LiveActivity/LiveActivitySettingsView.swift +++ b/Sources/App/Settings/LiveActivity/LiveActivitySettingsView.swift @@ -27,6 +27,7 @@ struct LiveActivitySettingsView: View { ) statusSection + frequentUpdatesSection if activities.isEmpty { Section(L10n.LiveActivity.Section.active) { @@ -62,15 +63,8 @@ struct LiveActivitySettingsView: View { } } - #if DEBUG - debugSection - #endif - privacySection - - if #available(iOS 17.2, *) { - frequentUpdatesSection - } + samplesSection } .navigationTitle(L10n.LiveActivity.title) .task { await loadActivities() } @@ -101,7 +95,7 @@ struct LiveActivitySettingsView: View { } } - // MARK: - Debug (DEBUG builds only) + // MARK: - Samples // // Two sections: Static (fixed snapshots to verify layout) and Animated (multi-stage @@ -121,131 +115,139 @@ struct LiveActivitySettingsView: View { // It does NOT appear on the lock screen. Use a Dynamic Island device or // simulator (iPhone 14 Pro+) to see it. - #if DEBUG - private var debugSection: some View { - Group { - Section { - // Minimum viable layout — only the message field is set. - // Verifies the bare layout renders without icon, progress, or timer. - Button("Plain Message") { - startTestActivity( - tag: "debug-plain", - title: "Home Assistant", - state: .init(message: "Everything looks good at home.") - ) + private var samplesSection: some View { + Section { + NavigationLink("Samples") { + List { + staticSamplesSection + animatedSamplesSection } + .navigationTitle("Samples") + } + } + } - // icon = nil code path. Layout must not shift or break when no icon is provided. - // color = nil so the progress bar uses the default HA-blue tint. - // criticalText ("Active") visible in DI compact trailing only. - Button("No Icon · Default Color") { - startTestActivity( - tag: "debug-no-icon", - title: "Script Running", - state: .init( - message: "Irrigation zone 3 is active", - criticalText: "Active", - progress: 35, - progressMax: 100 - ) - ) - } + private var staticSamplesSection: some View { + Section { + // Minimum viable layout — only the message field is set. + // Verifies the bare layout renders without icon, progress, or timer. + Button("Plain Message") { + startTestActivity( + tag: "debug-plain", + title: "Home Assistant", + state: .init(message: "Everything looks good at home.") + ) + } - // Short 60-second countdown with no progress bar. - // Red color communicates urgency. Watch the timer count down in real time. - // Represents automations like alarm arming delays or reminder countdowns. - Button("Alarm · 60 sec Countdown") { - startTestActivity( - tag: "debug-alarm", - title: "Security Alarm", - state: .init( - message: "Motion at back door · Arms in 60 seconds", - criticalText: "60 sec", - chronometer: true, - countdownEnd: Date().addingTimeInterval(60), - icon: "mdi:alarm-light", - color: "#F44336" - ) + // icon = nil code path. Layout must not shift or break when no icon is provided. + // color = nil so the progress bar uses the default HA-blue tint. + // criticalText ("Active") visible in DI compact trailing only. + Button("No Icon · Default Color") { + startTestActivity( + tag: "debug-no-icon", + title: "Script Running", + state: .init( + message: "Irrigation zone 3 is active", + criticalText: "Active", + progress: 35, + progressMax: 100 ) - } + ) + } - // Every ContentState field active at the same time. - // Lock screen shows: icon → live countdown → progress bar. - // criticalText ("5 min") visible in DI compact trailing only. - // Use this to confirm no layout collisions when all fields are populated. - Button("All Fields · Max Load") { - startTestActivity( - tag: "debug-all", - title: "All Fields", - state: .init( - message: "All content state fields active", - criticalText: "5 min", - progress: 42, - progressMax: 100, - chronometer: true, - countdownEnd: Date().addingTimeInterval(5 * 60), - icon: "mdi:home-assistant", - color: "#03A9F4" - ) + // Short 60-second countdown with no progress bar. + // Red color communicates urgency. Watch the timer count down in real time. + // Represents automations like alarm arming delays or reminder countdowns. + Button("Alarm · 60 sec Countdown") { + startTestActivity( + tag: "debug-alarm", + title: "Security Alarm", + state: .init( + message: "Motion at back door · Arms in 60 seconds", + criticalText: "60 sec", + chronometer: true, + countdownEnd: Date().addingTimeInterval(60), + icon: "mdi:alarm-light", + color: "#F44336" ) - } - } header: { - Text("Debug · Static") - } footer: { - Text("Fixed state — no updates after start. Good for checking layout at a glance.") + ) } - Section { - // Progress bar advances through five named stages. - // criticalText tracks the current stage name in the DI compact trailing slot. - // Icon swaps from washing-machine to check-circle on the final update. - // Represents any multi-step appliance cycle automation. - Button("Washing Machine · Stage Labels (~12 s)") { startWashingMachineCycle() } - - // Numeric percentage in criticalText updates alongside the progress bar. - // Color shifts from green to yellow-green as the charge nears 100 %. - // Represents any "% complete with time remaining" automation pattern. - Button("EV Charging · Numeric % (~16 s)") { startEVChargingSimulation() } - - // The only scenario where both progress (playback position) and a live countdown - // (time remaining in track) are active and updating at the same time. - // Simulates a track change mid-sequence: progress resets, countdown resets. - Button("Media Player · Progress + Timer (~20 s)") { startMediaNowPlaying() } - - // Message, criticalText, and icon all change on every update — no progress bar. - // Represents automations where the status category itself changes (not just a value). - Button("Package Delivery · All Text Fields (~15 s)") { startPackageJourney() } - - // No progress bar — state communicated entirely through color and icon. - // Escalates orange (motion) → red (person) → green (all clear). - // Represents any alert-and-resolve automation pattern. - Button("Security Escalation · Color + Icon (~8 s)") { startSecuritySequence() } - - // Cycles through wash stages then calls activity.end() with .default dismissal. - // The only scenario that tests the full lifecycle: start → update → end. - // After ending, the final "Done" state lingers on the lock screen (up to 4 h). - Button("Dishwasher · Full Lifecycle, Ends Itself (~12 s)") { startDishwasherAutoComplete() } - - // Fires 6 updates 2 seconds apart (12 s total). - // On iOS 18 the system enforces ~15 s between rendered updates — some will be - // silently dropped. Watch the counter skip values to see the rate limit in action. - // On the simulator and iOS 17 all 6 updates should render. - Button("Rate Limit · 6 Rapid Updates, 2 s Apart (~12 s)") { startRapidUpdateStressTest() } - } header: { - Text("Debug · Animated") - } footer: { - Text( - "Activity updates itself after you tap. Tap, then immediately lock (⌘L) " + - "to watch updates on the lock screen in real time." + // Every ContentState field active at the same time. + // Lock screen shows: icon → live countdown → progress bar. + // criticalText ("5 min") visible in DI compact trailing only. + // Use this to confirm no layout collisions when all fields are populated. + Button("All Fields · Max Load") { + startTestActivity( + tag: "debug-all", + title: "All Fields", + state: .init( + message: "All content state fields active", + criticalText: "5 min", + progress: 42, + progressMax: 100, + chronometer: true, + countdownEnd: Date().addingTimeInterval(5 * 60), + icon: "mdi:home-assistant", + color: "#03A9F4" + ) ) } + } header: { + Text("Sample · Static") + } footer: { + Text("Fixed state — no updates after start. Good for checking layout at a glance.") } } - #endif - #if DEBUG + private var animatedSamplesSection: some View { + Section { + // Progress bar advances through five named stages. + // criticalText tracks the current stage name in the DI compact trailing slot. + // Icon swaps from washing-machine to check-circle on the final update. + // Represents any multi-step appliance cycle automation. + Button("Washing Machine · Stage Labels (~12 s)") { startWashingMachineCycle() } + + // Numeric percentage in criticalText updates alongside the progress bar. + // Color shifts from green to yellow-green as the charge nears 100 %. + // Represents any "% complete with time remaining" automation pattern. + Button("EV Charging · Numeric % (~16 s)") { startEVChargingSimulation() } + + // The only scenario where both progress (playback position) and a live countdown + // (time remaining in track) are active and updating at the same time. + // Simulates a track change mid-sequence: progress resets, countdown resets. + Button("Media Player · Progress + Timer (~20 s)") { startMediaNowPlaying() } + + // Message, criticalText, and icon all change on every update — no progress bar. + // Represents automations where the status category itself changes (not just a value). + Button("Package Delivery · All Text Fields (~15 s)") { startPackageJourney() } + + // No progress bar — state communicated entirely through color and icon. + // Escalates orange (motion) → red (person) → green (all clear). + // Represents any alert-and-resolve automation pattern. + Button("Security Escalation · Color + Icon (~8 s)") { startSecuritySequence() } + + // Cycles through wash stages then calls activity.end() with .default dismissal. + // The only scenario that tests the full lifecycle: start → update → end. + // After ending, the final "Done" state lingers on the lock screen (up to 4 h). + Button("Dishwasher · Full Lifecycle, Ends Itself (~12 s)") { startDishwasherAutoComplete() } + + // Fires 6 updates 2 seconds apart (12 s total). + // On iOS 18 the system enforces ~15 s between rendered updates — some will be + // silently dropped. Watch the counter skip values to see the rate limit in action. + // On the simulator and iOS 17 all 6 updates should render. + Button("Rate Limit · 6 Rapid Updates, 2 s Apart (~12 s)") { startRapidUpdateStressTest() } + } header: { + Text("Sample · Animated") + } footer: { + Text( + "Activity updates itself after you tap. Tap, then immediately lock (⌘L) " + + "to watch updates on the lock screen in real time." + ) + } + } - // MARK: - Debug helpers + // MARK: - Sample helpers /// Starts a single-state activity (no subsequent updates). private func startTestActivity(tag: String, title: String, state: HALiveActivityAttributes.ContentState) { @@ -565,8 +567,6 @@ struct LiveActivitySettingsView: View { ) } - #endif - private var privacySection: some View { Section { Label(L10n.LiveActivity.Privacy.message, systemSymbol: .lockShield) diff --git a/Sources/App/Settings/Settings/SettingsItem.swift b/Sources/App/Settings/Settings/SettingsItem.swift index 4e183efab4..34eb5180bb 100644 --- a/Sources/App/Settings/Settings/SettingsItem.swift +++ b/Sources/App/Settings/Settings/SettingsItem.swift @@ -149,22 +149,30 @@ enum SettingsItem: String, Hashable, CaseIterable { allCases.filter { item in // Filter based on platform #if targetEnvironment(macCatalyst) - if item == .servers || item == .gestures || item == .kiosk || item == .watch || item == .carPlay || - item == .complications || item == .nfc || item == .help || - item == .whatsNew { + let hiddenItems: [SettingsItem] = [ + .servers, + .gestures, + .kiosk, + .watch, + .carPlay, + .complications, + .nfc, + .help, + .whatsNew, + .liveActivities, + ] + + if hiddenItems.contains(item) { return false } #endif - // Live Activities are shown in DebugView - if item == .liveActivities { - return false - } + return true } } static var generalItems: [SettingsItem] { - [.general, .gestures, .location, .notifications, .kiosk] + [.general, .gestures, .location, .notifications, .kiosk, .liveActivities] } static var integrationItems: [SettingsItem] { diff --git a/Sources/App/Settings/Settings/SettingsView.swift b/Sources/App/Settings/Settings/SettingsView.swift index 3dc3fab58d..35b4b61f7e 100644 --- a/Sources/App/Settings/Settings/SettingsView.swift +++ b/Sources/App/Settings/Settings/SettingsView.swift @@ -246,7 +246,7 @@ struct SettingsView: View { Label { HStack(spacing: DesignSystem.Spaces.one) { Text(item.title) - if item == .kiosk { + if [.kiosk, .liveActivities].contains(item) { LabsLabel() } } From f796192d7fc747a2c50b2580121d287a9047f6d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bruno=20Pantale=C3=A3o?= <5808343+bgoncal@users.noreply.github.com> Date: Thu, 23 Apr 2026 15:35:22 +0200 Subject: [PATCH 02/15] Reduce unnecessary info --- .../Resources/bg.lproj/Localizable.strings | 3 - .../Resources/ca-ES.lproj/Localizable.strings | 3 - .../Resources/cs.lproj/Localizable.strings | 3 - .../Resources/cy-GB.lproj/Localizable.strings | 3 - .../Resources/da.lproj/Localizable.strings | 3 - .../Resources/de.lproj/Localizable.strings | 3 - .../Resources/el.lproj/Localizable.strings | 3 - .../Resources/en-GB.lproj/Localizable.strings | 3 - .../Resources/en.lproj/Localizable.strings | 3 - .../Resources/es-ES.lproj/Localizable.strings | 3 - .../Resources/es-MX.lproj/Localizable.strings | 3 - .../Resources/es.lproj/Localizable.strings | 3 - .../Resources/et.lproj/Localizable.strings | 3 - .../Resources/fi.lproj/Localizable.strings | 3 - .../Resources/fr.lproj/Localizable.strings | 3 - .../Resources/he.lproj/Localizable.strings | 3 - .../Resources/hu.lproj/Localizable.strings | 3 - .../Resources/id.lproj/Localizable.strings | 3 - .../Resources/it.lproj/Localizable.strings | 3 - .../Resources/ja.lproj/Localizable.strings | 3 - .../Resources/ko-KR.lproj/Localizable.strings | 3 - .../Resources/ml.lproj/Localizable.strings | 3 - .../Resources/nb.lproj/Localizable.strings | 3 - .../Resources/nl.lproj/Localizable.strings | 3 - .../Resources/pl-PL.lproj/Localizable.strings | 3 - .../Resources/pt-BR.lproj/Localizable.strings | 3 - .../Resources/ru.lproj/Localizable.strings | 3 - .../Resources/sl.lproj/Localizable.strings | 3 - .../Resources/sv.lproj/Localizable.strings | 3 - .../Resources/tr.lproj/Localizable.strings | 3 - .../Resources/uk.lproj/Localizable.strings | 3 - .../Resources/vi.lproj/Localizable.strings | 3 - .../zh-Hans.lproj/Localizable.strings | 3 - .../zh-Hant.lproj/Localizable.strings | 3 - .../LiveActivitySettingsView.swift | 62 ++++++------------- .../Shared/Resources/Swiftgen/Strings.swift | 10 --- 36 files changed, 20 insertions(+), 154 deletions(-) diff --git a/Sources/App/Resources/bg.lproj/Localizable.strings b/Sources/App/Resources/bg.lproj/Localizable.strings index 5e3f53edd8..f2c5f18994 100644 --- a/Sources/App/Resources/bg.lproj/Localizable.strings +++ b/Sources/App/Resources/bg.lproj/Localizable.strings @@ -507,11 +507,8 @@ This server requires a client certificate (mTLS) but the operation was cancelled "live_activity.end_all.button" = "End All Activities"; "live_activity.end_all.confirm.button" = "End All"; "live_activity.end_all.confirm.title" = "End all Live Activities?"; -"live_activity.frequent_updates.footer" = "Allows Home Assistant to update Live Activities up to once per second. Enable in Settings \u203A %@ \u203A 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.section.active" = "Active Activities"; -"live_activity.section.privacy" = "Privacy"; "live_activity.section.status" = "Status"; "live_activity.status.enabled" = "Enabled"; "live_activity.status.not_supported" = "Not available on iPad"; diff --git a/Sources/App/Resources/ca-ES.lproj/Localizable.strings b/Sources/App/Resources/ca-ES.lproj/Localizable.strings index 4a057d519c..b2ecf48551 100644 --- a/Sources/App/Resources/ca-ES.lproj/Localizable.strings +++ b/Sources/App/Resources/ca-ES.lproj/Localizable.strings @@ -507,11 +507,8 @@ This server requires a client certificate (mTLS) but the operation was cancelled "live_activity.end_all.button" = "End All Activities"; "live_activity.end_all.confirm.button" = "End All"; "live_activity.end_all.confirm.title" = "End all Live Activities?"; -"live_activity.frequent_updates.footer" = "Allows Home Assistant to update Live Activities up to once per second. Enable in Settings \u203A %@ \u203A 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.section.active" = "Active Activities"; -"live_activity.section.privacy" = "Privacy"; "live_activity.section.status" = "Status"; "live_activity.status.enabled" = "Enabled"; "live_activity.status.not_supported" = "Not available on iPad"; diff --git a/Sources/App/Resources/cs.lproj/Localizable.strings b/Sources/App/Resources/cs.lproj/Localizable.strings index 79f18e2bc3..d49582249a 100644 --- a/Sources/App/Resources/cs.lproj/Localizable.strings +++ b/Sources/App/Resources/cs.lproj/Localizable.strings @@ -507,11 +507,8 @@ Tento server vyžaduje klientský certifikát (mTLS), ale operace byla zrušena. "live_activity.end_all.button" = "Ukončit všechny aktivity"; "live_activity.end_all.confirm.button" = "Ukončit vše"; "live_activity.end_all.confirm.title" = "Ukončit všechny živé aktivity?"; -"live_activity.frequent_updates.footer" = "Umožňuje Home Assistantovi aktualizovat živé aktivity až jednou za sekundu. Povolte v Nastavení. \u203A %@ \u203A Živé aktivity."; "live_activity.frequent_updates.title" = "Časté aktualizace"; -"live_activity.privacy.message" = "Obsah živých aktivit je viditelný na zamykací obrazovce a v Dynamic Islandu bez použití Face ID nebo Touch ID. Pečlivě si vyberte, co chcete zobrazit."; "live_activity.section.active" = "Aktivní aktivity"; -"live_activity.section.privacy" = "Soukromí"; "live_activity.section.status" = "Stav"; "live_activity.status.enabled" = "Povoleno"; "live_activity.status.not_supported" = "Není dostupné na iPadu"; diff --git a/Sources/App/Resources/cy-GB.lproj/Localizable.strings b/Sources/App/Resources/cy-GB.lproj/Localizable.strings index 6305bbb2c3..6a4d0a164f 100644 --- a/Sources/App/Resources/cy-GB.lproj/Localizable.strings +++ b/Sources/App/Resources/cy-GB.lproj/Localizable.strings @@ -507,11 +507,8 @@ This server requires a client certificate (mTLS) but the operation was cancelled "live_activity.end_all.button" = "End All Activities"; "live_activity.end_all.confirm.button" = "End All"; "live_activity.end_all.confirm.title" = "End all Live Activities?"; -"live_activity.frequent_updates.footer" = "Allows Home Assistant to update Live Activities up to once per second. Enable in Settings \u203A %@ \u203A 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.section.active" = "Active Activities"; -"live_activity.section.privacy" = "Privacy"; "live_activity.section.status" = "Status"; "live_activity.status.enabled" = "Enabled"; "live_activity.status.not_supported" = "Not available on iPad"; diff --git a/Sources/App/Resources/da.lproj/Localizable.strings b/Sources/App/Resources/da.lproj/Localizable.strings index e30d9958f7..f7fe9b080c 100644 --- a/Sources/App/Resources/da.lproj/Localizable.strings +++ b/Sources/App/Resources/da.lproj/Localizable.strings @@ -507,11 +507,8 @@ This server requires a client certificate (mTLS) but the operation was cancelled "live_activity.end_all.button" = "Afslut alle aktiviteter"; "live_activity.end_all.confirm.button" = "Afslut alle"; "live_activity.end_all.confirm.title" = "Afslut alle liveaktiviteter?"; -"live_activity.frequent_updates.footer" = "Tillader Home Assistant at opdatere Liveaktiviteter op til én gang i sekundet. Aktivér i Indstillinger \u203A %@ \u203A Liveaktiviteter."; "live_activity.frequent_updates.title" = "Hyppige opdateringer"; -"live_activity.privacy.message" = "Liveaktivitet-indhold er synligt på din låseskærm og Dynamic Island uden Face ID eller Touch ID. Vælg omhyggeligt, hvad du vil vise."; "live_activity.section.active" = "Aktive aktiviteter"; -"live_activity.section.privacy" = "Privatliv"; "live_activity.section.status" = "Status"; "live_activity.status.enabled" = "Aktiveret"; "live_activity.status.not_supported" = "Ikke tilgængelig på iPad"; diff --git a/Sources/App/Resources/de.lproj/Localizable.strings b/Sources/App/Resources/de.lproj/Localizable.strings index 147b5c2f60..02556d2d55 100644 --- a/Sources/App/Resources/de.lproj/Localizable.strings +++ b/Sources/App/Resources/de.lproj/Localizable.strings @@ -507,11 +507,8 @@ Dieser Server benötigt ein Clientzertifikat (mTLS), aber der Vorgang wurde abge "live_activity.end_all.button" = "Alle Aktivitäten beenden"; "live_activity.end_all.confirm.button" = "Alle beenden"; "live_activity.end_all.confirm.title" = "Alle Live-Aktivitäten beenden?"; -"live_activity.frequent_updates.footer" = "Ermöglicht es Home Assistant, Live-Aktivitäten bis zu einmal pro Sekunde zu aktualisieren. In Einstellungen \u203A %@ \u203A Live-Aktivitäten aktiveren."; "live_activity.frequent_updates.title" = "Häufige Aktualisierungen"; -"live_activity.privacy.message" = "Inhalte für Live-Aktivität sind auf deinem Sperrbildschirm und Dynamic Island auch ohne Face ID oder Touch ID sichtbar. Wähle die angezeigten Inhalte sorgfältig aus."; "live_activity.section.active" = "Aktive Aktivitäten"; -"live_activity.section.privacy" = "Datenschutz"; "live_activity.section.status" = "Status"; "live_activity.status.enabled" = "Aktiviert"; "live_activity.status.not_supported" = "Auf dem iPad nicht verfügbar"; diff --git a/Sources/App/Resources/el.lproj/Localizable.strings b/Sources/App/Resources/el.lproj/Localizable.strings index 86bf2efbb5..773f485239 100644 --- a/Sources/App/Resources/el.lproj/Localizable.strings +++ b/Sources/App/Resources/el.lproj/Localizable.strings @@ -507,11 +507,8 @@ "live_activity.end_all.button" = "Τερματισμός όλων των δραστηριοτήτων"; "live_activity.end_all.confirm.button" = "Τερματισμός όλων"; "live_activity.end_all.confirm.title" = "Τερματισμός όλων των ζωντανών δραστηριοτήτων;"; -"live_activity.frequent_updates.footer" = "Επιτρέπει στο Home Assistant να ενημερώνει τις Ζωντανές Δραστηριότητες έως και μία φορά ανά δευτερόλεπτο. Ενεργοποίηση στις Ρυθμίσεις \u203A %@ \u203A Ζωντανές Δραστηριότητες."; "live_activity.frequent_updates.title" = "Συχνές ενημερώσεις"; -"live_activity.privacy.message" = "Το περιεχόμενο της Ζωντανής Δραστηριότητας είναι ορατό στην Οθόνη Κλειδώματος και στο Dynamic Island χωρίς Face ID ή Touch ID. Επιλέξτε προσεκτικά τι θα εμφανίσετε."; "live_activity.section.active" = "Ενεργές δραστηριότητες"; -"live_activity.section.privacy" = "Απόρρητο"; "live_activity.section.status" = "Κατάσταση"; "live_activity.status.enabled" = "Ενεργό"; "live_activity.status.not_supported" = "Δεν είναι διαθέσιμο σε iPad"; diff --git a/Sources/App/Resources/en-GB.lproj/Localizable.strings b/Sources/App/Resources/en-GB.lproj/Localizable.strings index d6b40751c6..a112565d8f 100644 --- a/Sources/App/Resources/en-GB.lproj/Localizable.strings +++ b/Sources/App/Resources/en-GB.lproj/Localizable.strings @@ -507,11 +507,8 @@ This server requires a client certificate (mTLS) but the operation was cancelled "live_activity.end_all.button" = "End All Activities"; "live_activity.end_all.confirm.button" = "End All"; "live_activity.end_all.confirm.title" = "End all Live Activities?"; -"live_activity.frequent_updates.footer" = "Allows Home Assistant to update Live Activities up to once per second. Enable in Settings \u203A %@ \u203A 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.section.active" = "Active Activities"; -"live_activity.section.privacy" = "Privacy"; "live_activity.section.status" = "Status"; "live_activity.status.enabled" = "Enabled"; "live_activity.status.not_supported" = "Not available on iPad"; diff --git a/Sources/App/Resources/en.lproj/Localizable.strings b/Sources/App/Resources/en.lproj/Localizable.strings index 59d003f468..af33d23086 100644 --- a/Sources/App/Resources/en.lproj/Localizable.strings +++ b/Sources/App/Resources/en.lproj/Localizable.strings @@ -507,11 +507,8 @@ This server requires a client certificate (mTLS) but the operation was cancelled "live_activity.end_all.button" = "End All Activities"; "live_activity.end_all.confirm.button" = "End All"; "live_activity.end_all.confirm.title" = "End all Live Activities?"; -"live_activity.frequent_updates.footer" = "Allows Home Assistant to update Live Activities up to once per second. Enable in Settings \u203A %@ \u203A 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.section.active" = "Active Activities"; -"live_activity.section.privacy" = "Privacy"; "live_activity.section.status" = "Status"; "live_activity.status.enabled" = "Enabled"; "live_activity.status.not_supported" = "Not available on iPad"; diff --git a/Sources/App/Resources/es-ES.lproj/Localizable.strings b/Sources/App/Resources/es-ES.lproj/Localizable.strings index f4c3b6eec7..43ede16290 100644 --- a/Sources/App/Resources/es-ES.lproj/Localizable.strings +++ b/Sources/App/Resources/es-ES.lproj/Localizable.strings @@ -507,11 +507,8 @@ This server requires a client certificate (mTLS) but the operation was cancelled "live_activity.end_all.button" = "End All Activities"; "live_activity.end_all.confirm.button" = "End All"; "live_activity.end_all.confirm.title" = "End all Live Activities?"; -"live_activity.frequent_updates.footer" = "Allows Home Assistant to update Live Activities up to once per second. Enable in Settings \u203A %@ \u203A 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.section.active" = "Active Activities"; -"live_activity.section.privacy" = "Privacy"; "live_activity.section.status" = "Status"; "live_activity.status.enabled" = "Enabled"; "live_activity.status.not_supported" = "Not available on iPad"; diff --git a/Sources/App/Resources/es-MX.lproj/Localizable.strings b/Sources/App/Resources/es-MX.lproj/Localizable.strings index 0a8d5de332..0a841bb857 100644 --- a/Sources/App/Resources/es-MX.lproj/Localizable.strings +++ b/Sources/App/Resources/es-MX.lproj/Localizable.strings @@ -507,11 +507,8 @@ This server requires a client certificate (mTLS) but the operation was cancelled "live_activity.end_all.button" = "End All Activities"; "live_activity.end_all.confirm.button" = "End All"; "live_activity.end_all.confirm.title" = "End all Live Activities?"; -"live_activity.frequent_updates.footer" = "Allows Home Assistant to update Live Activities up to once per second. Enable in Settings \u203A %@ \u203A 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.section.active" = "Active Activities"; -"live_activity.section.privacy" = "Privacy"; "live_activity.section.status" = "Status"; "live_activity.status.enabled" = "Enabled"; "live_activity.status.not_supported" = "Not available on iPad"; diff --git a/Sources/App/Resources/es.lproj/Localizable.strings b/Sources/App/Resources/es.lproj/Localizable.strings index d5ef25663a..bec18a5bac 100644 --- a/Sources/App/Resources/es.lproj/Localizable.strings +++ b/Sources/App/Resources/es.lproj/Localizable.strings @@ -507,11 +507,8 @@ This server requires a client certificate (mTLS) but the operation was cancelled "live_activity.end_all.button" = "End All Activities"; "live_activity.end_all.confirm.button" = "End All"; "live_activity.end_all.confirm.title" = "End all Live Activities?"; -"live_activity.frequent_updates.footer" = "Allows Home Assistant to update Live Activities up to once per second. Enable in Settings \u203A %@ \u203A 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.section.active" = "Active Activities"; -"live_activity.section.privacy" = "Privacy"; "live_activity.section.status" = "Status"; "live_activity.status.enabled" = "Enabled"; "live_activity.status.not_supported" = "Not available on iPad"; diff --git a/Sources/App/Resources/et.lproj/Localizable.strings b/Sources/App/Resources/et.lproj/Localizable.strings index 2811c2e6be..36412cc129 100644 --- a/Sources/App/Resources/et.lproj/Localizable.strings +++ b/Sources/App/Resources/et.lproj/Localizable.strings @@ -507,11 +507,8 @@ "live_activity.end_all.button" = "Lõpeta kõik tegevused"; "live_activity.end_all.confirm.button" = "Lõpeta kõik"; "live_activity.end_all.confirm.title" = "Kas lõpetada kõik Live Activity tegevused?"; -"live_activity.frequent_updates.footer" = "Lubab Home Assistantil reaalajas tegevusi kuni üks kord sekundis värskendada. Luba seadetes. \u203A %@ \u203A Otseülekanded."; "live_activity.frequent_updates.title" = "Sagedased uuendused"; -"live_activity.privacy.message" = "Live Activity sisu on nähtav lukustusekraanil ja dünaamilisel saarel ilma Face ID või Touch ID-ta. Vali hoolikalt, mida kuvada."; "live_activity.section.active" = "Aktiivsed tegevused"; -"live_activity.section.privacy" = "Privaatsus"; "live_activity.section.status" = "Olek"; "live_activity.status.enabled" = "Lubatud"; "live_activity.status.not_supported" = "Pole iPadis saadaval"; diff --git a/Sources/App/Resources/fi.lproj/Localizable.strings b/Sources/App/Resources/fi.lproj/Localizable.strings index 5a13b632ee..be83fcea7c 100644 --- a/Sources/App/Resources/fi.lproj/Localizable.strings +++ b/Sources/App/Resources/fi.lproj/Localizable.strings @@ -507,11 +507,8 @@ This server requires a client certificate (mTLS) but the operation was cancelled "live_activity.end_all.button" = "End All Activities"; "live_activity.end_all.confirm.button" = "End All"; "live_activity.end_all.confirm.title" = "End all Live Activities?"; -"live_activity.frequent_updates.footer" = "Allows Home Assistant to update Live Activities up to once per second. Enable in Settings \u203A %@ \u203A 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.section.active" = "Active Activities"; -"live_activity.section.privacy" = "Privacy"; "live_activity.section.status" = "Status"; "live_activity.status.enabled" = "Enabled"; "live_activity.status.not_supported" = "Not available on iPad"; diff --git a/Sources/App/Resources/fr.lproj/Localizable.strings b/Sources/App/Resources/fr.lproj/Localizable.strings index 60c10fe186..5151a88c59 100644 --- a/Sources/App/Resources/fr.lproj/Localizable.strings +++ b/Sources/App/Resources/fr.lproj/Localizable.strings @@ -507,11 +507,8 @@ This server requires a client certificate (mTLS) but the operation was cancelled "live_activity.end_all.button" = "End All Activities"; "live_activity.end_all.confirm.button" = "End All"; "live_activity.end_all.confirm.title" = "End all Live Activities?"; -"live_activity.frequent_updates.footer" = "Allows Home Assistant to update Live Activities up to once per second. Enable in Settings \u203A %@ \u203A 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.section.active" = "Active Activities"; -"live_activity.section.privacy" = "Privacy"; "live_activity.section.status" = "Status"; "live_activity.status.enabled" = "Enabled"; "live_activity.status.not_supported" = "Not available on iPad"; diff --git a/Sources/App/Resources/he.lproj/Localizable.strings b/Sources/App/Resources/he.lproj/Localizable.strings index 968b6b2581..4d2bce18c2 100644 --- a/Sources/App/Resources/he.lproj/Localizable.strings +++ b/Sources/App/Resources/he.lproj/Localizable.strings @@ -507,11 +507,8 @@ This server requires a client certificate (mTLS) but the operation was cancelled "live_activity.end_all.button" = "End All Activities"; "live_activity.end_all.confirm.button" = "End All"; "live_activity.end_all.confirm.title" = "End all Live Activities?"; -"live_activity.frequent_updates.footer" = "Allows Home Assistant to update Live Activities up to once per second. Enable in Settings \u203A %@ \u203A 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.section.active" = "Active Activities"; -"live_activity.section.privacy" = "Privacy"; "live_activity.section.status" = "Status"; "live_activity.status.enabled" = "Enabled"; "live_activity.status.not_supported" = "Not available on iPad"; diff --git a/Sources/App/Resources/hu.lproj/Localizable.strings b/Sources/App/Resources/hu.lproj/Localizable.strings index 1e651da995..e0959db94c 100644 --- a/Sources/App/Resources/hu.lproj/Localizable.strings +++ b/Sources/App/Resources/hu.lproj/Localizable.strings @@ -507,11 +507,8 @@ Ez a szerver kliens tanúsítványt (mTLS) igényel, de a művelet megszakítva. "live_activity.end_all.button" = "End All Activities"; "live_activity.end_all.confirm.button" = "End All"; "live_activity.end_all.confirm.title" = "End all Live Activities?"; -"live_activity.frequent_updates.footer" = "Allows Home Assistant to update Live Activities up to once per second. Enable in Settings \u203A %@ \u203A 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.section.active" = "Active Activities"; -"live_activity.section.privacy" = "Privacy"; "live_activity.section.status" = "Status"; "live_activity.status.enabled" = "Enabled"; "live_activity.status.not_supported" = "Not available on iPad"; diff --git a/Sources/App/Resources/id.lproj/Localizable.strings b/Sources/App/Resources/id.lproj/Localizable.strings index 9c424ba70b..458d9ba7c5 100644 --- a/Sources/App/Resources/id.lproj/Localizable.strings +++ b/Sources/App/Resources/id.lproj/Localizable.strings @@ -507,11 +507,8 @@ Server ini memerlukan sertifikat klien (mTLS) tetapi operasi dibatalkan."; "live_activity.end_all.button" = "End All Activities"; "live_activity.end_all.confirm.button" = "End All"; "live_activity.end_all.confirm.title" = "End all Live Activities?"; -"live_activity.frequent_updates.footer" = "Allows Home Assistant to update Live Activities up to once per second. Enable in Settings \u203A %@ \u203A 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.section.active" = "Active Activities"; -"live_activity.section.privacy" = "Privacy"; "live_activity.section.status" = "Status"; "live_activity.status.enabled" = "Enabled"; "live_activity.status.not_supported" = "Not available on iPad"; diff --git a/Sources/App/Resources/it.lproj/Localizable.strings b/Sources/App/Resources/it.lproj/Localizable.strings index afe69868ba..90f7b8a8b6 100644 --- a/Sources/App/Resources/it.lproj/Localizable.strings +++ b/Sources/App/Resources/it.lproj/Localizable.strings @@ -507,11 +507,8 @@ Questo server richiede un certificato client (mTLS), ma l'operazione è stata an "live_activity.end_all.button" = "End All Activities"; "live_activity.end_all.confirm.button" = "End All"; "live_activity.end_all.confirm.title" = "End all Live Activities?"; -"live_activity.frequent_updates.footer" = "Consente a Home Assistant di aggiornare le Live Activity fino a una volta al secondo. Attiva nelle Impostazioni \u203A %@ \u203A 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.section.active" = "Active Activities"; -"live_activity.section.privacy" = "Privacy"; "live_activity.section.status" = "Status"; "live_activity.status.enabled" = "Enabled"; "live_activity.status.not_supported" = "Not available on iPad"; diff --git a/Sources/App/Resources/ja.lproj/Localizable.strings b/Sources/App/Resources/ja.lproj/Localizable.strings index 8b6dc41c9e..18a594847a 100644 --- a/Sources/App/Resources/ja.lproj/Localizable.strings +++ b/Sources/App/Resources/ja.lproj/Localizable.strings @@ -507,11 +507,8 @@ This server requires a client certificate (mTLS) but the operation was cancelled "live_activity.end_all.button" = "End All Activities"; "live_activity.end_all.confirm.button" = "End All"; "live_activity.end_all.confirm.title" = "End all Live Activities?"; -"live_activity.frequent_updates.footer" = "Allows Home Assistant to update Live Activities up to once per second. Enable in Settings \u203A %@ \u203A 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.section.active" = "Active Activities"; -"live_activity.section.privacy" = "Privacy"; "live_activity.section.status" = "Status"; "live_activity.status.enabled" = "Enabled"; "live_activity.status.not_supported" = "Not available on iPad"; diff --git a/Sources/App/Resources/ko-KR.lproj/Localizable.strings b/Sources/App/Resources/ko-KR.lproj/Localizable.strings index fe36a7782b..0aee9e0b6c 100644 --- a/Sources/App/Resources/ko-KR.lproj/Localizable.strings +++ b/Sources/App/Resources/ko-KR.lproj/Localizable.strings @@ -507,11 +507,8 @@ This server requires a client certificate (mTLS) but the operation was cancelled "live_activity.end_all.button" = "End All Activities"; "live_activity.end_all.confirm.button" = "End All"; "live_activity.end_all.confirm.title" = "End all Live Activities?"; -"live_activity.frequent_updates.footer" = "Allows Home Assistant to update Live Activities up to once per second. Enable in Settings \u203A %@ \u203A 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.section.active" = "Active Activities"; -"live_activity.section.privacy" = "Privacy"; "live_activity.section.status" = "Status"; "live_activity.status.enabled" = "Enabled"; "live_activity.status.not_supported" = "Not available on iPad"; diff --git a/Sources/App/Resources/ml.lproj/Localizable.strings b/Sources/App/Resources/ml.lproj/Localizable.strings index e309c75b15..433619071c 100644 --- a/Sources/App/Resources/ml.lproj/Localizable.strings +++ b/Sources/App/Resources/ml.lproj/Localizable.strings @@ -507,11 +507,8 @@ This server requires a client certificate (mTLS) but the operation was cancelled "live_activity.end_all.button" = "End All Activities"; "live_activity.end_all.confirm.button" = "End All"; "live_activity.end_all.confirm.title" = "End all Live Activities?"; -"live_activity.frequent_updates.footer" = "Allows Home Assistant to update Live Activities up to once per second. Enable in Settings \u203A %@ \u203A 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.section.active" = "Active Activities"; -"live_activity.section.privacy" = "Privacy"; "live_activity.section.status" = "Status"; "live_activity.status.enabled" = "Enabled"; "live_activity.status.not_supported" = "Not available on iPad"; diff --git a/Sources/App/Resources/nb.lproj/Localizable.strings b/Sources/App/Resources/nb.lproj/Localizable.strings index a326f221e2..ef41d064fd 100644 --- a/Sources/App/Resources/nb.lproj/Localizable.strings +++ b/Sources/App/Resources/nb.lproj/Localizable.strings @@ -507,11 +507,8 @@ This server requires a client certificate (mTLS) but the operation was cancelled "live_activity.end_all.button" = "End All Activities"; "live_activity.end_all.confirm.button" = "End All"; "live_activity.end_all.confirm.title" = "End all Live Activities?"; -"live_activity.frequent_updates.footer" = "Lar Home Assistant oppdatere Live Activities opptil én gang per sekund. Aktiver i Settings \u203A %@ \u203A Live Activities."; "live_activity.frequent_updates.title" = "Hyppige oppdateringer"; -"live_activity.privacy.message" = "Live Activity-innhold er synlig på låseskjermen og Dynamic Island uten Face ID eller Touch ID. Vær nøye med hva du viser."; "live_activity.section.active" = "Active Activities"; -"live_activity.section.privacy" = "Privacy"; "live_activity.section.status" = "Status"; "live_activity.status.enabled" = "Enabled"; "live_activity.status.not_supported" = "Not available on iPad"; diff --git a/Sources/App/Resources/nl.lproj/Localizable.strings b/Sources/App/Resources/nl.lproj/Localizable.strings index 88296eddd4..594656a054 100644 --- a/Sources/App/Resources/nl.lproj/Localizable.strings +++ b/Sources/App/Resources/nl.lproj/Localizable.strings @@ -507,11 +507,8 @@ Deze server vereist een clientcertificaat (mTLS), maar de bewerking is geannulee "live_activity.end_all.button" = "Alle activiteiten beëindigen"; "live_activity.end_all.confirm.button" = "Alles beëindigen"; "live_activity.end_all.confirm.title" = "Alle live activiteiten beëindigen?"; -"live_activity.frequent_updates.footer" = "Hiermee kan Home Assistant Live Activiteiten tot één keer per seconde bijwerken. Schakel dit in via Instellingen \u203A %@ \u203A Live activiteiten."; "live_activity.frequent_updates.title" = "Regelmatig bijwerken"; -"live_activity.privacy.message" = "Live activiteit-inhoud is zichtbaar op je vergrendelscherm en Dynamic Island zonder Face ID of Touch ID. Bedenk goed wat je wilt laten zien."; "live_activity.section.active" = "Actieve activiteiten"; -"live_activity.section.privacy" = "Privacy"; "live_activity.section.status" = "Status"; "live_activity.status.enabled" = "Ingeschakeld"; "live_activity.status.not_supported" = "Niet beschikbaar op iPad"; diff --git a/Sources/App/Resources/pl-PL.lproj/Localizable.strings b/Sources/App/Resources/pl-PL.lproj/Localizable.strings index 960f88a4da..38fbaf986e 100644 --- a/Sources/App/Resources/pl-PL.lproj/Localizable.strings +++ b/Sources/App/Resources/pl-PL.lproj/Localizable.strings @@ -507,11 +507,8 @@ "live_activity.end_all.button" = "End All Activities"; "live_activity.end_all.confirm.button" = "End All"; "live_activity.end_all.confirm.title" = "End all Live Activities?"; -"live_activity.frequent_updates.footer" = "Allows Home Assistant to update Live Activities up to once per second. Enable in Settings \u203A %@ \u203A 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.section.active" = "Active Activities"; -"live_activity.section.privacy" = "Privacy"; "live_activity.section.status" = "Status"; "live_activity.status.enabled" = "Enabled"; "live_activity.status.not_supported" = "Not available on iPad"; diff --git a/Sources/App/Resources/pt-BR.lproj/Localizable.strings b/Sources/App/Resources/pt-BR.lproj/Localizable.strings index 6bebe5dbff..b324cb4538 100644 --- a/Sources/App/Resources/pt-BR.lproj/Localizable.strings +++ b/Sources/App/Resources/pt-BR.lproj/Localizable.strings @@ -507,11 +507,8 @@ This server requires a client certificate (mTLS) but the operation was cancelled "live_activity.end_all.button" = "End All Activities"; "live_activity.end_all.confirm.button" = "End All"; "live_activity.end_all.confirm.title" = "End all Live Activities?"; -"live_activity.frequent_updates.footer" = "Allows Home Assistant to update Live Activities up to once per second. Enable in Settings \u203A %@ \u203A 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.section.active" = "Active Activities"; -"live_activity.section.privacy" = "Privacy"; "live_activity.section.status" = "Status"; "live_activity.status.enabled" = "Enabled"; "live_activity.status.not_supported" = "Not available on iPad"; diff --git a/Sources/App/Resources/ru.lproj/Localizable.strings b/Sources/App/Resources/ru.lproj/Localizable.strings index 386ae67ca6..2013674d04 100644 --- a/Sources/App/Resources/ru.lproj/Localizable.strings +++ b/Sources/App/Resources/ru.lproj/Localizable.strings @@ -507,11 +507,8 @@ "live_activity.end_all.button" = "End All Activities"; "live_activity.end_all.confirm.button" = "End All"; "live_activity.end_all.confirm.title" = "End all Live Activities?"; -"live_activity.frequent_updates.footer" = "Allows Home Assistant to update Live Activities up to once per second. Enable in Settings \u203A %@ \u203A 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.section.active" = "Active Activities"; -"live_activity.section.privacy" = "Privacy"; "live_activity.section.status" = "Status"; "live_activity.status.enabled" = "Enabled"; "live_activity.status.not_supported" = "Not available on iPad"; diff --git a/Sources/App/Resources/sl.lproj/Localizable.strings b/Sources/App/Resources/sl.lproj/Localizable.strings index 7b0333058d..e36a5db897 100644 --- a/Sources/App/Resources/sl.lproj/Localizable.strings +++ b/Sources/App/Resources/sl.lproj/Localizable.strings @@ -507,11 +507,8 @@ This server requires a client certificate (mTLS) but the operation was cancelled "live_activity.end_all.button" = "End All Activities"; "live_activity.end_all.confirm.button" = "End All"; "live_activity.end_all.confirm.title" = "End all Live Activities?"; -"live_activity.frequent_updates.footer" = "Allows Home Assistant to update Live Activities up to once per second. Enable in Settings \u203A %@ \u203A 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.section.active" = "Active Activities"; -"live_activity.section.privacy" = "Privacy"; "live_activity.section.status" = "Status"; "live_activity.status.enabled" = "Enabled"; "live_activity.status.not_supported" = "Not available on iPad"; diff --git a/Sources/App/Resources/sv.lproj/Localizable.strings b/Sources/App/Resources/sv.lproj/Localizable.strings index 103a6dca2e..64584e1dcb 100644 --- a/Sources/App/Resources/sv.lproj/Localizable.strings +++ b/Sources/App/Resources/sv.lproj/Localizable.strings @@ -507,11 +507,8 @@ This server requires a client certificate (mTLS) but the operation was cancelled "live_activity.end_all.button" = "End All Activities"; "live_activity.end_all.confirm.button" = "End All"; "live_activity.end_all.confirm.title" = "End all Live Activities?"; -"live_activity.frequent_updates.footer" = "Allows Home Assistant to update Live Activities up to once per second. Enable in Settings \u203A %@ \u203A 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.section.active" = "Active Activities"; -"live_activity.section.privacy" = "Privacy"; "live_activity.section.status" = "Status"; "live_activity.status.enabled" = "Enabled"; "live_activity.status.not_supported" = "Not available on iPad"; diff --git a/Sources/App/Resources/tr.lproj/Localizable.strings b/Sources/App/Resources/tr.lproj/Localizable.strings index e407a56b94..74dbaa43b8 100644 --- a/Sources/App/Resources/tr.lproj/Localizable.strings +++ b/Sources/App/Resources/tr.lproj/Localizable.strings @@ -507,11 +507,8 @@ "live_activity.end_all.button" = "Tüm Etkinlikleri Sonlandır"; "live_activity.end_all.confirm.button" = "Tümünü Sonlandır"; "live_activity.end_all.confirm.title" = "Tüm canlı etkinlikleri sonlandırılsın mı?"; -"live_activity.frequent_updates.footer" = "Home Assistant'ın Canlı Etkinlikleri saniyede bir kez güncellemesine olanak tanır. Ayarlar'dan etkinleştirin. \u203A %@ \u203A Canlı Etkinlikler."; "live_activity.frequent_updates.title" = "Sık Güncellemeler"; -"live_activity.privacy.message" = "Canlı Etkinlik içeriği, Face ID veya Touch ID olmadan da Kilit Ekranınızda ve Dinamik Ada'da görünür. Ne göstereceğinizi dikkatlice seçin."; "live_activity.section.active" = "Aktif Etkinlikler"; -"live_activity.section.privacy" = "Gizlilik"; "live_activity.section.status" = "Durum"; "live_activity.status.enabled" = "Etkin"; "live_activity.status.not_supported" = "iPad'de mevcut değil"; diff --git a/Sources/App/Resources/uk.lproj/Localizable.strings b/Sources/App/Resources/uk.lproj/Localizable.strings index e7ff95e18a..8a5c892714 100644 --- a/Sources/App/Resources/uk.lproj/Localizable.strings +++ b/Sources/App/Resources/uk.lproj/Localizable.strings @@ -507,11 +507,8 @@ This server requires a client certificate (mTLS) but the operation was cancelled "live_activity.end_all.button" = "End All Activities"; "live_activity.end_all.confirm.button" = "End All"; "live_activity.end_all.confirm.title" = "End all Live Activities?"; -"live_activity.frequent_updates.footer" = "Allows Home Assistant to update Live Activities up to once per second. Enable in Settings \u203A %@ \u203A 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.section.active" = "Active Activities"; -"live_activity.section.privacy" = "Privacy"; "live_activity.section.status" = "Status"; "live_activity.status.enabled" = "Enabled"; "live_activity.status.not_supported" = "Not available on iPad"; diff --git a/Sources/App/Resources/vi.lproj/Localizable.strings b/Sources/App/Resources/vi.lproj/Localizable.strings index 1a7f84c0eb..0d977b2b4b 100644 --- a/Sources/App/Resources/vi.lproj/Localizable.strings +++ b/Sources/App/Resources/vi.lproj/Localizable.strings @@ -507,11 +507,8 @@ This server requires a client certificate (mTLS) but the operation was cancelled "live_activity.end_all.button" = "End All Activities"; "live_activity.end_all.confirm.button" = "End All"; "live_activity.end_all.confirm.title" = "End all Live Activities?"; -"live_activity.frequent_updates.footer" = "Allows Home Assistant to update Live Activities up to once per second. Enable in Settings \u203A %@ \u203A 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.section.active" = "Active Activities"; -"live_activity.section.privacy" = "Privacy"; "live_activity.section.status" = "Status"; "live_activity.status.enabled" = "Enabled"; "live_activity.status.not_supported" = "Not available on iPad"; diff --git a/Sources/App/Resources/zh-Hans.lproj/Localizable.strings b/Sources/App/Resources/zh-Hans.lproj/Localizable.strings index b6e16ea329..058bc5d8a9 100644 --- a/Sources/App/Resources/zh-Hans.lproj/Localizable.strings +++ b/Sources/App/Resources/zh-Hans.lproj/Localizable.strings @@ -507,11 +507,8 @@ "live_activity.end_all.button" = "结束所有活动"; "live_activity.end_all.confirm.button" = "全部结束"; "live_activity.end_all.confirm.title" = "结束所有实时活动吗 ?"; -"live_activity.frequent_updates.footer" = "允许 Home Assistant 每秒更新一次实时活动。在“设置” \u203A %@ \u203A “实时活动”中启用。"; "live_activity.frequent_updates.title" = "频繁更新"; -"live_activity.privacy.message" = "即使没有面容 ID 或触控 ID,您也可以在锁定屏幕和动态岛屿上查看实时活动内容。请谨慎选择要显示的内容。"; "live_activity.section.active" = "活跃活动"; -"live_activity.section.privacy" = "隐私"; "live_activity.section.status" = "状况"; "live_activity.status.enabled" = "已启用"; "live_activity.status.not_supported" = "iPad 版暂不可用"; diff --git a/Sources/App/Resources/zh-Hant.lproj/Localizable.strings b/Sources/App/Resources/zh-Hant.lproj/Localizable.strings index ec2148c75f..3a70016102 100644 --- a/Sources/App/Resources/zh-Hant.lproj/Localizable.strings +++ b/Sources/App/Resources/zh-Hant.lproj/Localizable.strings @@ -507,11 +507,8 @@ "live_activity.end_all.button" = "結束所有即時動態"; "live_activity.end_all.confirm.button" = "全部結束"; "live_activity.end_all.confirm.title" = "結束所有即時動態?"; -"live_activity.frequent_updates.footer" = "允許 Home Assistant 每秒更新一次即時動態。於設定中開啟 \u203A %@ \u203A 即時動態。"; "live_activity.frequent_updates.title" = "頻繁更新"; -"live_activity.privacy.message" = "即使沒有 Face ID 或 Touch ID,也可以在鎖定畫面和動態島上查看即時動態內容。請謹慎選擇要顯示的內容。"; "live_activity.section.active" = "啟用動態"; -"live_activity.section.privacy" = "隱私權"; "live_activity.section.status" = "狀態"; "live_activity.status.enabled" = "已開啟"; "live_activity.status.not_supported" = "iPad 版暫不可用"; diff --git a/Sources/App/Settings/LiveActivity/LiveActivitySettingsView.swift b/Sources/App/Settings/LiveActivity/LiveActivitySettingsView.swift index 7c564fa7c6..6f688654d7 100644 --- a/Sources/App/Settings/LiveActivity/LiveActivitySettingsView.swift +++ b/Sources/App/Settings/LiveActivity/LiveActivitySettingsView.swift @@ -27,7 +27,6 @@ struct LiveActivitySettingsView: View { ) statusSection - frequentUpdatesSection if activities.isEmpty { Section(L10n.LiveActivity.Section.active) { @@ -63,7 +62,6 @@ struct LiveActivitySettingsView: View { } } - privacySection samplesSection } .navigationTitle(L10n.LiveActivity.title) @@ -73,7 +71,7 @@ struct LiveActivitySettingsView: View { // MARK: - Sections private var statusSection: some View { - Section(L10n.LiveActivity.Section.status) { + Section { HStack { Label(L10n.LiveActivity.title, systemSymbol: .livephoto) Spacer() @@ -92,6 +90,24 @@ struct LiveActivitySettingsView: View { .foregroundStyle(.orange) } } + + HStack { + Label(L10n.LiveActivity.FrequentUpdates.title, systemSymbol: .bolt) + Spacer() + if frequentUpdatesEnabled { + Text(L10n.LiveActivity.Status.enabled) + .foregroundStyle(.green) + } else { + Button(L10n.LiveActivity.Status.openSettings) { + if let url = URL(string: UIApplication.openSettingsURLString) { + UIApplication.shared.open(url) + } + } + .foregroundStyle(.secondary) + } + } + } header: { + Text(L10n.LiveActivity.Section.status) } } @@ -567,50 +583,12 @@ struct LiveActivitySettingsView: View { ) } - private var privacySection: some View { - Section { - Label(L10n.LiveActivity.Privacy.message, systemSymbol: .lockShield) - .font(.footnote) - .foregroundStyle(.secondary) - } header: { - Text(L10n.LiveActivity.Section.privacy) - } - } - - @available(iOS 17.2, *) - private var frequentUpdatesSection: some View { - let appName = Bundle.main.infoDictionary?["CFBundleDisplayName"] as? String ?? "Home Assistant" - return Section { - HStack { - Label(L10n.LiveActivity.FrequentUpdates.title, systemSymbol: .bolt) - Spacer() - if frequentUpdatesEnabled { - Text(L10n.LiveActivity.Status.enabled) - .foregroundStyle(.green) - } else { - Button(L10n.LiveActivity.Status.openSettings) { - if let url = URL(string: UIApplication.openSettingsURLString) { - UIApplication.shared.open(url) - } - } - .foregroundStyle(.secondary) - } - } - } header: { - Text(L10n.LiveActivity.FrequentUpdates.title) - } footer: { - Text(L10n.LiveActivity.FrequentUpdates.footer(appName)) - } - } - // MARK: - Data private func loadActivities() async { let info = ActivityAuthorizationInfo() authorizationEnabled = info.areActivitiesEnabled - if #available(iOS 17.2, *) { - frequentUpdatesEnabled = info.frequentPushesEnabled - } + frequentUpdatesEnabled = info.frequentPushesEnabled activities = Activity.activities.map { ActivitySnapshot(activity: $0) diff --git a/Sources/Shared/Resources/Swiftgen/Strings.swift b/Sources/Shared/Resources/Swiftgen/Strings.swift index ec37262803..bcf80f20b5 100644 --- a/Sources/Shared/Resources/Swiftgen/Strings.swift +++ b/Sources/Shared/Resources/Swiftgen/Strings.swift @@ -1888,22 +1888,12 @@ 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. - public static func footer(_ p1: Any) -> String { - return L10n.tr("Localizable", "live_activity.frequent_updates.footer", String(describing: p1)) - } /// Frequent Updates public static var title: String { return L10n.tr("Localizable", "live_activity.frequent_updates.title") } } - public enum Privacy { - /// Live Activity content is visible on your Lock Screen and Dynamic Island without Face ID or Touch ID. Choose what you display carefully. - public static var message: String { return L10n.tr("Localizable", "live_activity.privacy.message") } - } public enum Section { /// Active Activities public static var active: String { return L10n.tr("Localizable", "live_activity.section.active") } - /// Privacy - public static var privacy: String { return L10n.tr("Localizable", "live_activity.section.privacy") } /// Status public static var status: String { return L10n.tr("Localizable", "live_activity.section.status") } } From a1fb8864691672652cf9c24e54726cfe0e7e1033 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bruno=20Pantale=C3=A3o?= <5808343+bgoncal@users.noreply.github.com> Date: Fri, 24 Apr 2026 14:46:29 +0200 Subject: [PATCH 03/15] PR improvements --- .../LiveActivitySettingsView.swift | 25 +++++++++++++------ .../App/Settings/Settings/SettingsItem.swift | 23 +++++++++++++++-- 2 files changed, 39 insertions(+), 9 deletions(-) diff --git a/Sources/App/Settings/LiveActivity/LiveActivitySettingsView.swift b/Sources/App/Settings/LiveActivity/LiveActivitySettingsView.swift index 6f688654d7..29c3f6e8f1 100644 --- a/Sources/App/Settings/LiveActivity/LiveActivitySettingsView.swift +++ b/Sources/App/Settings/LiveActivity/LiveActivitySettingsView.swift @@ -6,7 +6,7 @@ import SwiftUI // MARK: - Entry point /// Deployment target is iOS 15. The settings item is filtered from the list on < iOS 17.2 -/// (see SettingsItem.allVisibleCases), so this view is only ever navigated to on iOS 17.2+. +/// (see SettingsItem.generalItems/allVisibleCases), so this view is only ever navigated to on iOS 17.2+. @available(iOS 17.2, *) struct LiveActivitySettingsView: View { // MARK: State @@ -62,7 +62,9 @@ struct LiveActivitySettingsView: View { } } + #if DEBUG samplesSection + #endif } .navigationTitle(L10n.LiveActivity.title) .task { await loadActivities() } @@ -75,12 +77,12 @@ struct LiveActivitySettingsView: View { HStack { Label(L10n.LiveActivity.title, systemSymbol: .livephoto) Spacer() - if authorizationEnabled { - Text(L10n.LiveActivity.Status.enabled) - .foregroundStyle(.green) - } else if UIDevice.current.userInterfaceIdiom == .pad { + if !isLiveActivitySupportedOnDevice { Text(L10n.LiveActivity.Status.notSupported) .foregroundStyle(.secondary) + } else if authorizationEnabled { + Text(L10n.LiveActivity.Status.enabled) + .foregroundStyle(.green) } else { Button(L10n.LiveActivity.Status.openSettings) { if let url = URL(string: UIApplication.openSettingsURLString) { @@ -94,7 +96,10 @@ struct LiveActivitySettingsView: View { HStack { Label(L10n.LiveActivity.FrequentUpdates.title, systemSymbol: .bolt) Spacer() - if frequentUpdatesEnabled { + if !isLiveActivitySupportedOnDevice { + Text(L10n.LiveActivity.Status.notSupported) + .foregroundStyle(.secondary) + } else if frequentUpdatesEnabled { Text(L10n.LiveActivity.Status.enabled) .foregroundStyle(.green) } else { @@ -111,8 +116,13 @@ struct LiveActivitySettingsView: View { } } - // MARK: - Samples + private var isLiveActivitySupportedOnDevice: Bool { + UIDevice.current.userInterfaceIdiom != .pad + } + + // MARK: - Samples (DEBUG builds only) + #if DEBUG // // Two sections: Static (fixed snapshots to verify layout) and Animated (multi-stage // self-updating sequences to simulate real HA automation behavior). @@ -582,6 +592,7 @@ struct LiveActivitySettingsView: View { ] ) } + #endif // MARK: - Data diff --git a/Sources/App/Settings/Settings/SettingsItem.swift b/Sources/App/Settings/Settings/SettingsItem.swift index 34eb5180bb..d3d0e35b12 100644 --- a/Sources/App/Settings/Settings/SettingsItem.swift +++ b/Sources/App/Settings/Settings/SettingsItem.swift @@ -147,6 +147,10 @@ enum SettingsItem: String, Hashable, CaseIterable { static var allVisibleCases: [SettingsItem] { allCases.filter { item in + if item == .liveActivities { + return canShowLiveActivities + } + // Filter based on platform #if targetEnvironment(macCatalyst) let hiddenItems: [SettingsItem] = [ @@ -159,7 +163,6 @@ enum SettingsItem: String, Hashable, CaseIterable { .nfc, .help, .whatsNew, - .liveActivities, ] if hiddenItems.contains(item) { @@ -172,7 +175,11 @@ enum SettingsItem: String, Hashable, CaseIterable { } static var generalItems: [SettingsItem] { - [.general, .gestures, .location, .notifications, .kiosk, .liveActivities] + var items: [SettingsItem] = [.general, .gestures, .location, .notifications, .kiosk] + if canShowLiveActivities { + items.append(.liveActivities) + } + return items } static var integrationItems: [SettingsItem] { @@ -194,6 +201,18 @@ enum SettingsItem: String, Hashable, CaseIterable { static var helpItems: [SettingsItem] { [.help, .privacy, .debugging] } + + private static var canShowLiveActivities: Bool { + #if os(iOS) && !targetEnvironment(macCatalyst) + if #available(iOS 17.2, *) { + return true + } else { + return false + } + #else + return false + #endif + } } // MARK: - Material Design Icons Image From 292d96858d707a88f18c9add785f9d684993314c Mon Sep 17 00:00:00 2001 From: rwarner Date: Wed, 29 Apr 2026 12:31:15 -0400 Subject: [PATCH 04/15] Remove supports_live_activities fields from registration payload HA core no longer reads or stores these fields (removed in home-assistant/core#166072 per code review). Sending unused data wastes bandwidth on every app registration. Co-Authored-By: Claude Sonnet 4.6 --- Sources/Shared/API/HAAPI.swift | 7 ------- 1 file changed, 7 deletions(-) diff --git a/Sources/Shared/API/HAAPI.swift b/Sources/Shared/API/HAAPI.swift index 2b842d4ee9..83d65bd409 100644 --- a/Sources/Shared/API/HAAPI.swift +++ b/Sources/Shared/API/HAAPI.swift @@ -602,13 +602,6 @@ public class HomeAssistantAPI { #if os(iOS) && !targetEnvironment(macCatalyst) if #available(iOS 17.2, *) { - // Advertise Live Activity support so HA can send activity push tokens - // to the relay server. areActivitiesEnabled reflects the user's Settings - // toggle and returns true on both iPhone and iPad (iPadOS 17+). - appData["supports_live_activities"] = ActivityAuthorizationInfo().areActivitiesEnabled - appData["supports_live_activities_frequent_updates"] = - ActivityAuthorizationInfo().frequentPushesEnabled - // Push-to-start token (stored in Keychain at launch, updated via stream). // The relay server uses this token to start a Live Activity entirely via APNs. if let pushToStartToken = LiveActivityRegistry.storedPushToStartToken { From 4faadd42af8ff6face68841b59d8181a88d8f627 Mon Sep 17 00:00:00 2001 From: rwarner Date: Wed, 29 Apr 2026 12:29:26 -0400 Subject: [PATCH 05/15] Rename live activity webhook types to drop mobile_app_ prefix Aligns with home-assistant/core#166072 where the webhook handlers were renamed from mobile_app_live_activity_token/dismissed to live_activity_token/dismissed to match the naming convention of all other webhooks in the mobile_app integration (none of which carry the prefix). Co-Authored-By: Claude Sonnet 4.6 --- Sources/Shared/LiveActivity/LiveActivityRegistry.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Sources/Shared/LiveActivity/LiveActivityRegistry.swift b/Sources/Shared/LiveActivity/LiveActivityRegistry.swift index c0775fe813..8816054136 100644 --- a/Sources/Shared/LiveActivity/LiveActivityRegistry.swift +++ b/Sources/Shared/LiveActivity/LiveActivityRegistry.swift @@ -36,12 +36,12 @@ public actor LiveActivityRegistry: LiveActivityRegistryProtocol { // MARK: - Webhook Constants (wire-format frozen — tested in LiveActivityContractTests) /// Webhook type for reporting a new per-activity push token to HA. - static let webhookTypeToken = "mobile_app_live_activity_token" + static let webhookTypeToken = "live_activity_token" /// Keys in the token webhook request data dictionary. static let tokenWebhookKeys: Set = ["activity_id", "push_token", "apns_environment"] /// Webhook type for reporting that a Live Activity was dismissed. - static let webhookTypeDismissed = "mobile_app_live_activity_dismissed" + static let webhookTypeDismissed = "live_activity_dismissed" /// Keys in the dismissed webhook request data dictionary. static let dismissedWebhookKeys: Set = ["activity_id", "live_activity_tag", "reason"] From 9a5e0d1222142c835e69871957d5e219de750845 Mon Sep 17 00:00:00 2001 From: rwarner Date: Wed, 29 Apr 2026 12:36:03 -0400 Subject: [PATCH 06/15] Update contract tests to match renamed webhook types Follows the rename in LiveActivityRegistry.swift from mobile_app_live_activity_* to live_activity_* to stay consistent with HA core's webhook naming convention. Co-Authored-By: Claude Sonnet 4.6 --- Tests/Shared/LiveActivity/LiveActivityContractTests.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Tests/Shared/LiveActivity/LiveActivityContractTests.swift b/Tests/Shared/LiveActivity/LiveActivityContractTests.swift index 1f4e3a631a..6d4cd0111f 100644 --- a/Tests/Shared/LiveActivity/LiveActivityContractTests.swift +++ b/Tests/Shared/LiveActivity/LiveActivityContractTests.swift @@ -90,7 +90,7 @@ final class LiveActivityContractTests: XCTestCase { func testWebhookTypeToken_isFrozen() { XCTAssertEqual( LiveActivityRegistry.webhookTypeToken, - "mobile_app_live_activity_token" + "live_activity_token" ) } @@ -108,7 +108,7 @@ final class LiveActivityContractTests: XCTestCase { func testWebhookTypeDismissed_isFrozen() { XCTAssertEqual( LiveActivityRegistry.webhookTypeDismissed, - "mobile_app_live_activity_dismissed" + "live_activity_dismissed" ) } From 4cc787e9ffd20e14a38d9f7d8bf9388ef75cd0c7 Mon Sep 17 00:00:00 2001 From: rwarner Date: Wed, 29 Apr 2026 12:41:48 -0400 Subject: [PATCH 07/15] Remove unused ActivityKit import from HAAPI.swift Now that the supports_live_activities registration fields are gone, ActivityKit types are no longer referenced in this file. Co-Authored-By: Claude Sonnet 4.6 --- Sources/Shared/API/HAAPI.swift | 3 --- 1 file changed, 3 deletions(-) diff --git a/Sources/Shared/API/HAAPI.swift b/Sources/Shared/API/HAAPI.swift index 83d65bd409..0608209ffa 100644 --- a/Sources/Shared/API/HAAPI.swift +++ b/Sources/Shared/API/HAAPI.swift @@ -11,9 +11,6 @@ import Version #if os(iOS) import Reachability #endif -#if os(iOS) && !targetEnvironment(macCatalyst) -import ActivityKit -#endif public class HomeAssistantAPI { public enum APIError: Error, Equatable { From 3f30ee2ac7e4b2ecf4b734b65ac688a802f6c7be Mon Sep 17 00:00:00 2001 From: rwarner Date: Thu, 7 May 2026 09:42:51 -0400 Subject: [PATCH 08/15] =?UTF-8?q?Remove=20end=5Flive=5Factivity=20command?= =?UTF-8?q?=20=E2=80=94=20clear=5Fnotification=20handles=20it?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit clear_notification with a tag already ends Live Activities via HandlerClearNotification, making end_live_activity redundant. Removes HandlerEndLiveActivity and its registration. Co-Authored-By: Claude Sonnet 4.6 --- .../HandlerLiveActivity.swift | 45 ------------------- .../NotificationsCommandManager.swift | 1 - 2 files changed, 46 deletions(-) diff --git a/Sources/Shared/Notifications/NotificationCommands/HandlerLiveActivity.swift b/Sources/Shared/Notifications/NotificationCommands/HandlerLiveActivity.swift index 0d710150fd..d1729b9921 100644 --- a/Sources/Shared/Notifications/NotificationCommands/HandlerLiveActivity.swift +++ b/Sources/Shared/Notifications/NotificationCommands/HandlerLiveActivity.swift @@ -137,49 +137,4 @@ struct HandlerStartOrUpdateLiveActivity: NotificationCommandHandler { } } -// MARK: - HandlerEndLiveActivity - -/// Handles explicit `end_live_activity` commands. -/// Note: the `clear_notification` + `tag` dismiss flow is handled in `HandlerClearNotification`. -@available(iOS 17.2, *) -struct HandlerEndLiveActivity: NotificationCommandHandler { - func handle(_ payload: [String: Any]) -> Promise { - guard !Current.isAppExtension else { - return .value(()) - } - - return Promise { seal in - Task { - guard let tag = payload["tag"] as? String, !tag.isEmpty, - HandlerStartOrUpdateLiveActivity.isValidTag(tag) else { - seal.fulfill(()) - return - } - - let policy = Self.dismissalPolicy(from: payload) - await Current.liveActivityRegistry?.end(tag: tag, dismissalPolicy: policy) - seal.fulfill(()) - } - } - } - - private static func dismissalPolicy(from payload: [String: Any]) -> ActivityUIDismissalPolicy { - switch payload["dismissal_policy"] as? String { - case "default": - return .default - case let str where str?.hasPrefix("after:") == true: - if let timestampStr = str?.dropFirst(6), - let timestamp = Double(timestampStr) { - // Cap to 24 hours — iOS enforces its own maximum, but this prevents - // a far-future date from lingering in the dismissed activities list - // longer than intended if Apple ever relaxes the OS limit. - let maxDate = Date().addingTimeInterval(24 * 60 * 60) - return .after(min(Date(timeIntervalSince1970: timestamp), maxDate)) - } - return .immediate - default: - return .immediate - } - } -} #endif diff --git a/Sources/Shared/Notifications/NotificationCommands/NotificationsCommandManager.swift b/Sources/Shared/Notifications/NotificationCommands/NotificationsCommandManager.swift index 9760f49b28..8e52c01123 100644 --- a/Sources/Shared/Notifications/NotificationCommands/NotificationsCommandManager.swift +++ b/Sources/Shared/Notifications/NotificationCommands/NotificationsCommandManager.swift @@ -25,7 +25,6 @@ public class NotificationCommandManager { #if !targetEnvironment(macCatalyst) if #available(iOS 17.2, *) { register(command: "live_activity", handler: HandlerStartOrUpdateLiveActivity()) - register(command: "end_live_activity", handler: HandlerEndLiveActivity()) } #endif #endif From ce051cae5ec9c9c5f84ab2f8ef4fe1ec8a83cfb4 Mon Sep 17 00:00:00 2001 From: rwarner Date: Thu, 7 May 2026 09:49:15 -0400 Subject: [PATCH 09/15] =?UTF-8?q?Remove=20end=5Flive=5Factivity=20tests=20?= =?UTF-8?q?=E2=80=94=20handler=20no=20longer=20exists?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Drops HandlerEndLiveActivityTests and all end_live_activity routing tests. clear_notification with a tag covers the dismissal path. Co-Authored-By: Claude Sonnet 4.6 --- .../HandlerLiveActivityTests.swift | 111 ------------------ .../LiveActivityContractTests.swift | 9 -- ...tionsCommandManagerLiveActivityTests.swift | 27 +---- 3 files changed, 1 insertion(+), 146 deletions(-) diff --git a/Tests/Shared/LiveActivity/HandlerLiveActivityTests.swift b/Tests/Shared/LiveActivity/HandlerLiveActivityTests.swift index 5f563cdee1..a22b77b5bd 100644 --- a/Tests/Shared/LiveActivity/HandlerLiveActivityTests.swift +++ b/Tests/Shared/LiveActivity/HandlerLiveActivityTests.swift @@ -216,115 +216,4 @@ final class HandlerStartOrUpdateLiveActivityTests: XCTestCase { } } -// MARK: - HandlerEndLiveActivity Tests - -@available(iOS 17.2, *) -final class HandlerEndLiveActivityTests: XCTestCase { - private var sut: HandlerEndLiveActivity! - private var mockRegistry: MockLiveActivityRegistry! - - override func setUp() { - super.setUp() - sut = HandlerEndLiveActivity() - mockRegistry = MockLiveActivityRegistry() - Current.liveActivityRegistry = mockRegistry - Current.isAppExtension = false - } - - override func tearDown() { - sut = nil - mockRegistry = nil - super.tearDown() - } - - // MARK: - App extension guard - - func testHandle_inAppExtension_skipsRegistryAndFulfills() { - Current.isAppExtension = true - let payload: [String: Any] = ["tag": "test-tag"] - XCTAssertNoThrow(try hang(sut.handle(payload))) - XCTAssertTrue(mockRegistry.endCalls.isEmpty) - } - - // MARK: - Tag validation - - func testHandle_missingTag_fulfillsWithoutCallingRegistry() { - XCTAssertNoThrow(try hang(sut.handle([:]))) - XCTAssertTrue(mockRegistry.endCalls.isEmpty) - } - - func testHandle_emptyTag_fulfillsWithoutCallingRegistry() { - let payload: [String: Any] = ["tag": ""] - XCTAssertNoThrow(try hang(sut.handle(payload))) - XCTAssertTrue(mockRegistry.endCalls.isEmpty) - } - - func testHandle_invalidTag_fulfillsWithoutCallingRegistry() { - let payload: [String: Any] = ["tag": "bad tag!"] - XCTAssertNoThrow(try hang(sut.handle(payload))) - XCTAssertTrue(mockRegistry.endCalls.isEmpty) - } - - // MARK: - Dismissal policy - - func testHandle_noDismissalPolicy_usesImmediate() { - let payload: [String: Any] = ["tag": "end-tag"] - XCTAssertNoThrow(try hang(sut.handle(payload))) - XCTAssertEqual(mockRegistry.endCalls.count, 1) - XCTAssertEqual(mockRegistry.endCalls[0].tag, "end-tag") - XCTAssertTrue(mockRegistry.endCalls[0].policyIsImmediate) - } - - func testHandle_defaultDismissalPolicy_usesDefault() { - let payload: [String: Any] = ["tag": "end-tag", "dismissal_policy": "default"] - XCTAssertNoThrow(try hang(sut.handle(payload))) - XCTAssertEqual(mockRegistry.endCalls.count, 1) - XCTAssertTrue(mockRegistry.endCalls[0].policyIsDefault) - } - - func testHandle_afterDismissalPolicy_usesAfterPolicy() { - let future = Date().addingTimeInterval(60) - let payload: [String: Any] = [ - "tag": "end-tag", - "dismissal_policy": "after:\(future.timeIntervalSince1970)", - ] - XCTAssertNoThrow(try hang(sut.handle(payload))) - XCTAssertEqual(mockRegistry.endCalls.count, 1) - // Verify an .after policy was chosen (not .immediate or .default) - XCTAssertTrue(mockRegistry.endCalls[0].policyIsAfter) - // Verify the stored policy matches the expected date (ActivityUIDismissalPolicy is Equatable) - let expectedDate = Date(timeIntervalSince1970: future.timeIntervalSince1970) - XCTAssertEqual(mockRegistry.endCalls[0].policy, .after(expectedDate)) - } - - func testHandle_afterDismissalPolicy_capsAt24Hours() { - // A timestamp 48 hours in the future should be capped to ≤24 hours - let farFuture = Date().addingTimeInterval(48 * 60 * 60) - let payload: [String: Any] = [ - "tag": "end-tag", - "dismissal_policy": "after:\(farFuture.timeIntervalSince1970)", - ] - XCTAssertNoThrow(try hang(sut.handle(payload))) - XCTAssertEqual(mockRegistry.endCalls.count, 1) - let call = mockRegistry.endCalls[0] - // The policy should be .after (not .immediate), confirming it wasn't discarded - XCTAssertTrue(call.policyIsAfter) - // The stored date must not equal the uncapped far-future date - XCTAssertNotEqual(call.policy, .after(Date(timeIntervalSince1970: farFuture.timeIntervalSince1970))) - } - - func testHandle_afterDismissalPolicyWithInvalidTimestamp_usesImmediate() { - let payload: [String: Any] = ["tag": "end-tag", "dismissal_policy": "after:not-a-number"] - XCTAssertNoThrow(try hang(sut.handle(payload))) - XCTAssertEqual(mockRegistry.endCalls.count, 1) - XCTAssertTrue(mockRegistry.endCalls[0].policyIsImmediate) - } - - func testHandle_unknownDismissalPolicy_usesImmediate() { - let payload: [String: Any] = ["tag": "end-tag", "dismissal_policy": "unknown"] - XCTAssertNoThrow(try hang(sut.handle(payload))) - XCTAssertEqual(mockRegistry.endCalls.count, 1) - XCTAssertTrue(mockRegistry.endCalls[0].policyIsImmediate) - } -} #endif diff --git a/Tests/Shared/LiveActivity/LiveActivityContractTests.swift b/Tests/Shared/LiveActivity/LiveActivityContractTests.swift index 1f4e3a631a..81c2f85305 100644 --- a/Tests/Shared/LiveActivity/LiveActivityContractTests.swift +++ b/Tests/Shared/LiveActivity/LiveActivityContractTests.swift @@ -138,15 +138,6 @@ final class LiveActivityContractTests: XCTestCase { ] as [String: Any], ] XCTAssertNoThrow(try hang(manager.handle(liveActivityPayload))) - - // "end_live_activity" command must route successfully - let endPayload: [AnyHashable: Any] = [ - "homeassistant": [ - "command": "end_live_activity", - "tag": "test", - ] as [String: Any], - ] - XCTAssertNoThrow(try hang(manager.handle(endPayload))) } /// The `live_update: true` data flag must be recognized (same field as Android Live Updates). diff --git a/Tests/Shared/LiveActivity/NotificationsCommandManagerLiveActivityTests.swift b/Tests/Shared/LiveActivity/NotificationsCommandManagerLiveActivityTests.swift index 8d5be00ab1..a70c786709 100644 --- a/Tests/Shared/LiveActivity/NotificationsCommandManagerLiveActivityTests.swift +++ b/Tests/Shared/LiveActivity/NotificationsCommandManagerLiveActivityTests.swift @@ -7,8 +7,7 @@ import XCTest /// Tests for the two live-activity routing paths in `NotificationCommandManager`: /// 1. `homeassistant.command == "live_activity"` — explicit command key /// 2. `homeassistant.live_update == true` — data flag (Android-compat pattern) -/// 3. `homeassistant.command == "end_live_activity"` — end command -/// 4. `homeassistant.command == "clear_notification"` with a `tag` — dismisses live activity +/// 3. `homeassistant.command == "clear_notification"` with a `tag` — dismisses live activity @available(iOS 17.2, *) final class NotificationsCommandManagerLiveActivityTests: XCTestCase { private var sut: NotificationCommandManager! @@ -77,30 +76,6 @@ final class NotificationsCommandManagerLiveActivityTests: XCTestCase { XCTAssertTrue(mockRegistry.startOrUpdateCalls.isEmpty) } - // MARK: - end_live_activity command - - func testHandle_endLiveActivityCommand_callsRegistryEnd() { - let payload = makePayload([ - "command": "end_live_activity", - "tag": "end-me", - ]) - XCTAssertNoThrow(try hang(sut.handle(payload))) - XCTAssertEqual(mockRegistry.endCalls.count, 1) - XCTAssertEqual(mockRegistry.endCalls[0].tag, "end-me") - XCTAssertTrue(mockRegistry.endCalls[0].policyIsImmediate) - } - - func testHandle_endLiveActivityCommand_withDefaultPolicy_callsRegistryEndWithDefaultPolicy() { - let payload = makePayload([ - "command": "end_live_activity", - "tag": "end-me", - "dismissal_policy": "default", - ]) - XCTAssertNoThrow(try hang(sut.handle(payload))) - XCTAssertEqual(mockRegistry.endCalls.count, 1) - XCTAssertTrue(mockRegistry.endCalls[0].policyIsDefault) - } - // MARK: - clear_notification also ends live activity // NOTE: testHandle_clearNotificationWithTag_callsRegistryEnd is intentionally omitted. From 7cfa0e3143d882a2b71a4203d3038ddfc105c902 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bruno=20Pantale=C3=A3o?= <5808343+bgoncal@users.noreply.github.com> Date: Wed, 27 May 2026 11:56:59 +0200 Subject: [PATCH 10/15] Rename live activity tags --- Sources/Shared/API/HAAPI.swift | 2 +- Sources/Shared/LiveActivity/LiveActivityRegistry.swift | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Sources/Shared/API/HAAPI.swift b/Sources/Shared/API/HAAPI.swift index 2b842d4ee9..d19bc1892f 100644 --- a/Sources/Shared/API/HAAPI.swift +++ b/Sources/Shared/API/HAAPI.swift @@ -612,7 +612,7 @@ public class HomeAssistantAPI { // Push-to-start token (stored in Keychain at launch, updated via stream). // The relay server uses this token to start a Live Activity entirely via APNs. if let pushToStartToken = LiveActivityRegistry.storedPushToStartToken { - appData["live_activity_push_to_start_token"] = pushToStartToken + appData["live_activity_start_token"] = pushToStartToken appData["live_activity_push_to_start_apns_environment"] = Current.apnsEnvironment } } diff --git a/Sources/Shared/LiveActivity/LiveActivityRegistry.swift b/Sources/Shared/LiveActivity/LiveActivityRegistry.swift index c0775fe813..af39b0e173 100644 --- a/Sources/Shared/LiveActivity/LiveActivityRegistry.swift +++ b/Sources/Shared/LiveActivity/LiveActivityRegistry.swift @@ -43,7 +43,7 @@ public actor LiveActivityRegistry: LiveActivityRegistryProtocol { /// Webhook type for reporting that a Live Activity was dismissed. static let webhookTypeDismissed = "mobile_app_live_activity_dismissed" /// Keys in the dismissed webhook request data dictionary. - static let dismissedWebhookKeys: Set = ["activity_id", "live_activity_tag", "reason"] + static let dismissedWebhookKeys: Set = ["activity_id", "tag", "reason"] // MARK: - State @@ -265,7 +265,7 @@ public actor LiveActivityRegistry: LiveActivityRegistryProtocol { AppConstants.Keychain[pushToStartTokenKeychainKey] } - static let pushToStartTokenKeychainKey = "live_activity_push_to_start_token" + static let pushToStartTokenKeychainKey = "live_activity_start_token" // MARK: - Private — Stale Date @@ -354,7 +354,7 @@ public actor LiveActivityRegistry: LiveActivityRegistryProtocol { type: Self.webhookTypeDismissed, data: [ "activity_id": activityID, - "live_activity_tag": tag, + "tag": tag, "reason": reason, ] ) From f81dac24befecd5deff0dafa522780a3e05ac3af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bruno=20Pantale=C3=A3o?= <5808343+bgoncal@users.noreply.github.com> Date: Wed, 27 May 2026 15:16:28 +0200 Subject: [PATCH 11/15] Fix remote Live Activity token lifecycle --- Sources/App/AppDelegate.swift | 6 +++ Sources/Shared/API/HAAPI.swift | 2 +- .../LiveActivity/LiveActivityRegistry.swift | 41 ++++++++++++------- .../LiveActivityContractTests.swift | 4 +- 4 files changed, 35 insertions(+), 18 deletions(-) diff --git a/Sources/App/AppDelegate.swift b/Sources/App/AppDelegate.swift index 7c10e2a8a2..3c797690f1 100644 --- a/Sources/App/AppDelegate.swift +++ b/Sources/App/AppDelegate.swift @@ -407,6 +407,12 @@ class AppDelegate: UIResponder, UIApplicationDelegate { Task { await registry.startObservingPushToStartToken() } + + // Observe activities that ActivityKit starts directly from APNs push-to-start. + // The stream is infinite; this Task is kept alive for the app's lifetime. + Task { + await registry.startObservingRemoteActivityStarts() + } } #endif } diff --git a/Sources/Shared/API/HAAPI.swift b/Sources/Shared/API/HAAPI.swift index 133f4fb857..b3554d1fab 100644 --- a/Sources/Shared/API/HAAPI.swift +++ b/Sources/Shared/API/HAAPI.swift @@ -602,7 +602,7 @@ public class HomeAssistantAPI { // Push-to-start token (stored in Keychain at launch, updated via stream). // The relay server uses this token to start a Live Activity entirely via APNs. if let pushToStartToken = LiveActivityRegistry.storedPushToStartToken { - appData["live_activity_start_token"] = pushToStartToken + appData["live_activity_token"] = pushToStartToken appData["live_activity_push_to_start_apns_environment"] = Current.apnsEnvironment } } diff --git a/Sources/Shared/LiveActivity/LiveActivityRegistry.swift b/Sources/Shared/LiveActivity/LiveActivityRegistry.swift index 339f70e9fc..2e1a2863ef 100644 --- a/Sources/Shared/LiveActivity/LiveActivityRegistry.swift +++ b/Sources/Shared/LiveActivity/LiveActivityRegistry.swift @@ -15,6 +15,8 @@ public protocol LiveActivityRegistryProtocol: AnyObject { func reattach() async @available(iOS 17.2, *) func startObservingPushToStartToken() async + @available(iOS 17.2, *) + func startObservingRemoteActivityStarts() async } /// Thread-safe registry for active `Activity` instances. @@ -38,12 +40,12 @@ public actor LiveActivityRegistry: LiveActivityRegistryProtocol { /// Webhook type for reporting a new per-activity push token to HA. static let webhookTypeToken = "live_activity_token" /// Keys in the token webhook request data dictionary. - static let tokenWebhookKeys: Set = ["activity_id", "push_token", "apns_environment"] + static let tokenWebhookKeys: Set = ["tag", "push_token"] /// Webhook type for reporting that a Live Activity was dismissed. static let webhookTypeDismissed = "live_activity_dismissed" /// Keys in the dismissed webhook request data dictionary. - static let dismissedWebhookKeys: Set = ["activity_id", "tag", "reason"] + static let dismissedWebhookKeys: Set = ["tag"] // MARK: - State @@ -257,6 +259,22 @@ public actor LiveActivityRegistry: LiveActivityRegistryProtocol { } } + /// Observe activities started remotely by ActivityKit push-to-start notifications. + /// + /// When APNs starts a Live Activity without launching through the notification command + /// handler, ActivityKit delivers the created activity through this stream. Attach our + /// normal push-token and lifecycle observers so Core can receive the per-activity token. + public func startObservingRemoteActivityStarts() async { + for await activity in Activity.activityUpdates { + let tag = activity.attributes.tag + guard entries[tag] == nil else { continue } + + let observationTask = makeObservationTask(for: activity) + entries[tag] = Entry(activity: activity, observationTask: observationTask) + Current.Log.verbose("LiveActivityRegistry: observed remotely started activity for tag \(tag), id=\(activity.id)") + } + } + // MARK: - Public Helpers /// The stored push-to-start token for inclusion in registration payloads. @@ -265,7 +283,7 @@ public actor LiveActivityRegistry: LiveActivityRegistryProtocol { AppConstants.Keychain[pushToStartTokenKeychainKey] } - static let pushToStartTokenKeychainKey = "live_activity_start_token" + static let pushToStartTokenKeychainKey = "live_activity_token" // MARK: - Private — Stale Date @@ -299,7 +317,7 @@ public actor LiveActivityRegistry: LiveActivityRegistryProtocol { Current.Log.verbose( "LiveActivityRegistry: new push token for tag \(activity.attributes.tag)" ) - await self.reportPushToken(tokenHex, activityID: activity.id) + await self.reportPushToken(tokenHex, tag: activity.attributes.tag) } } @@ -308,11 +326,7 @@ public actor LiveActivityRegistry: LiveActivityRegistryProtocol { for await state in activity.activityStateUpdates { switch state { case .dismissed, .ended: - await self.reportActivityDismissed( - activityID: activity.id, - tag: activity.attributes.tag, - reason: state == .dismissed ? "user_dismissed" : "ended" - ) + await self.reportActivityDismissed(tag: activity.attributes.tag) _ = await self.remove(id: activity.attributes.tag) return case .active, .stale: @@ -333,13 +347,12 @@ public actor LiveActivityRegistry: LiveActivityRegistryProtocol { /// Report a new activity push token to all connected HA servers. /// The token is used by the relay server to send APNs updates directly to this activity. - private func reportPushToken(_ tokenHex: String, activityID: String) async { + private func reportPushToken(_ tokenHex: String, tag: String) async { let request = WebhookRequest( type: Self.webhookTypeToken, data: [ - "activity_id": activityID, + "tag": tag, "push_token": tokenHex, - "apns_environment": Current.apnsEnvironment, ] ) for server in Current.servers.all { @@ -349,13 +362,11 @@ public actor LiveActivityRegistry: LiveActivityRegistryProtocol { /// Notify HA servers that the Live Activity was dismissed or ended externally. /// This allows HA to stop sending APNs updates for this activity. - private func reportActivityDismissed(activityID: String, tag: String, reason: String) async { + private func reportActivityDismissed(tag: String) async { let request = WebhookRequest( type: Self.webhookTypeDismissed, data: [ - "activity_id": activityID, "tag": tag, - "reason": reason, ] ) for server in Current.servers.all { diff --git a/Tests/Shared/LiveActivity/LiveActivityContractTests.swift b/Tests/Shared/LiveActivity/LiveActivityContractTests.swift index a3c24509ff..c28c2373cc 100644 --- a/Tests/Shared/LiveActivity/LiveActivityContractTests.swift +++ b/Tests/Shared/LiveActivity/LiveActivityContractTests.swift @@ -99,7 +99,7 @@ final class LiveActivityContractTests: XCTestCase { func testTokenWebhookKeys_areFrozen() { XCTAssertEqual( LiveActivityRegistry.tokenWebhookKeys, - ["activity_id", "push_token", "apns_environment"] + ["tag", "push_token"] ) } @@ -117,7 +117,7 @@ final class LiveActivityContractTests: XCTestCase { func testDismissedWebhookKeys_areFrozen() { XCTAssertEqual( LiveActivityRegistry.dismissedWebhookKeys, - ["activity_id", "live_activity_tag", "reason"] + ["tag"] ) } From 1d06f66862301bc426da49cd23cfb233c85bc088 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bruno=20Pantale=C3=A3o?= <5808343+bgoncal@users.noreply.github.com> Date: Wed, 27 May 2026 15:23:21 +0200 Subject: [PATCH 12/15] Remove Live Activity localization churn --- Sources/App/Resources/bg.lproj/Localizable.strings | 3 +++ .../App/Resources/ca-ES.lproj/Localizable.strings | 3 +++ Sources/App/Resources/cs.lproj/Localizable.strings | 3 +++ .../App/Resources/cy-GB.lproj/Localizable.strings | 3 +++ Sources/App/Resources/da.lproj/Localizable.strings | 3 +++ Sources/App/Resources/de.lproj/Localizable.strings | 3 +++ Sources/App/Resources/el.lproj/Localizable.strings | 3 +++ .../App/Resources/en-GB.lproj/Localizable.strings | 3 +++ Sources/App/Resources/en.lproj/Localizable.strings | 4 +++- .../App/Resources/es-ES.lproj/Localizable.strings | 3 +++ .../App/Resources/es-MX.lproj/Localizable.strings | 3 +++ Sources/App/Resources/es.lproj/Localizable.strings | 3 +++ Sources/App/Resources/et.lproj/Localizable.strings | 3 +++ Sources/App/Resources/fi.lproj/Localizable.strings | 3 +++ Sources/App/Resources/fr.lproj/Localizable.strings | 3 +++ Sources/App/Resources/he.lproj/Localizable.strings | 3 +++ Sources/App/Resources/hu.lproj/Localizable.strings | 6 ++++-- Sources/App/Resources/id.lproj/Localizable.strings | 3 +++ Sources/App/Resources/it.lproj/Localizable.strings | 3 +++ Sources/App/Resources/ja.lproj/Localizable.strings | 3 +++ .../App/Resources/ko-KR.lproj/Localizable.strings | 6 ++++-- Sources/App/Resources/ml.lproj/Localizable.strings | 3 +++ Sources/App/Resources/nb.lproj/Localizable.strings | 3 +++ Sources/App/Resources/nl.lproj/Localizable.strings | 3 +++ .../App/Resources/pl-PL.lproj/Localizable.strings | 3 +++ .../App/Resources/pt-BR.lproj/Localizable.strings | 3 +++ Sources/App/Resources/ru.lproj/Localizable.strings | 6 ++++-- Sources/App/Resources/sl.lproj/Localizable.strings | 3 +++ Sources/App/Resources/sv.lproj/Localizable.strings | 6 ++++-- Sources/App/Resources/tr.lproj/Localizable.strings | 3 +++ Sources/App/Resources/uk.lproj/Localizable.strings | 3 +++ Sources/App/Resources/vi.lproj/Localizable.strings | 3 +++ .../App/Resources/zh-Hans.lproj/Localizable.strings | 3 +++ .../App/Resources/zh-Hant.lproj/Localizable.strings | 3 +++ .../LiveActivity/LiveActivitySettingsView.swift | 4 ++-- Sources/Shared/Resources/Swiftgen/Strings.swift | 12 ++++++++++-- 36 files changed, 118 insertions(+), 13 deletions(-) diff --git a/Sources/App/Resources/bg.lproj/Localizable.strings b/Sources/App/Resources/bg.lproj/Localizable.strings index 8b2577b95d..e9b528ed1a 100644 --- a/Sources/App/Resources/bg.lproj/Localizable.strings +++ b/Sources/App/Resources/bg.lproj/Localizable.strings @@ -559,8 +559,11 @@ This server requires a client certificate (mTLS) but the operation was cancelled "live_activity.end_all.button" = "End All Activities"; "live_activity.end_all.confirm.button" = "End All"; "live_activity.end_all.confirm.title" = "End all Live Activities?"; +"live_activity.frequent_updates.footer" = "Allows Home Assistant to update Live Activities up to once per second. Enable in Settings \u203A %@ \u203A 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.section.active" = "Active Activities"; +"live_activity.section.privacy" = "Privacy"; "live_activity.section.status" = "Status"; "live_activity.status.enabled" = "Enabled"; "live_activity.status.open_settings" = "Open Settings"; diff --git a/Sources/App/Resources/ca-ES.lproj/Localizable.strings b/Sources/App/Resources/ca-ES.lproj/Localizable.strings index 9c899a74f5..d4707b9b34 100644 --- a/Sources/App/Resources/ca-ES.lproj/Localizable.strings +++ b/Sources/App/Resources/ca-ES.lproj/Localizable.strings @@ -559,8 +559,11 @@ This server requires a client certificate (mTLS) but the operation was cancelled "live_activity.end_all.button" = "End All Activities"; "live_activity.end_all.confirm.button" = "End All"; "live_activity.end_all.confirm.title" = "End all Live Activities?"; +"live_activity.frequent_updates.footer" = "Allows Home Assistant to update Live Activities up to once per second. Enable in Settings \u203A %@ \u203A 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.section.active" = "Active Activities"; +"live_activity.section.privacy" = "Privacy"; "live_activity.section.status" = "Status"; "live_activity.status.enabled" = "Enabled"; "live_activity.status.open_settings" = "Open Settings"; diff --git a/Sources/App/Resources/cs.lproj/Localizable.strings b/Sources/App/Resources/cs.lproj/Localizable.strings index b98995e9fa..eef5f2cc47 100644 --- a/Sources/App/Resources/cs.lproj/Localizable.strings +++ b/Sources/App/Resources/cs.lproj/Localizable.strings @@ -559,8 +559,11 @@ Tento server vyžaduje klientský certifikát (mTLS), ale operace byla zrušena. "live_activity.end_all.button" = "Ukončit všechny aktivity"; "live_activity.end_all.confirm.button" = "Ukončit vše"; "live_activity.end_all.confirm.title" = "Ukončit všechny živé aktivity?"; +"live_activity.frequent_updates.footer" = "Umožňuje Home Assistantovi aktualizovat živé aktivity až jednou za sekundu. Povolte v Nastavení. \u203A %@ \u203A Živé aktivity."; "live_activity.frequent_updates.title" = "Časté aktualizace"; +"live_activity.privacy.message" = "Obsah živých aktivit je viditelný na zamykací obrazovce a v Dynamic Islandu bez použití Face ID nebo Touch ID. Pečlivě si vyberte, co chcete zobrazit."; "live_activity.section.active" = "Aktivní aktivity"; +"live_activity.section.privacy" = "Soukromí"; "live_activity.section.status" = "Stav"; "live_activity.status.enabled" = "Povoleno"; "live_activity.status.open_settings" = "Otevřít Nastavení"; diff --git a/Sources/App/Resources/cy-GB.lproj/Localizable.strings b/Sources/App/Resources/cy-GB.lproj/Localizable.strings index 092bd698e1..8b4c44d0f5 100644 --- a/Sources/App/Resources/cy-GB.lproj/Localizable.strings +++ b/Sources/App/Resources/cy-GB.lproj/Localizable.strings @@ -559,8 +559,11 @@ This server requires a client certificate (mTLS) but the operation was cancelled "live_activity.end_all.button" = "End All Activities"; "live_activity.end_all.confirm.button" = "End All"; "live_activity.end_all.confirm.title" = "End all Live Activities?"; +"live_activity.frequent_updates.footer" = "Allows Home Assistant to update Live Activities up to once per second. Enable in Settings \u203A %@ \u203A 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.section.active" = "Active Activities"; +"live_activity.section.privacy" = "Privacy"; "live_activity.section.status" = "Status"; "live_activity.status.enabled" = "Enabled"; "live_activity.status.open_settings" = "Open Settings"; diff --git a/Sources/App/Resources/da.lproj/Localizable.strings b/Sources/App/Resources/da.lproj/Localizable.strings index 78355ab0ae..a8f8815a9f 100644 --- a/Sources/App/Resources/da.lproj/Localizable.strings +++ b/Sources/App/Resources/da.lproj/Localizable.strings @@ -559,8 +559,11 @@ This server requires a client certificate (mTLS) but the operation was cancelled "live_activity.end_all.button" = "Afslut alle aktiviteter"; "live_activity.end_all.confirm.button" = "Afslut alle"; "live_activity.end_all.confirm.title" = "Afslut alle liveaktiviteter?"; +"live_activity.frequent_updates.footer" = "Tillader Home Assistant at opdatere Liveaktiviteter op til én gang i sekundet. Aktivér i Indstillinger \u203A %@ \u203A Liveaktiviteter."; "live_activity.frequent_updates.title" = "Hyppige opdateringer"; +"live_activity.privacy.message" = "Liveaktivitet-indhold er synligt på din låseskærm og Dynamic Island uden Face ID eller Touch ID. Vælg omhyggeligt, hvad du vil vise."; "live_activity.section.active" = "Aktive aktiviteter"; +"live_activity.section.privacy" = "Privatliv"; "live_activity.section.status" = "Status"; "live_activity.status.enabled" = "Aktiveret"; "live_activity.status.open_settings" = "Åbn indstillinger"; diff --git a/Sources/App/Resources/de.lproj/Localizable.strings b/Sources/App/Resources/de.lproj/Localizable.strings index a2965bae86..c357bd4387 100644 --- a/Sources/App/Resources/de.lproj/Localizable.strings +++ b/Sources/App/Resources/de.lproj/Localizable.strings @@ -559,8 +559,11 @@ Dieser Server benötigt ein Clientzertifikat (mTLS), aber der Vorgang wurde abge "live_activity.end_all.button" = "Alle Aktivitäten beenden"; "live_activity.end_all.confirm.button" = "Alle beenden"; "live_activity.end_all.confirm.title" = "Alle Live-Aktivitäten beenden?"; +"live_activity.frequent_updates.footer" = "Ermöglicht es Home Assistant, Live-Aktivitäten bis zu einmal pro Sekunde zu aktualisieren. In Einstellungen \u203A %@ \u203A Live-Aktivitäten aktiveren."; "live_activity.frequent_updates.title" = "Häufige Aktualisierungen"; +"live_activity.privacy.message" = "Inhalte für Live-Aktivität sind auf deinem Sperrbildschirm und Dynamic Island auch ohne Face ID oder Touch ID sichtbar. Wähle die angezeigten Inhalte sorgfältig aus."; "live_activity.section.active" = "Aktive Aktivitäten"; +"live_activity.section.privacy" = "Datenschutz"; "live_activity.section.status" = "Status"; "live_activity.status.enabled" = "Aktiviert"; "live_activity.status.open_settings" = "Einstellungen öffnen"; diff --git a/Sources/App/Resources/el.lproj/Localizable.strings b/Sources/App/Resources/el.lproj/Localizable.strings index 9aa380eab4..a9a301cfaa 100644 --- a/Sources/App/Resources/el.lproj/Localizable.strings +++ b/Sources/App/Resources/el.lproj/Localizable.strings @@ -559,8 +559,11 @@ "live_activity.end_all.button" = "Τερματισμός όλων των δραστηριοτήτων"; "live_activity.end_all.confirm.button" = "Τερματισμός όλων"; "live_activity.end_all.confirm.title" = "Τερματισμός όλων των ζωντανών δραστηριοτήτων;"; +"live_activity.frequent_updates.footer" = "Επιτρέπει στο Home Assistant να ενημερώνει τις Ζωντανές Δραστηριότητες έως και μία φορά ανά δευτερόλεπτο. Ενεργοποίηση στις Ρυθμίσεις \u203A %@ \u203A Ζωντανές Δραστηριότητες."; "live_activity.frequent_updates.title" = "Συχνές ενημερώσεις"; +"live_activity.privacy.message" = "Το περιεχόμενο της Ζωντανής Δραστηριότητας είναι ορατό στην Οθόνη Κλειδώματος και στο Dynamic Island χωρίς Face ID ή Touch ID. Επιλέξτε προσεκτικά τι θα εμφανίσετε."; "live_activity.section.active" = "Ενεργές δραστηριότητες"; +"live_activity.section.privacy" = "Απόρρητο"; "live_activity.section.status" = "Κατάσταση"; "live_activity.status.enabled" = "Ενεργό"; "live_activity.status.open_settings" = "Ανοιγμα ρυθμίσεων"; diff --git a/Sources/App/Resources/en-GB.lproj/Localizable.strings b/Sources/App/Resources/en-GB.lproj/Localizable.strings index 504ae493f3..8d5615ad1c 100644 --- a/Sources/App/Resources/en-GB.lproj/Localizable.strings +++ b/Sources/App/Resources/en-GB.lproj/Localizable.strings @@ -559,8 +559,11 @@ This server requires a client certificate (mTLS) but the operation was cancelled "live_activity.end_all.button" = "End All Activities"; "live_activity.end_all.confirm.button" = "End All"; "live_activity.end_all.confirm.title" = "End all Live Activities?"; +"live_activity.frequent_updates.footer" = "Allows Home Assistant to update Live Activities up to once per second. Enable in Settings \u203A %@ \u203A 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.section.active" = "Active Activities"; +"live_activity.section.privacy" = "Privacy"; "live_activity.section.status" = "Status"; "live_activity.status.enabled" = "Enabled"; "live_activity.status.open_settings" = "Open Settings"; diff --git a/Sources/App/Resources/en.lproj/Localizable.strings b/Sources/App/Resources/en.lproj/Localizable.strings index 67d65e9071..a8270f18ce 100644 --- a/Sources/App/Resources/en.lproj/Localizable.strings +++ b/Sources/App/Resources/en.lproj/Localizable.strings @@ -559,11 +559,13 @@ This server requires a client certificate (mTLS) but the operation was cancelled "live_activity.end_all.button" = "End All Activities"; "live_activity.end_all.confirm.button" = "End All"; "live_activity.end_all.confirm.title" = "End all Live Activities?"; +"live_activity.frequent_updates.footer" = "Allows Home Assistant to update Live Activities up to once per second. Enable in Settings \u203A %@ \u203A 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.section.active" = "Active Activities"; +"live_activity.section.privacy" = "Privacy"; "live_activity.section.status" = "Status"; "live_activity.status.enabled" = "Enabled"; -"live_activity.status.not_supported" = "Not available on iPad"; "live_activity.status.open_settings" = "Open Settings"; "live_activity.subtitle" = "Real-time Home Assistant updates on your Lock Screen and Dynamic Island."; "live_activity.title" = "Live Activities"; diff --git a/Sources/App/Resources/es-ES.lproj/Localizable.strings b/Sources/App/Resources/es-ES.lproj/Localizable.strings index a471c63849..1932ebc739 100644 --- a/Sources/App/Resources/es-ES.lproj/Localizable.strings +++ b/Sources/App/Resources/es-ES.lproj/Localizable.strings @@ -559,8 +559,11 @@ This server requires a client certificate (mTLS) but the operation was cancelled "live_activity.end_all.button" = "End All Activities"; "live_activity.end_all.confirm.button" = "End All"; "live_activity.end_all.confirm.title" = "End all Live Activities?"; +"live_activity.frequent_updates.footer" = "Allows Home Assistant to update Live Activities up to once per second. Enable in Settings \u203A %@ \u203A 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.section.active" = "Active Activities"; +"live_activity.section.privacy" = "Privacy"; "live_activity.section.status" = "Status"; "live_activity.status.enabled" = "Enabled"; "live_activity.status.open_settings" = "Open Settings"; diff --git a/Sources/App/Resources/es-MX.lproj/Localizable.strings b/Sources/App/Resources/es-MX.lproj/Localizable.strings index 5ddc6080b5..7dfd4273fe 100644 --- a/Sources/App/Resources/es-MX.lproj/Localizable.strings +++ b/Sources/App/Resources/es-MX.lproj/Localizable.strings @@ -559,8 +559,11 @@ This server requires a client certificate (mTLS) but the operation was cancelled "live_activity.end_all.button" = "End All Activities"; "live_activity.end_all.confirm.button" = "End All"; "live_activity.end_all.confirm.title" = "End all Live Activities?"; +"live_activity.frequent_updates.footer" = "Allows Home Assistant to update Live Activities up to once per second. Enable in Settings \u203A %@ \u203A 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.section.active" = "Active Activities"; +"live_activity.section.privacy" = "Privacy"; "live_activity.section.status" = "Status"; "live_activity.status.enabled" = "Enabled"; "live_activity.status.open_settings" = "Open Settings"; diff --git a/Sources/App/Resources/es.lproj/Localizable.strings b/Sources/App/Resources/es.lproj/Localizable.strings index 75fe3b0208..7f1f714eba 100644 --- a/Sources/App/Resources/es.lproj/Localizable.strings +++ b/Sources/App/Resources/es.lproj/Localizable.strings @@ -559,8 +559,11 @@ This server requires a client certificate (mTLS) but the operation was cancelled "live_activity.end_all.button" = "End All Activities"; "live_activity.end_all.confirm.button" = "End All"; "live_activity.end_all.confirm.title" = "End all Live Activities?"; +"live_activity.frequent_updates.footer" = "Allows Home Assistant to update Live Activities up to once per second. Enable in Settings \u203A %@ \u203A 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.section.active" = "Active Activities"; +"live_activity.section.privacy" = "Privacy"; "live_activity.section.status" = "Status"; "live_activity.status.enabled" = "Enabled"; "live_activity.status.open_settings" = "Open Settings"; diff --git a/Sources/App/Resources/et.lproj/Localizable.strings b/Sources/App/Resources/et.lproj/Localizable.strings index 92f81062f2..caacb3221b 100644 --- a/Sources/App/Resources/et.lproj/Localizable.strings +++ b/Sources/App/Resources/et.lproj/Localizable.strings @@ -559,8 +559,11 @@ "live_activity.end_all.button" = "Lõpeta kõik tegevused"; "live_activity.end_all.confirm.button" = "Lõpeta kõik"; "live_activity.end_all.confirm.title" = "Kas lõpetada kõik Live Activity tegevused?"; +"live_activity.frequent_updates.footer" = "Lubab Home Assistantil reaalajas tegevusi kuni üks kord sekundis värskendada. Luba seadetes. \u203A %@ \u203A Otseülekanded."; "live_activity.frequent_updates.title" = "Sagedased uuendused"; +"live_activity.privacy.message" = "Live Activity sisu on nähtav lukustusekraanil ja dünaamilisel saarel ilma Face ID või Touch ID-ta. Vali hoolikalt, mida kuvada."; "live_activity.section.active" = "Aktiivsed tegevused"; +"live_activity.section.privacy" = "Privaatsus"; "live_activity.section.status" = "Olek"; "live_activity.status.enabled" = "Lubatud"; "live_activity.status.open_settings" = "Ava seaded"; diff --git a/Sources/App/Resources/fi.lproj/Localizable.strings b/Sources/App/Resources/fi.lproj/Localizable.strings index 6cc57ef87e..02dbc2a395 100644 --- a/Sources/App/Resources/fi.lproj/Localizable.strings +++ b/Sources/App/Resources/fi.lproj/Localizable.strings @@ -559,8 +559,11 @@ This server requires a client certificate (mTLS) but the operation was cancelled "live_activity.end_all.button" = "End All Activities"; "live_activity.end_all.confirm.button" = "End All"; "live_activity.end_all.confirm.title" = "End all Live Activities?"; +"live_activity.frequent_updates.footer" = "Allows Home Assistant to update Live Activities up to once per second. Enable in Settings \u203A %@ \u203A 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.section.active" = "Active Activities"; +"live_activity.section.privacy" = "Privacy"; "live_activity.section.status" = "Status"; "live_activity.status.enabled" = "Enabled"; "live_activity.status.open_settings" = "Open Settings"; diff --git a/Sources/App/Resources/fr.lproj/Localizable.strings b/Sources/App/Resources/fr.lproj/Localizable.strings index 77f3991662..a0e4837d1e 100644 --- a/Sources/App/Resources/fr.lproj/Localizable.strings +++ b/Sources/App/Resources/fr.lproj/Localizable.strings @@ -559,8 +559,11 @@ This server requires a client certificate (mTLS) but the operation was cancelled "live_activity.end_all.button" = "End All Activities"; "live_activity.end_all.confirm.button" = "End All"; "live_activity.end_all.confirm.title" = "End all Live Activities?"; +"live_activity.frequent_updates.footer" = "Allows Home Assistant to update Live Activities up to once per second. Enable in Settings \u203A %@ \u203A 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.section.active" = "Active Activities"; +"live_activity.section.privacy" = "Privacy"; "live_activity.section.status" = "Status"; "live_activity.status.enabled" = "Enabled"; "live_activity.status.open_settings" = "Open Settings"; diff --git a/Sources/App/Resources/he.lproj/Localizable.strings b/Sources/App/Resources/he.lproj/Localizable.strings index d469ba4094..63c139b49c 100644 --- a/Sources/App/Resources/he.lproj/Localizable.strings +++ b/Sources/App/Resources/he.lproj/Localizable.strings @@ -559,8 +559,11 @@ This server requires a client certificate (mTLS) but the operation was cancelled "live_activity.end_all.button" = "End All Activities"; "live_activity.end_all.confirm.button" = "End All"; "live_activity.end_all.confirm.title" = "End all Live Activities?"; +"live_activity.frequent_updates.footer" = "Allows Home Assistant to update Live Activities up to once per second. Enable in Settings \u203A %@ \u203A 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.section.active" = "Active Activities"; +"live_activity.section.privacy" = "Privacy"; "live_activity.section.status" = "Status"; "live_activity.status.enabled" = "Enabled"; "live_activity.status.open_settings" = "Open Settings"; diff --git a/Sources/App/Resources/hu.lproj/Localizable.strings b/Sources/App/Resources/hu.lproj/Localizable.strings index 41988f3ad3..2471b1c17e 100644 --- a/Sources/App/Resources/hu.lproj/Localizable.strings +++ b/Sources/App/Resources/hu.lproj/Localizable.strings @@ -559,11 +559,13 @@ Ez a szerver kliens tanúsítványt (mTLS) igényel, de a művelet megszakítva. "live_activity.end_all.button" = "Összes aktivitás befejezése"; "live_activity.end_all.confirm.button" = "Összes befejezése"; "live_activity.end_all.confirm.title" = "Befejezi az összes élő aktivitást?"; +"live_activity.frequent_updates.footer" = "Lehetővé teszi a Home Assistant számára, hogy az élő aktivitásokat másodpercenként egyszer frissítse. Engedélyezés: Beállítások › %@ › Élő aktivitások."; "live_activity.frequent_updates.title" = "Gyakori frissítések"; +"live_activity.privacy.message" = "Az élő aktivitás tartalma látható a zárolási képernyőn és a Dynamic Island területen Face ID vagy Touch ID nélkül is. Körültekintően válassza meg, mit jelenít meg."; "live_activity.section.active" = "Aktív aktivitások"; +"live_activity.section.privacy" = "Adatvédelem"; "live_activity.section.status" = "Állapot"; "live_activity.status.enabled" = "Engedélyezve"; -"live_activity.status.not_supported" = "Not available on iPad"; "live_activity.status.open_settings" = "Beállítások megnyitása"; "live_activity.subtitle" = "Valós idejű Home Assistant frissítések a zárolási képernyőn és a Dynamic Island területen."; "live_activity.title" = "Élő aktivitások"; @@ -1738,4 +1740,4 @@ A Home Assistant nyílt forráskódú, az adatvédelem mellett áll ki és helyi "widgets.todo_list.refresh_title" = "Teendőlista frissítése"; "widgets.todo_list.select_list" = "Szerkessze a widgetet a lista kiválasztásához."; "widgets.todo_list.title" = "Teendőlista"; -"yes_label" = "Igen"; +"yes_label" = "Igen"; \ No newline at end of file diff --git a/Sources/App/Resources/id.lproj/Localizable.strings b/Sources/App/Resources/id.lproj/Localizable.strings index 439e6ed941..777063d0b4 100644 --- a/Sources/App/Resources/id.lproj/Localizable.strings +++ b/Sources/App/Resources/id.lproj/Localizable.strings @@ -559,8 +559,11 @@ Server ini memerlukan sertifikat klien (mTLS) tetapi operasi dibatalkan."; "live_activity.end_all.button" = "End All Activities"; "live_activity.end_all.confirm.button" = "End All"; "live_activity.end_all.confirm.title" = "End all Live Activities?"; +"live_activity.frequent_updates.footer" = "Allows Home Assistant to update Live Activities up to once per second. Enable in Settings \u203A %@ \u203A 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.section.active" = "Active Activities"; +"live_activity.section.privacy" = "Privacy"; "live_activity.section.status" = "Status"; "live_activity.status.enabled" = "Enabled"; "live_activity.status.open_settings" = "Open Settings"; diff --git a/Sources/App/Resources/it.lproj/Localizable.strings b/Sources/App/Resources/it.lproj/Localizable.strings index c3b63de9e6..9d3e6339c7 100644 --- a/Sources/App/Resources/it.lproj/Localizable.strings +++ b/Sources/App/Resources/it.lproj/Localizable.strings @@ -559,8 +559,11 @@ Questo server richiede un certificato client (mTLS), ma l'operazione è stata an "live_activity.end_all.button" = "End All Activities"; "live_activity.end_all.confirm.button" = "End All"; "live_activity.end_all.confirm.title" = "End all Live Activities?"; +"live_activity.frequent_updates.footer" = "Consente a Home Assistant di aggiornare le Live Activity fino a una volta al secondo. Attiva nelle Impostazioni \u203A %@ \u203A 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.section.active" = "Active Activities"; +"live_activity.section.privacy" = "Privacy"; "live_activity.section.status" = "Status"; "live_activity.status.enabled" = "Enabled"; "live_activity.status.open_settings" = "Open Settings"; diff --git a/Sources/App/Resources/ja.lproj/Localizable.strings b/Sources/App/Resources/ja.lproj/Localizable.strings index df1974c106..f1b7324540 100644 --- a/Sources/App/Resources/ja.lproj/Localizable.strings +++ b/Sources/App/Resources/ja.lproj/Localizable.strings @@ -559,8 +559,11 @@ This server requires a client certificate (mTLS) but the operation was cancelled "live_activity.end_all.button" = "End All Activities"; "live_activity.end_all.confirm.button" = "End All"; "live_activity.end_all.confirm.title" = "End all Live Activities?"; +"live_activity.frequent_updates.footer" = "Allows Home Assistant to update Live Activities up to once per second. Enable in Settings \u203A %@ \u203A 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.section.active" = "Active Activities"; +"live_activity.section.privacy" = "Privacy"; "live_activity.section.status" = "Status"; "live_activity.status.enabled" = "Enabled"; "live_activity.status.open_settings" = "Open Settings"; diff --git a/Sources/App/Resources/ko-KR.lproj/Localizable.strings b/Sources/App/Resources/ko-KR.lproj/Localizable.strings index 6524b152b6..f6fc215994 100644 --- a/Sources/App/Resources/ko-KR.lproj/Localizable.strings +++ b/Sources/App/Resources/ko-KR.lproj/Localizable.strings @@ -559,11 +559,13 @@ This server requires a client certificate (mTLS) but the operation was cancelled "live_activity.end_all.button" = "End All Activities"; "live_activity.end_all.confirm.button" = "End All"; "live_activity.end_all.confirm.title" = "End all Live Activities?"; +"live_activity.frequent_updates.footer" = "Allows Home Assistant to update Live Activities up to once per second. Enable in Settings \u203A %@ \u203A Live Activities."; "live_activity.frequent_updates.title" = "Frequent Updates"; +"live_activity.privacy.message" = "실시간 현황 콘텐츠는 Face ID 또는 Touch ID 없이도 잠금 화면 및 다이내믹 아일랜드에서 볼 수 있습니다. 표시할 내용을 신중하게 선택하십시오."; "live_activity.section.active" = "Active Activities"; +"live_activity.section.privacy" = "Privacy"; "live_activity.section.status" = "Status"; "live_activity.status.enabled" = "Enabled"; -"live_activity.status.not_supported" = "Not available on iPad"; "live_activity.status.open_settings" = "Open Settings"; "live_activity.subtitle" = "잠금 화면 및 다이내믹 아일랜드에서 확인하는 Home Assistant 실시간 업데이트"; "live_activity.title" = "실시간 현황"; @@ -1739,4 +1741,4 @@ Home Assistant is open source, advocates for privacy and runs locally in your ho "widgets.todo_list.refresh_title" = "Refresh To-do List"; "widgets.todo_list.select_list" = "Edit widget to select list."; "widgets.todo_list.title" = "To-do List"; -"yes_label" = "네"; +"yes_label" = "네"; \ No newline at end of file diff --git a/Sources/App/Resources/ml.lproj/Localizable.strings b/Sources/App/Resources/ml.lproj/Localizable.strings index 4f548018ee..bef78df41e 100644 --- a/Sources/App/Resources/ml.lproj/Localizable.strings +++ b/Sources/App/Resources/ml.lproj/Localizable.strings @@ -559,8 +559,11 @@ This server requires a client certificate (mTLS) but the operation was cancelled "live_activity.end_all.button" = "End All Activities"; "live_activity.end_all.confirm.button" = "End All"; "live_activity.end_all.confirm.title" = "End all Live Activities?"; +"live_activity.frequent_updates.footer" = "Allows Home Assistant to update Live Activities up to once per second. Enable in Settings \u203A %@ \u203A 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.section.active" = "Active Activities"; +"live_activity.section.privacy" = "Privacy"; "live_activity.section.status" = "Status"; "live_activity.status.enabled" = "Enabled"; "live_activity.status.open_settings" = "Open Settings"; diff --git a/Sources/App/Resources/nb.lproj/Localizable.strings b/Sources/App/Resources/nb.lproj/Localizable.strings index 08a1cc0fab..b57aa2bec8 100644 --- a/Sources/App/Resources/nb.lproj/Localizable.strings +++ b/Sources/App/Resources/nb.lproj/Localizable.strings @@ -559,8 +559,11 @@ This server requires a client certificate (mTLS) but the operation was cancelled "live_activity.end_all.button" = "Avslutt alle aktiviteter"; "live_activity.end_all.confirm.button" = "End All"; "live_activity.end_all.confirm.title" = "End all Live Activities?"; +"live_activity.frequent_updates.footer" = "Lar Home Assistant oppdatere Live Activities opptil én gang per sekund. Aktiver i Settings \u203A %@ \u203A Live Activities."; "live_activity.frequent_updates.title" = "Hyppige oppdateringer"; +"live_activity.privacy.message" = "Live Activity-innhold er synlig på låseskjermen og Dynamic Island uten Face ID eller Touch ID. Vær nøye med hva du viser."; "live_activity.section.active" = "Active Activities"; +"live_activity.section.privacy" = "Privacy"; "live_activity.section.status" = "Status"; "live_activity.status.enabled" = "Aktivert"; "live_activity.status.open_settings" = "Åpne Innstillinger"; diff --git a/Sources/App/Resources/nl.lproj/Localizable.strings b/Sources/App/Resources/nl.lproj/Localizable.strings index 215be6b001..4a464cc62d 100644 --- a/Sources/App/Resources/nl.lproj/Localizable.strings +++ b/Sources/App/Resources/nl.lproj/Localizable.strings @@ -559,8 +559,11 @@ Deze server vereist een clientcertificaat (mTLS), maar de bewerking is geannulee "live_activity.end_all.button" = "Alle activiteiten beëindigen"; "live_activity.end_all.confirm.button" = "Alles beëindigen"; "live_activity.end_all.confirm.title" = "Alle live activiteiten beëindigen?"; +"live_activity.frequent_updates.footer" = "Hiermee kan Home Assistant Live Activiteiten tot één keer per seconde bijwerken. Schakel dit in via Instellingen \u203A %@ \u203A Live activiteiten."; "live_activity.frequent_updates.title" = "Regelmatig bijwerken"; +"live_activity.privacy.message" = "Live activiteit-inhoud is zichtbaar op je vergrendelscherm en Dynamic Island zonder Face ID of Touch ID. Bedenk goed wat je wilt laten zien."; "live_activity.section.active" = "Actieve activiteiten"; +"live_activity.section.privacy" = "Privacy"; "live_activity.section.status" = "Status"; "live_activity.status.enabled" = "Ingeschakeld"; "live_activity.status.open_settings" = "Open instellingen"; diff --git a/Sources/App/Resources/pl-PL.lproj/Localizable.strings b/Sources/App/Resources/pl-PL.lproj/Localizable.strings index 503a10d988..412e5e095b 100644 --- a/Sources/App/Resources/pl-PL.lproj/Localizable.strings +++ b/Sources/App/Resources/pl-PL.lproj/Localizable.strings @@ -559,8 +559,11 @@ "live_activity.end_all.button" = "End All Activities"; "live_activity.end_all.confirm.button" = "End All"; "live_activity.end_all.confirm.title" = "End all Live Activities?"; +"live_activity.frequent_updates.footer" = "Allows Home Assistant to update Live Activities up to once per second. Enable in Settings \u203A %@ \u203A 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.section.active" = "Active Activities"; +"live_activity.section.privacy" = "Privacy"; "live_activity.section.status" = "Status"; "live_activity.status.enabled" = "Enabled"; "live_activity.status.open_settings" = "Open Settings"; diff --git a/Sources/App/Resources/pt-BR.lproj/Localizable.strings b/Sources/App/Resources/pt-BR.lproj/Localizable.strings index 773e9c300d..726febaa3e 100644 --- a/Sources/App/Resources/pt-BR.lproj/Localizable.strings +++ b/Sources/App/Resources/pt-BR.lproj/Localizable.strings @@ -559,8 +559,11 @@ This server requires a client certificate (mTLS) but the operation was cancelled "live_activity.end_all.button" = "End All Activities"; "live_activity.end_all.confirm.button" = "End All"; "live_activity.end_all.confirm.title" = "End all Live Activities?"; +"live_activity.frequent_updates.footer" = "Allows Home Assistant to update Live Activities up to once per second. Enable in Settings \u203A %@ \u203A 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.section.active" = "Active Activities"; +"live_activity.section.privacy" = "Privacy"; "live_activity.section.status" = "Status"; "live_activity.status.enabled" = "Enabled"; "live_activity.status.open_settings" = "Open Settings"; diff --git a/Sources/App/Resources/ru.lproj/Localizable.strings b/Sources/App/Resources/ru.lproj/Localizable.strings index 83265f9234..2f3086e7b7 100644 --- a/Sources/App/Resources/ru.lproj/Localizable.strings +++ b/Sources/App/Resources/ru.lproj/Localizable.strings @@ -559,11 +559,13 @@ "live_activity.end_all.button" = "End All Activities"; "live_activity.end_all.confirm.button" = "End All"; "live_activity.end_all.confirm.title" = "End all Live Activities?"; +"live_activity.frequent_updates.footer" = "Позволяет Home Assistant обновлять эфир активности с частотой до одного раза в секунду. Включите в настройках \u203A %@ \u203A эфира активности."; "live_activity.frequent_updates.title" = "Более частые обновления"; +"live_activity.privacy.message" = "Информация о текущей активности отображается на экране блокировки и в динамическом острове без использования Face ID или Touch ID. Внимательно выбирайте то, что вы показываете."; "live_activity.section.active" = "Действующие активности"; +"live_activity.section.privacy" = "Конфиденциальность"; "live_activity.section.status" = "Статус"; "live_activity.status.enabled" = "Включен"; -"live_activity.status.not_supported" = "Not available on iPad"; "live_activity.status.open_settings" = "Open Settings"; "live_activity.subtitle" = "Обновление Home Assistant в режиме реального времени на экране блокировки и динамическом острове."; "live_activity.title" = "Эфир активности"; @@ -1739,4 +1741,4 @@ Home Assistant — это приложение с открытым исходн "widgets.todo_list.refresh_title" = "Обновить список задач"; "widgets.todo_list.select_list" = "Отредактируйте виджет, чтобы выбрать список."; "widgets.todo_list.title" = "Список задач"; -"yes_label" = "Да"; +"yes_label" = "Да"; \ No newline at end of file diff --git a/Sources/App/Resources/sl.lproj/Localizable.strings b/Sources/App/Resources/sl.lproj/Localizable.strings index 1406f4f147..b29fc8eddc 100644 --- a/Sources/App/Resources/sl.lproj/Localizable.strings +++ b/Sources/App/Resources/sl.lproj/Localizable.strings @@ -559,8 +559,11 @@ This server requires a client certificate (mTLS) but the operation was cancelled "live_activity.end_all.button" = "End All Activities"; "live_activity.end_all.confirm.button" = "End All"; "live_activity.end_all.confirm.title" = "End all Live Activities?"; +"live_activity.frequent_updates.footer" = "Allows Home Assistant to update Live Activities up to once per second. Enable in Settings \u203A %@ \u203A 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.section.active" = "Active Activities"; +"live_activity.section.privacy" = "Privacy"; "live_activity.section.status" = "Status"; "live_activity.status.enabled" = "Enabled"; "live_activity.status.open_settings" = "Open Settings"; diff --git a/Sources/App/Resources/sv.lproj/Localizable.strings b/Sources/App/Resources/sv.lproj/Localizable.strings index 70ccf0bd4c..0a69d0ffda 100644 --- a/Sources/App/Resources/sv.lproj/Localizable.strings +++ b/Sources/App/Resources/sv.lproj/Localizable.strings @@ -557,11 +557,13 @@ "live_activity.end_all.button" = "Avsluta alla aktiviteter"; "live_activity.end_all.confirm.button" = "Avsluta alla"; "live_activity.end_all.confirm.title" = "Avsluta alla Live-aktiviteter?"; +"live_activity.frequent_updates.footer" = "Tillåter Home Assistant att uppdatera Live-aktiviteter upp till en gång per sekund. Aktivera i Inställningar \u203A %@ \u203A Liveaktiviteter."; "live_activity.frequent_updates.title" = "Frekventa uppdateringar"; +"live_activity.privacy.message" = "Live Activity-innehåll är synligt på din låsskärm och Dynamic Island utan Face ID eller Touch ID. Välj vad du visar noggrant."; "live_activity.section.active" = "Aktiva aktiviteter"; +"live_activity.section.privacy" = "Integritet"; "live_activity.section.status" = "Status"; "live_activity.status.enabled" = "Aktiverad"; -"live_activity.status.not_supported" = "Not available on iPad"; "live_activity.status.open_settings" = "Öppna inställningar"; "live_activity.subtitle" = "Uppdateringar av Home Assistant i realtid på din låsskärm och Dynamic Island."; "live_activity.title" = "Live-aktiviteter"; @@ -1734,4 +1736,4 @@ Home Assistant är öppen källkod, förespråkar integritet och körs lokalt i "widgets.todo_list.refresh_title" = "Uppdatera att-göra-listan"; "widgets.todo_list.select_list" = "Redigera widget för att välja lista."; "widgets.todo_list.title" = "Att göra-lista"; -"yes_label" = "Ja"; +"yes_label" = "Ja"; \ No newline at end of file diff --git a/Sources/App/Resources/tr.lproj/Localizable.strings b/Sources/App/Resources/tr.lproj/Localizable.strings index 5e694e7b85..3df6b834a0 100644 --- a/Sources/App/Resources/tr.lproj/Localizable.strings +++ b/Sources/App/Resources/tr.lproj/Localizable.strings @@ -559,8 +559,11 @@ "live_activity.end_all.button" = "Tüm Etkinlikleri Sonlandır"; "live_activity.end_all.confirm.button" = "Tümünü Sonlandır"; "live_activity.end_all.confirm.title" = "Tüm canlı etkinlikleri sonlandırılsın mı?"; +"live_activity.frequent_updates.footer" = "Home Assistant'ın Canlı Etkinlikleri saniyede bir kez güncellemesine olanak tanır. Ayarlar'dan etkinleştirin. \u203A %@ \u203A Canlı Etkinlikler."; "live_activity.frequent_updates.title" = "Sık Güncellemeler"; +"live_activity.privacy.message" = "Canlı Etkinlik içeriği, Face ID veya Touch ID olmadan da Kilit Ekranınızda ve Dinamik Ada'da görünür. Ne göstereceğinizi dikkatlice seçin."; "live_activity.section.active" = "Aktif Etkinlikler"; +"live_activity.section.privacy" = "Gizlilik"; "live_activity.section.status" = "Durum"; "live_activity.status.enabled" = "Etkin"; "live_activity.status.open_settings" = "Ayarları Aç"; diff --git a/Sources/App/Resources/uk.lproj/Localizable.strings b/Sources/App/Resources/uk.lproj/Localizable.strings index 10ca3a90ff..f460bd76aa 100644 --- a/Sources/App/Resources/uk.lproj/Localizable.strings +++ b/Sources/App/Resources/uk.lproj/Localizable.strings @@ -559,8 +559,11 @@ This server requires a client certificate (mTLS) but the operation was cancelled "live_activity.end_all.button" = "End All Activities"; "live_activity.end_all.confirm.button" = "End All"; "live_activity.end_all.confirm.title" = "End all Live Activities?"; +"live_activity.frequent_updates.footer" = "Allows Home Assistant to update Live Activities up to once per second. Enable in Settings \u203A %@ \u203A 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.section.active" = "Active Activities"; +"live_activity.section.privacy" = "Privacy"; "live_activity.section.status" = "Status"; "live_activity.status.enabled" = "Enabled"; "live_activity.status.open_settings" = "Open Settings"; diff --git a/Sources/App/Resources/vi.lproj/Localizable.strings b/Sources/App/Resources/vi.lproj/Localizable.strings index 869f572baa..ccc8ccc9ca 100644 --- a/Sources/App/Resources/vi.lproj/Localizable.strings +++ b/Sources/App/Resources/vi.lproj/Localizable.strings @@ -559,8 +559,11 @@ This server requires a client certificate (mTLS) but the operation was cancelled "live_activity.end_all.button" = "End All Activities"; "live_activity.end_all.confirm.button" = "End All"; "live_activity.end_all.confirm.title" = "End all Live Activities?"; +"live_activity.frequent_updates.footer" = "Allows Home Assistant to update Live Activities up to once per second. Enable in Settings \u203A %@ \u203A 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.section.active" = "Active Activities"; +"live_activity.section.privacy" = "Privacy"; "live_activity.section.status" = "Status"; "live_activity.status.enabled" = "Enabled"; "live_activity.status.open_settings" = "Open Settings"; diff --git a/Sources/App/Resources/zh-Hans.lproj/Localizable.strings b/Sources/App/Resources/zh-Hans.lproj/Localizable.strings index d7a070d867..e6e48a4967 100644 --- a/Sources/App/Resources/zh-Hans.lproj/Localizable.strings +++ b/Sources/App/Resources/zh-Hans.lproj/Localizable.strings @@ -559,8 +559,11 @@ "live_activity.end_all.button" = "结束所有活动"; "live_activity.end_all.confirm.button" = "全部结束"; "live_activity.end_all.confirm.title" = "结束所有实时活动吗 ?"; +"live_activity.frequent_updates.footer" = "允许 Home Assistant 每秒更新一次实时活动。在“设置” \u203A %@ \u203A “实时活动”中启用。"; "live_activity.frequent_updates.title" = "频繁更新"; +"live_activity.privacy.message" = "即使没有面容 ID 或触控 ID,您也可以在锁定屏幕和动态岛屿上查看实时活动内容。请谨慎选择要显示的内容。"; "live_activity.section.active" = "活跃活动"; +"live_activity.section.privacy" = "隐私"; "live_activity.section.status" = "状况"; "live_activity.status.enabled" = "已启用"; "live_activity.status.open_settings" = "打开设置"; diff --git a/Sources/App/Resources/zh-Hant.lproj/Localizable.strings b/Sources/App/Resources/zh-Hant.lproj/Localizable.strings index 1d0fefa22b..9bf46a12a8 100644 --- a/Sources/App/Resources/zh-Hant.lproj/Localizable.strings +++ b/Sources/App/Resources/zh-Hant.lproj/Localizable.strings @@ -559,8 +559,11 @@ "live_activity.end_all.button" = "結束所有即時動態"; "live_activity.end_all.confirm.button" = "全部結束"; "live_activity.end_all.confirm.title" = "結束所有即時動態?"; +"live_activity.frequent_updates.footer" = "允許 Home Assistant 每秒更新一次即時動態。於設定中開啟 \u203A %@ \u203A 即時動態。"; "live_activity.frequent_updates.title" = "頻繁更新"; +"live_activity.privacy.message" = "即使沒有 Face ID 或 Touch ID,也可以在鎖定畫面和動態島上查看即時動態內容。請謹慎選擇要顯示的內容。"; "live_activity.section.active" = "啟用動態"; +"live_activity.section.privacy" = "隱私權"; "live_activity.section.status" = "狀態"; "live_activity.status.enabled" = "已開啟"; "live_activity.status.open_settings" = "開啟設定"; diff --git a/Sources/App/Settings/LiveActivity/LiveActivitySettingsView.swift b/Sources/App/Settings/LiveActivity/LiveActivitySettingsView.swift index 29c3f6e8f1..4062641198 100644 --- a/Sources/App/Settings/LiveActivity/LiveActivitySettingsView.swift +++ b/Sources/App/Settings/LiveActivity/LiveActivitySettingsView.swift @@ -78,7 +78,7 @@ struct LiveActivitySettingsView: View { Label(L10n.LiveActivity.title, systemSymbol: .livephoto) Spacer() if !isLiveActivitySupportedOnDevice { - Text(L10n.LiveActivity.Status.notSupported) + Text("Not available on iPad") .foregroundStyle(.secondary) } else if authorizationEnabled { Text(L10n.LiveActivity.Status.enabled) @@ -97,7 +97,7 @@ struct LiveActivitySettingsView: View { Label(L10n.LiveActivity.FrequentUpdates.title, systemSymbol: .bolt) Spacer() if !isLiveActivitySupportedOnDevice { - Text(L10n.LiveActivity.Status.notSupported) + Text("Not available on iPad") .foregroundStyle(.secondary) } else if frequentUpdatesEnabled { Text(L10n.LiveActivity.Status.enabled) diff --git a/Sources/Shared/Resources/Swiftgen/Strings.swift b/Sources/Shared/Resources/Swiftgen/Strings.swift index ecd57b0259..4fd42f2e3b 100644 --- a/Sources/Shared/Resources/Swiftgen/Strings.swift +++ b/Sources/Shared/Resources/Swiftgen/Strings.swift @@ -2024,20 +2024,28 @@ 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. + public static func footer(_ p1: Any) -> String { + return L10n.tr("Localizable", "live_activity.frequent_updates.footer", String(describing: p1)) + } /// Frequent Updates public static var title: String { return L10n.tr("Localizable", "live_activity.frequent_updates.title") } } + public enum Privacy { + /// Live Activity content is visible on your Lock Screen and Dynamic Island without Face ID or Touch ID. Choose what you display carefully. + public static var message: String { return L10n.tr("Localizable", "live_activity.privacy.message") } + } public enum Section { /// Active Activities public static var active: String { return L10n.tr("Localizable", "live_activity.section.active") } + /// Privacy + public static var privacy: String { return L10n.tr("Localizable", "live_activity.section.privacy") } /// Status public static var status: String { return L10n.tr("Localizable", "live_activity.section.status") } } public enum Status { /// Enabled public static var enabled: String { return L10n.tr("Localizable", "live_activity.status.enabled") } - /// Not available on iPad - public static var notSupported: String { return L10n.tr("Localizable", "live_activity.status.not_supported") } /// Open Settings public static var openSettings: String { return L10n.tr("Localizable", "live_activity.status.open_settings") } } From 7762f889bacdf272f188b563349448e3f533d2f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bruno=20Pantale=C3=A3o?= <5808343+bgoncal@users.noreply.github.com> Date: Wed, 27 May 2026 15:25:33 +0200 Subject: [PATCH 13/15] Remove settings UI changes from Live Activity PR --- Sources/App/Settings/DebugView.swift | 15 + .../LiveActivitySettingsView.swift | 316 +++++++++--------- .../App/Settings/Settings/SettingsItem.swift | 45 +-- .../App/Settings/Settings/SettingsView.swift | 2 +- 4 files changed, 187 insertions(+), 191 deletions(-) diff --git a/Sources/App/Settings/DebugView.swift b/Sources/App/Settings/DebugView.swift index d3303df0ab..8defed9979 100644 --- a/Sources/App/Settings/DebugView.swift +++ b/Sources/App/Settings/DebugView.swift @@ -146,6 +146,21 @@ struct DebugView: View { #endif } + #if os(iOS) && !targetEnvironment(macCatalyst) + if #available(iOS 17.2, *) { + Section { + NavigationLink { + LiveActivitySettingsView() + } label: { + linkContent( + image: .init(systemSymbol: .livephoto), + title: L10n.LiveActivity.title + ) + } + } + } + #endif + carPlayDebugSection criticalSection diff --git a/Sources/App/Settings/LiveActivity/LiveActivitySettingsView.swift b/Sources/App/Settings/LiveActivity/LiveActivitySettingsView.swift index 4062641198..d7ddf75cc0 100644 --- a/Sources/App/Settings/LiveActivity/LiveActivitySettingsView.swift +++ b/Sources/App/Settings/LiveActivity/LiveActivitySettingsView.swift @@ -6,7 +6,7 @@ import SwiftUI // MARK: - Entry point /// Deployment target is iOS 15. The settings item is filtered from the list on < iOS 17.2 -/// (see SettingsItem.generalItems/allVisibleCases), so this view is only ever navigated to on iOS 17.2+. +/// (see SettingsItem.allVisibleCases), so this view is only ever navigated to on iOS 17.2+. @available(iOS 17.2, *) struct LiveActivitySettingsView: View { // MARK: State @@ -63,8 +63,14 @@ struct LiveActivitySettingsView: View { } #if DEBUG - samplesSection + debugSection #endif + + privacySection + + if #available(iOS 17.2, *) { + frequentUpdatesSection + } } .navigationTitle(L10n.LiveActivity.title) .task { await loadActivities() } @@ -73,14 +79,11 @@ struct LiveActivitySettingsView: View { // MARK: - Sections private var statusSection: some View { - Section { + Section(L10n.LiveActivity.Section.status) { HStack { Label(L10n.LiveActivity.title, systemSymbol: .livephoto) Spacer() - if !isLiveActivitySupportedOnDevice { - Text("Not available on iPad") - .foregroundStyle(.secondary) - } else if authorizationEnabled { + if authorizationEnabled { Text(L10n.LiveActivity.Status.enabled) .foregroundStyle(.green) } else { @@ -92,37 +95,11 @@ struct LiveActivitySettingsView: View { .foregroundStyle(.orange) } } - - HStack { - Label(L10n.LiveActivity.FrequentUpdates.title, systemSymbol: .bolt) - Spacer() - if !isLiveActivitySupportedOnDevice { - Text("Not available on iPad") - .foregroundStyle(.secondary) - } else if frequentUpdatesEnabled { - Text(L10n.LiveActivity.Status.enabled) - .foregroundStyle(.green) - } else { - Button(L10n.LiveActivity.Status.openSettings) { - if let url = URL(string: UIApplication.openSettingsURLString) { - UIApplication.shared.open(url) - } - } - .foregroundStyle(.secondary) - } - } - } header: { - Text(L10n.LiveActivity.Section.status) } } - private var isLiveActivitySupportedOnDevice: Bool { - UIDevice.current.userInterfaceIdiom != .pad - } - - // MARK: - Samples (DEBUG builds only) + // MARK: - Debug (DEBUG builds only) - #if DEBUG // // Two sections: Static (fixed snapshots to verify layout) and Animated (multi-stage // self-updating sequences to simulate real HA automation behavior). @@ -141,139 +118,131 @@ struct LiveActivitySettingsView: View { // It does NOT appear on the lock screen. Use a Dynamic Island device or // simulator (iPhone 14 Pro+) to see it. - private var samplesSection: some View { - Section { - NavigationLink("Samples") { - List { - staticSamplesSection - animatedSamplesSection + #if DEBUG + private var debugSection: some View { + Group { + Section { + // Minimum viable layout — only the message field is set. + // Verifies the bare layout renders without icon, progress, or timer. + Button("Plain Message") { + startTestActivity( + tag: "debug-plain", + title: "Home Assistant", + state: .init(message: "Everything looks good at home.") + ) } - .navigationTitle("Samples") - } - } - } - private var staticSamplesSection: some View { - Section { - // Minimum viable layout — only the message field is set. - // Verifies the bare layout renders without icon, progress, or timer. - Button("Plain Message") { - startTestActivity( - tag: "debug-plain", - title: "Home Assistant", - state: .init(message: "Everything looks good at home.") - ) - } + // icon = nil code path. Layout must not shift or break when no icon is provided. + // color = nil so the progress bar uses the default HA-blue tint. + // criticalText ("Active") visible in DI compact trailing only. + Button("No Icon · Default Color") { + startTestActivity( + tag: "debug-no-icon", + title: "Script Running", + state: .init( + message: "Irrigation zone 3 is active", + criticalText: "Active", + progress: 35, + progressMax: 100 + ) + ) + } - // icon = nil code path. Layout must not shift or break when no icon is provided. - // color = nil so the progress bar uses the default HA-blue tint. - // criticalText ("Active") visible in DI compact trailing only. - Button("No Icon · Default Color") { - startTestActivity( - tag: "debug-no-icon", - title: "Script Running", - state: .init( - message: "Irrigation zone 3 is active", - criticalText: "Active", - progress: 35, - progressMax: 100 + // Short 60-second countdown with no progress bar. + // Red color communicates urgency. Watch the timer count down in real time. + // Represents automations like alarm arming delays or reminder countdowns. + Button("Alarm · 60 sec Countdown") { + startTestActivity( + tag: "debug-alarm", + title: "Security Alarm", + state: .init( + message: "Motion at back door · Arms in 60 seconds", + criticalText: "60 sec", + chronometer: true, + countdownEnd: Date().addingTimeInterval(60), + icon: "mdi:alarm-light", + color: "#F44336" + ) ) - ) - } + } - // Short 60-second countdown with no progress bar. - // Red color communicates urgency. Watch the timer count down in real time. - // Represents automations like alarm arming delays or reminder countdowns. - Button("Alarm · 60 sec Countdown") { - startTestActivity( - tag: "debug-alarm", - title: "Security Alarm", - state: .init( - message: "Motion at back door · Arms in 60 seconds", - criticalText: "60 sec", - chronometer: true, - countdownEnd: Date().addingTimeInterval(60), - icon: "mdi:alarm-light", - color: "#F44336" + // Every ContentState field active at the same time. + // Lock screen shows: icon → live countdown → progress bar. + // criticalText ("5 min") visible in DI compact trailing only. + // Use this to confirm no layout collisions when all fields are populated. + Button("All Fields · Max Load") { + startTestActivity( + tag: "debug-all", + title: "All Fields", + state: .init( + message: "All content state fields active", + criticalText: "5 min", + progress: 42, + progressMax: 100, + chronometer: true, + countdownEnd: Date().addingTimeInterval(5 * 60), + icon: "mdi:home-assistant", + color: "#03A9F4" + ) ) - ) + } + } header: { + Text("Debug · Static") + } footer: { + Text("Fixed state — no updates after start. Good for checking layout at a glance.") } - // Every ContentState field active at the same time. - // Lock screen shows: icon → live countdown → progress bar. - // criticalText ("5 min") visible in DI compact trailing only. - // Use this to confirm no layout collisions when all fields are populated. - Button("All Fields · Max Load") { - startTestActivity( - tag: "debug-all", - title: "All Fields", - state: .init( - message: "All content state fields active", - criticalText: "5 min", - progress: 42, - progressMax: 100, - chronometer: true, - countdownEnd: Date().addingTimeInterval(5 * 60), - icon: "mdi:home-assistant", - color: "#03A9F4" - ) + Section { + // Progress bar advances through five named stages. + // criticalText tracks the current stage name in the DI compact trailing slot. + // Icon swaps from washing-machine to check-circle on the final update. + // Represents any multi-step appliance cycle automation. + Button("Washing Machine · Stage Labels (~12 s)") { startWashingMachineCycle() } + + // Numeric percentage in criticalText updates alongside the progress bar. + // Color shifts from green to yellow-green as the charge nears 100 %. + // Represents any "% complete with time remaining" automation pattern. + Button("EV Charging · Numeric % (~16 s)") { startEVChargingSimulation() } + + // The only scenario where both progress (playback position) and a live countdown + // (time remaining in track) are active and updating at the same time. + // Simulates a track change mid-sequence: progress resets, countdown resets. + Button("Media Player · Progress + Timer (~20 s)") { startMediaNowPlaying() } + + // Message, criticalText, and icon all change on every update — no progress bar. + // Represents automations where the status category itself changes (not just a value). + Button("Package Delivery · All Text Fields (~15 s)") { startPackageJourney() } + + // No progress bar — state communicated entirely through color and icon. + // Escalates orange (motion) → red (person) → green (all clear). + // Represents any alert-and-resolve automation pattern. + Button("Security Escalation · Color + Icon (~8 s)") { startSecuritySequence() } + + // Cycles through wash stages then calls activity.end() with .default dismissal. + // The only scenario that tests the full lifecycle: start → update → end. + // After ending, the final "Done" state lingers on the lock screen (up to 4 h). + Button("Dishwasher · Full Lifecycle, Ends Itself (~12 s)") { startDishwasherAutoComplete() } + + // Fires 6 updates 2 seconds apart (12 s total). + // On iOS 18 the system enforces ~15 s between rendered updates — some will be + // silently dropped. Watch the counter skip values to see the rate limit in action. + // On the simulator and iOS 17 all 6 updates should render. + Button("Rate Limit · 6 Rapid Updates, 2 s Apart (~12 s)") { startRapidUpdateStressTest() } + } header: { + Text("Debug · Animated") + } footer: { + Text( + "Activity updates itself after you tap. Tap, then immediately lock (⌘L) " + + "to watch updates on the lock screen in real time." ) } - } header: { - Text("Sample · Static") - } footer: { - Text("Fixed state — no updates after start. Good for checking layout at a glance.") } } + #endif - private var animatedSamplesSection: some View { - Section { - // Progress bar advances through five named stages. - // criticalText tracks the current stage name in the DI compact trailing slot. - // Icon swaps from washing-machine to check-circle on the final update. - // Represents any multi-step appliance cycle automation. - Button("Washing Machine · Stage Labels (~12 s)") { startWashingMachineCycle() } - - // Numeric percentage in criticalText updates alongside the progress bar. - // Color shifts from green to yellow-green as the charge nears 100 %. - // Represents any "% complete with time remaining" automation pattern. - Button("EV Charging · Numeric % (~16 s)") { startEVChargingSimulation() } - - // The only scenario where both progress (playback position) and a live countdown - // (time remaining in track) are active and updating at the same time. - // Simulates a track change mid-sequence: progress resets, countdown resets. - Button("Media Player · Progress + Timer (~20 s)") { startMediaNowPlaying() } - - // Message, criticalText, and icon all change on every update — no progress bar. - // Represents automations where the status category itself changes (not just a value). - Button("Package Delivery · All Text Fields (~15 s)") { startPackageJourney() } - - // No progress bar — state communicated entirely through color and icon. - // Escalates orange (motion) → red (person) → green (all clear). - // Represents any alert-and-resolve automation pattern. - Button("Security Escalation · Color + Icon (~8 s)") { startSecuritySequence() } - - // Cycles through wash stages then calls activity.end() with .default dismissal. - // The only scenario that tests the full lifecycle: start → update → end. - // After ending, the final "Done" state lingers on the lock screen (up to 4 h). - Button("Dishwasher · Full Lifecycle, Ends Itself (~12 s)") { startDishwasherAutoComplete() } - - // Fires 6 updates 2 seconds apart (12 s total). - // On iOS 18 the system enforces ~15 s between rendered updates — some will be - // silently dropped. Watch the counter skip values to see the rate limit in action. - // On the simulator and iOS 17 all 6 updates should render. - Button("Rate Limit · 6 Rapid Updates, 2 s Apart (~12 s)") { startRapidUpdateStressTest() } - } header: { - Text("Sample · Animated") - } footer: { - Text( - "Activity updates itself after you tap. Tap, then immediately lock (⌘L) " + - "to watch updates on the lock screen in real time." - ) - } - } + #if DEBUG - // MARK: - Sample helpers + // MARK: - Debug helpers /// Starts a single-state activity (no subsequent updates). private func startTestActivity(tag: String, title: String, state: HALiveActivityAttributes.ContentState) { @@ -592,14 +561,53 @@ struct LiveActivitySettingsView: View { ] ) } + #endif + private var privacySection: some View { + Section { + Label(L10n.LiveActivity.Privacy.message, systemSymbol: .lockShield) + .font(.footnote) + .foregroundStyle(.secondary) + } header: { + Text(L10n.LiveActivity.Section.privacy) + } + } + + @available(iOS 17.2, *) + private var frequentUpdatesSection: some View { + let appName = Bundle.main.infoDictionary?["CFBundleDisplayName"] as? String ?? "Home Assistant" + return Section { + HStack { + Label(L10n.LiveActivity.FrequentUpdates.title, systemSymbol: .bolt) + Spacer() + if frequentUpdatesEnabled { + Text(L10n.LiveActivity.Status.enabled) + .foregroundStyle(.green) + } else { + Button(L10n.LiveActivity.Status.openSettings) { + if let url = URL(string: UIApplication.openSettingsURLString) { + UIApplication.shared.open(url) + } + } + .foregroundStyle(.secondary) + } + } + } header: { + Text(L10n.LiveActivity.FrequentUpdates.title) + } footer: { + Text(L10n.LiveActivity.FrequentUpdates.footer(appName)) + } + } + // MARK: - Data private func loadActivities() async { let info = ActivityAuthorizationInfo() authorizationEnabled = info.areActivitiesEnabled - frequentUpdatesEnabled = info.frequentPushesEnabled + if #available(iOS 17.2, *) { + frequentUpdatesEnabled = info.frequentPushesEnabled + } activities = Activity.activities.map { ActivitySnapshot(activity: $0) diff --git a/Sources/App/Settings/Settings/SettingsItem.swift b/Sources/App/Settings/Settings/SettingsItem.swift index e27b5facf6..e26e3fffac 100644 --- a/Sources/App/Settings/Settings/SettingsItem.swift +++ b/Sources/App/Settings/Settings/SettingsItem.swift @@ -153,40 +153,25 @@ enum SettingsItem: String, Hashable, CaseIterable { static var allVisibleCases: [SettingsItem] { allCases.filter { item in - if item == .liveActivities { - return canShowLiveActivities - } - // Filter based on platform #if targetEnvironment(macCatalyst) - let hiddenItems: [SettingsItem] = [ - .servers, - .gestures, - .kiosk, - .watch, - .carPlay, - .appIconShortcuts, - .complications, - .nfc, - .help, - .whatsNew, - ] - - if hiddenItems.contains(item) { + if item == .servers || item == .gestures || item == .kiosk || item == .watch || item == .carPlay || + item == .appIconShortcuts || + item == .complications || item == .nfc || item == .help || + item == .whatsNew { return false } #endif - + // Live Activities are shown in DebugView + if item == .liveActivities { + return false + } return true } } static var generalItems: [SettingsItem] { - var items: [SettingsItem] = [.general, .gestures, .location, .notifications, .kiosk] - if canShowLiveActivities { - items.append(.liveActivities) - } - return items + [.general, .gestures, .location, .notifications, .kiosk] } static var integrationItems: [SettingsItem] { @@ -208,18 +193,6 @@ enum SettingsItem: String, Hashable, CaseIterable { static var helpItems: [SettingsItem] { [.help, .privacy, .debugging] } - - private static var canShowLiveActivities: Bool { - #if os(iOS) && !targetEnvironment(macCatalyst) - if #available(iOS 17.2, *) { - return true - } else { - return false - } - #else - return false - #endif - } } // MARK: - Material Design Icons Image diff --git a/Sources/App/Settings/Settings/SettingsView.swift b/Sources/App/Settings/Settings/SettingsView.swift index 71cb275871..17c4ff67b8 100644 --- a/Sources/App/Settings/Settings/SettingsView.swift +++ b/Sources/App/Settings/Settings/SettingsView.swift @@ -292,7 +292,7 @@ struct SettingsView: View { Label { HStack(spacing: DesignSystem.Spaces.one) { Text(item.title) - if [.kiosk, .liveActivities].contains(item) { + if item == .kiosk { LabsLabel() } } From 914488b979f4fecfa9dd8f25f110da77581cafc9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bruno=20Pantale=C3=A3o?= <5808343+bgoncal@users.noreply.github.com> Date: Wed, 27 May 2026 15:29:06 +0200 Subject: [PATCH 14/15] Fix Live Activity registry line wrapping --- Sources/Shared/LiveActivity/LiveActivityRegistry.swift | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Sources/Shared/LiveActivity/LiveActivityRegistry.swift b/Sources/Shared/LiveActivity/LiveActivityRegistry.swift index 2e1a2863ef..5597def28d 100644 --- a/Sources/Shared/LiveActivity/LiveActivityRegistry.swift +++ b/Sources/Shared/LiveActivity/LiveActivityRegistry.swift @@ -271,7 +271,9 @@ public actor LiveActivityRegistry: LiveActivityRegistryProtocol { let observationTask = makeObservationTask(for: activity) entries[tag] = Entry(activity: activity, observationTask: observationTask) - Current.Log.verbose("LiveActivityRegistry: observed remotely started activity for tag \(tag), id=\(activity.id)") + Current.Log.verbose( + "LiveActivityRegistry: observed remotely started activity for tag \(tag), id=\(activity.id)" + ) } } From b8361379c8be6431166b09ba1239294938d063cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bruno=20Pantale=C3=A3o=20Gon=C3=A7alves?= <5808343+bgoncal@users.noreply.github.com> Date: Wed, 27 May 2026 15:57:37 +0200 Subject: [PATCH 15/15] Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- Sources/Shared/LiveActivity/LiveActivityRegistry.swift | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Sources/Shared/LiveActivity/LiveActivityRegistry.swift b/Sources/Shared/LiveActivity/LiveActivityRegistry.swift index 5597def28d..38258d6bdb 100644 --- a/Sources/Shared/LiveActivity/LiveActivityRegistry.swift +++ b/Sources/Shared/LiveActivity/LiveActivityRegistry.swift @@ -19,6 +19,11 @@ public protocol LiveActivityRegistryProtocol: AnyObject { func startObservingRemoteActivityStarts() async } +public extension LiveActivityRegistryProtocol { + @available(iOS 17.2, *) + func startObservingRemoteActivityStarts() async {} +} + /// Thread-safe registry for active `Activity` instances. /// /// Uses Swift's actor isolation to protect the `[String: Entry]` dictionary from