Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
61c9ff7
Add kiosk mode core infrastructure (PR 1/5)
nstefanelli Jan 13, 2026
dcfb29e
Use SFSafeSymbols instead of string-based system images
nstefanelli Jan 13, 2026
b9efbcf
Add unit tests for kiosk mode functionality
nstefanelli Jan 13, 2026
9817f51
Address PR review feedback
nstefanelli Jan 14, 2026
aa39b40
Add Kiosk Mode entry to app settings menu
nstefanelli Jan 14, 2026
b2ce91a
Fix MainActor isolation error in deinit - weak references handle clea…
nstefanelli Jan 14, 2026
db18fb5
Improve kiosk auth behavior
nstefanelli Jan 14, 2026
664b1b9
Fix: Use manager.settings for auth checks, not local settings copy
nstefanelli Jan 14, 2026
4329c1e
Fix status bar hiding and add 30-second screensaver timeout
nstefanelli Jan 14, 2026
7f25208
Merge upstream/main to resolve conflicts
nstefanelli Jan 14, 2026
4004e78
Fix SwiftFormat lint issues
nstefanelli Jan 14, 2026
a79e322
Fix SwiftFormat issues in test file (sort imports, remove test prefix)
nstefanelli Jan 14, 2026
e05279e
Merge branch 'main' into kiosk-pr1-core
nstefanelli Jan 14, 2026
39d5d5e
Fix kiosk settings UI issues from PR review
nstefanelli Jan 14, 2026
a71e530
Clarify night time logic comments per Copilot review
nstefanelli Jan 14, 2026
19f95f3
Merge branch 'main' into kiosk-pr1-core
nstefanelli Jan 14, 2026
806f28f
Address PR review feedback from bgoncal
nstefanelli Jan 15, 2026
fedb9ba
Merge branch 'main' into kiosk-pr1-core
nstefanelli Jan 15, 2026
a618cdc
Remove unused settings and add Done button to toolbar
nstefanelli Jan 15, 2026
a690539
Merge branch 'main' into kiosk-pr1-core
nstefanelli Jan 15, 2026
1649e29
Address PR #4218 review feedback
nstefanelli Jan 16, 2026
cf2b11a
Merge branch 'main' into kiosk-pr1-core
nstefanelli Jan 16, 2026
20f3679
Merge branch 'main' into kiosk-pr1-core
nstefanelli Jan 17, 2026
be5fb59
Address PR review feedback from bgoncal
nstefanelli Jan 21, 2026
913edc2
Merge upstream/main and adapt kiosk mode to new Frontend directory st…
nstefanelli Mar 9, 2026
57848ae
Regenerate SwiftGen Strings.swift with kiosk localization keys
nstefanelli Mar 9, 2026
f4896bb
Fix Xcode project file and update database table for upstream compati…
nstefanelli Mar 9, 2026
b5e5285
Update Xcode project and regenerate SwiftGen strings after upstream m…
nstefanelli Mar 9, 2026
89db14f
Replace deprecated edgesIgnoringSafeArea with ignoresSafeArea
nstefanelli Mar 9, 2026
83bbd2f
Address Copilot code review feedback
nstefanelli Mar 9, 2026
d89d94c
Remove extra trailing blank line in KioskSettings test file
nstefanelli Mar 9, 2026
3050279
Fix database table count tests for kiosk settings table
nstefanelli Mar 9, 2026
0a7f1c0
Merge remote-tracking branch 'upstream/main' into kiosk-pr1-core
nstefanelli Mar 9, 2026
58241d1
Rebuild pbxproj from upstream to fix corrupted merge
nstefanelli Mar 9, 2026
099edf2
Add KioskSettingsTable to Shared-watchOS build phase
nstefanelli Mar 9, 2026
75e8b72
Remove KioskSettingsTable from SharedTesting build phase
nstefanelli Mar 9, 2026
7786ec9
Address second round of Copilot review feedback
nstefanelli Mar 9, 2026
69b0743
Address maintainer review: handler pattern, fullscreen screensaver, G…
nstefanelli Mar 11, 2026
7a2cceb
Strip day/night brightness schedule for separate PR, hide clock optio…
nstefanelli Mar 11, 2026
d6a88b7
Merge upstream/main into kiosk-pr1-core
nstefanelli Mar 11, 2026
b1622f2
Address round 2 maintainer review and Copilot feedback
nstefanelli Mar 12, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
136 changes: 101 additions & 35 deletions HomeAssistant.xcodeproj/project.pbxproj

Large diffs are not rendered by default.

30 changes: 30 additions & 0 deletions Sources/App/Frontend/Extensions/WebViewController+Kiosk.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import Shared
import UIKit

// MARK: - Kiosk Mode Extension

extension WebViewController {
/// Setup kiosk mode integration with KioskModeManager
/// Call this from viewDidLoad
func setupKioskMode() {
KioskModeManager.shared.setup(using: self)
}

// MARK: - Status Bar & Home Indicator

var kioskPrefersStatusBarHidden: Bool {
KioskModeManager.shared.prefersStatusBarHidden
}

var kioskPrefersHomeIndicatorAutoHidden: Bool {
KioskModeManager.shared.prefersHomeIndicatorAutoHidden
}

// MARK: - Touch Handling

/// Record user touch activity to reset the screensaver idle timer
/// Required because WKWebView consumes touch events before UIKit idle detection
func recordKioskActivity() {
KioskModeManager.shared.recordActivity(source: "touch")
}
}
12 changes: 10 additions & 2 deletions Sources/App/Frontend/WebView/WebViewController.swift
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import AVFoundation
import AVKit
import Combine
import CoreLocation
import HAKit
import Improv_iOS
Expand Down Expand Up @@ -69,11 +70,11 @@ final class WebViewController: UIViewController, WKNavigationDelegate, WKUIDeleg
var underlyingPreferredStatusBarStyle: UIStatusBarStyle = .lightContent

override var prefersStatusBarHidden: Bool {
Current.settingsStore.fullScreen
Current.settingsStore.fullScreen || kioskPrefersStatusBarHidden
}

override var prefersHomeIndicatorAutoHidden: Bool {
Current.settingsStore.fullScreen
Current.settingsStore.fullScreen || kioskPrefersHomeIndicatorAutoHidden
}

override var preferredStatusBarStyle: UIStatusBarStyle {
Expand Down Expand Up @@ -263,6 +264,7 @@ final class WebViewController: UIViewController, WKNavigationDelegate, WKUIDeleg
postOnboardingNotificationPermission()
emptyStateObservations()
checkForLocalSecurityLevelDecisionNeeded()
setupKioskMode()
}

// Workaround for webview rotation issues: https://github.com/Telerik-Verified-Plugins/WKWebView/pull/263
Expand All @@ -282,6 +284,12 @@ final class WebViewController: UIViewController, WKNavigationDelegate, WKUIDeleg
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
updateDatabaseAndPanels()

// Refresh kiosk status bar state when view appears (e.g., after settings modal dismisses)
if KioskModeManager.shared.isKioskModeActive {
setNeedsStatusBarAppearanceUpdate()
navigationController?.setNeedsStatusBarAppearanceUpdate()
}
}

override func viewWillDisappear(_ animated: Bool) {
Expand Down
18 changes: 17 additions & 1 deletion Sources/App/Frontend/WebView/WebViewWindowController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,22 @@ import Shared
import SwiftUI
import UIKit

/// Navigation controller that forwards status bar and home indicator preferences to its top view controller.
/// This is needed for kiosk mode to properly hide the status bar when WebViewController is embedded.
final class StatusBarForwardingNavigationController: UINavigationController {
override var childForStatusBarHidden: UIViewController? {
topViewController
}

override var childForStatusBarStyle: UIViewController? {
topViewController
}

override var childForHomeIndicatorAutoHidden: UIViewController? {
topViewController
}
}

final class WebViewWindowController {
enum RootViewControllerType {
case onboarding
Expand Down Expand Up @@ -55,7 +71,7 @@ final class WebViewWindowController {
}

private func webViewNavigationController(rootViewController: UIViewController? = nil) -> UINavigationController {
let navigationController = UINavigationController()
let navigationController = StatusBarForwardingNavigationController()
navigationController.setNavigationBarHidden(true, animated: false)

if let rootViewController {
Expand Down
38 changes: 38 additions & 0 deletions Sources/App/Kiosk/KioskConstants.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import CoreGraphics
import Foundation
import Shared

// MARK: - Kiosk Constants

/// Centralized constants for the kiosk mode
public enum KioskConstants {
// MARK: - Animation Durations

public enum Animation {
/// Standard transition animation duration
public static let standard: TimeInterval = 0.3
/// Quick animation for subtle transitions
public static let quick: TimeInterval = 0.2
/// Slow animation for screensaver transitions
public static let slow: TimeInterval = 0.5
/// Pixel shift animation duration
public static let pixelShift: TimeInterval = 1.0
}

// MARK: - UI Dimensions

public enum UI {
/// Standard corner radius (uses DesignSystem)
public static let cornerRadius: CGFloat = DesignSystem.CornerRadius.oneAndHalf
/// Small corner radius (uses DesignSystem)
public static let smallCornerRadius: CGFloat = DesignSystem.CornerRadius.one
/// Large clock font size
public static let largeClockFontSize: CGFloat = 120
/// Minimal clock font size
public static let minimalClockFontSize: CGFloat = 80
/// Digital clock font size
public static let digitalClockFontSize: CGFloat = 100
/// Analog clock size
public static let analogClockSize: CGFloat = 300
}
}
Loading