general: basic cleanup
This commit is contained in:
@@ -28,11 +28,8 @@ class AppDelegate: NSObject, NSApplicationDelegate, ObservableObject {
|
|||||||
self.windowManager = WindowManager.shared
|
self.windowManager = WindowManager.shared
|
||||||
super.init()
|
super.init()
|
||||||
|
|
||||||
// Setup window close observers
|
|
||||||
setupWindowCloseObservers()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Initializer for testing with injectable dependencies
|
|
||||||
init(serviceContainer: ServiceContainer, windowManager: WindowManaging) {
|
init(serviceContainer: ServiceContainer, windowManager: WindowManaging) {
|
||||||
self.serviceContainer = serviceContainer
|
self.serviceContainer = serviceContainer
|
||||||
self.windowManager = windowManager
|
self.windowManager = windowManager
|
||||||
@@ -40,16 +37,12 @@ class AppDelegate: NSObject, NSApplicationDelegate, ObservableObject {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func applicationDidFinishLaunching(_ notification: Notification) {
|
func applicationDidFinishLaunching(_ notification: Notification) {
|
||||||
// Set activation policy to hide dock icon
|
|
||||||
NSApplication.shared.setActivationPolicy(.accessory)
|
NSApplication.shared.setActivationPolicy(.accessory)
|
||||||
|
|
||||||
logInfo("🚀 Application did finish launching")
|
|
||||||
|
|
||||||
timerEngine = serviceContainer.timerEngine
|
timerEngine = serviceContainer.timerEngine
|
||||||
|
|
||||||
serviceContainer.setupSmartModeServices()
|
serviceContainer.setupSmartModeServices()
|
||||||
|
|
||||||
|
|
||||||
// Initialize update manager after onboarding is complete
|
// Initialize update manager after onboarding is complete
|
||||||
if settingsManager.settings.hasCompletedOnboarding {
|
if settingsManager.settings.hasCompletedOnboarding {
|
||||||
updateManager = UpdateManager.shared
|
updateManager = UpdateManager.shared
|
||||||
@@ -62,24 +55,6 @@ class AppDelegate: NSObject, NSApplicationDelegate, ObservableObject {
|
|||||||
if settingsManager.settings.hasCompletedOnboarding {
|
if settingsManager.settings.hasCompletedOnboarding {
|
||||||
startTimers()
|
startTimers()
|
||||||
}
|
}
|
||||||
|
|
||||||
// DEBUG: Auto-start eye tracking test mode if launch argument is present
|
|
||||||
#if DEBUG
|
|
||||||
if CommandLine.arguments.contains("--debug-eye-tracking") {
|
|
||||||
NSLog("🔬 DEBUG: Auto-starting eye tracking test mode")
|
|
||||||
Task { @MainActor in
|
|
||||||
// Enable enforce mode if not already
|
|
||||||
if !settingsManager.settings.enforcementMode {
|
|
||||||
settingsManager.settings.enforcementMode = true
|
|
||||||
}
|
|
||||||
// Start test mode after a brief delay
|
|
||||||
try? await Task.sleep(nanoseconds: 1_000_000_000) // 1 second
|
|
||||||
NSLog("🔬 DEBUG: Starting test mode now...")
|
|
||||||
await EnforceModeService.shared.startTestMode()
|
|
||||||
NSLog("🔬 DEBUG: Test mode started")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Note: Smart mode setup is now handled by ServiceContainer
|
// Note: Smart mode setup is now handled by ServiceContainer
|
||||||
@@ -105,7 +80,6 @@ class AppDelegate: NSObject, NSApplicationDelegate, ObservableObject {
|
|||||||
func onboardingCompleted() {
|
func onboardingCompleted() {
|
||||||
startTimers()
|
startTimers()
|
||||||
|
|
||||||
// Start update checks after onboarding
|
|
||||||
if updateManager == nil {
|
if updateManager == nil {
|
||||||
updateManager = UpdateManager.shared
|
updateManager = UpdateManager.shared
|
||||||
}
|
}
|
||||||
@@ -249,24 +223,4 @@ class AppDelegate: NSObject, NSApplicationDelegate, ObservableObject {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func setupWindowCloseObservers() {
|
|
||||||
NotificationCenter.default.addObserver(
|
|
||||||
self,
|
|
||||||
selector: #selector(settingsWindowDidClose),
|
|
||||||
name: Notification.Name("SettingsWindowDidClose"),
|
|
||||||
object: nil
|
|
||||||
)
|
|
||||||
|
|
||||||
NotificationCenter.default.addObserver(
|
|
||||||
self,
|
|
||||||
selector: #selector(onboardingWindowDidClose),
|
|
||||||
name: Notification.Name("OnboardingWindowDidClose"),
|
|
||||||
object: nil
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
@objc private func settingsWindowDidClose() {}
|
|
||||||
|
|
||||||
@objc private func onboardingWindowDidClose() {}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ struct GazeApp: App {
|
|||||||
}
|
}
|
||||||
.windowStyle(.hiddenTitleBar)
|
.windowStyle(.hiddenTitleBar)
|
||||||
.windowResizability(.contentSize)
|
.windowResizability(.contentSize)
|
||||||
.defaultSize(width: 700, height: 700)
|
.defaultSize(width: 1000, height: 700)
|
||||||
.commands {
|
.commands {
|
||||||
CommandGroup(replacing: .newItem) {}
|
CommandGroup(replacing: .newItem) {}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,9 +5,9 @@
|
|||||||
// Fullscreen overlay view for eye tracking calibration targets.
|
// Fullscreen overlay view for eye tracking calibration targets.
|
||||||
//
|
//
|
||||||
|
|
||||||
import SwiftUI
|
|
||||||
import Combine
|
|
||||||
import AVFoundation
|
import AVFoundation
|
||||||
|
import Combine
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
struct CalibrationOverlayView: View {
|
struct CalibrationOverlayView: View {
|
||||||
@StateObject private var calibrationManager = CalibrationManager.shared
|
@StateObject private var calibrationManager = CalibrationManager.shared
|
||||||
@@ -35,7 +35,9 @@ struct CalibrationOverlayView: View {
|
|||||||
startingCameraView
|
startingCameraView
|
||||||
} else if calibrationManager.isCalibrating {
|
} else if calibrationManager.isCalibrating {
|
||||||
calibrationContentView(screenSize: geometry.size)
|
calibrationContentView(screenSize: geometry.size)
|
||||||
} else if viewModel.calibrationStarted && calibrationManager.calibrationData.isComplete {
|
} else if viewModel.calibrationStarted
|
||||||
|
&& calibrationManager.calibrationData.isComplete
|
||||||
|
{
|
||||||
// Only show completion if we started calibration this session AND it completed
|
// Only show completion if we started calibration this session AND it completed
|
||||||
completionView
|
completionView
|
||||||
} else if viewModel.calibrationStarted {
|
} else if viewModel.calibrationStarted {
|
||||||
@@ -45,10 +47,12 @@ struct CalibrationOverlayView: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
.task {
|
.task {
|
||||||
await viewModel.startCamera(eyeTrackingService: eyeTrackingService, calibrationManager: calibrationManager)
|
await viewModel.startCamera(
|
||||||
|
eyeTrackingService: eyeTrackingService, calibrationManager: calibrationManager)
|
||||||
}
|
}
|
||||||
.onDisappear {
|
.onDisappear {
|
||||||
viewModel.cleanup(eyeTrackingService: eyeTrackingService, calibrationManager: calibrationManager)
|
viewModel.cleanup(
|
||||||
|
eyeTrackingService: eyeTrackingService, calibrationManager: calibrationManager)
|
||||||
}
|
}
|
||||||
.onChange(of: calibrationManager.currentStep) { oldStep, newStep in
|
.onChange(of: calibrationManager.currentStep) { oldStep, newStep in
|
||||||
if newStep != nil && oldStep != newStep {
|
if newStep != nil && oldStep != newStep {
|
||||||
@@ -200,14 +204,16 @@ struct CalibrationOverlayView: View {
|
|||||||
.stroke(Color.green, lineWidth: 4)
|
.stroke(Color.green, lineWidth: 4)
|
||||||
.frame(width: 90, height: 90)
|
.frame(width: 90, height: 90)
|
||||||
.rotationEffect(.degrees(-90))
|
.rotationEffect(.degrees(-90))
|
||||||
.animation(.linear(duration: 0.1), value: calibrationManager.samplesCollected)
|
.animation(
|
||||||
|
.linear(duration: 0.1), value: calibrationManager.samplesCollected)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Inner circle
|
// Inner circle
|
||||||
Circle()
|
Circle()
|
||||||
.fill(calibrationManager.isCollectingSamples ? Color.green : Color.blue)
|
.fill(calibrationManager.isCollectingSamples ? Color.green : Color.blue)
|
||||||
.frame(width: 60, height: 60)
|
.frame(width: 60, height: 60)
|
||||||
.animation(.easeInOut(duration: 0.3), value: calibrationManager.isCollectingSamples)
|
.animation(
|
||||||
|
.easeInOut(duration: 0.3), value: calibrationManager.isCollectingSamples)
|
||||||
|
|
||||||
// Countdown number or collecting indicator
|
// Countdown number or collecting indicator
|
||||||
if viewModel.isCountingDown && viewModel.countdownValue > 0 {
|
if viewModel.isCountingDown && viewModel.countdownValue > 0 {
|
||||||
@@ -260,7 +266,8 @@ struct CalibrationOverlayView: View {
|
|||||||
|
|
||||||
private var cancelButton: some View {
|
private var cancelButton: some View {
|
||||||
Button {
|
Button {
|
||||||
viewModel.cleanup(eyeTrackingService: eyeTrackingService, calibrationManager: calibrationManager)
|
viewModel.cleanup(
|
||||||
|
eyeTrackingService: eyeTrackingService, calibrationManager: calibrationManager)
|
||||||
onDismiss()
|
onDismiss()
|
||||||
} label: {
|
} label: {
|
||||||
HStack(spacing: 6) {
|
HStack(spacing: 6) {
|
||||||
@@ -362,7 +369,9 @@ class CalibrationOverlayViewModel: ObservableObject {
|
|||||||
private var lastFaceDetectedTime: Date = .distantPast
|
private var lastFaceDetectedTime: Date = .distantPast
|
||||||
private let faceDetectionDebounce: TimeInterval = 0.5 // 500ms debounce
|
private let faceDetectionDebounce: TimeInterval = 0.5 // 500ms debounce
|
||||||
|
|
||||||
func startCamera(eyeTrackingService: EyeTrackingService, calibrationManager: CalibrationManager) async {
|
func startCamera(eyeTrackingService: EyeTrackingService, calibrationManager: CalibrationManager)
|
||||||
|
async
|
||||||
|
{
|
||||||
do {
|
do {
|
||||||
try await eyeTrackingService.startEyeTracking()
|
try await eyeTrackingService.startEyeTracking()
|
||||||
cameraStarted = true
|
cameraStarted = true
|
||||||
@@ -370,8 +379,7 @@ class CalibrationOverlayViewModel: ObservableObject {
|
|||||||
// Set up debounced face detection
|
// Set up debounced face detection
|
||||||
setupFaceDetectionObserver(eyeTrackingService: eyeTrackingService)
|
setupFaceDetectionObserver(eyeTrackingService: eyeTrackingService)
|
||||||
|
|
||||||
// Small delay to let camera stabilize
|
try? await Task.sleep(for: .seconds(0.5))
|
||||||
try? await Task.sleep(nanoseconds: 500_000_000)
|
|
||||||
|
|
||||||
// Reset any previous calibration data before starting fresh
|
// Reset any previous calibration data before starting fresh
|
||||||
calibrationManager.resetForNewCalibration()
|
calibrationManager.resetForNewCalibration()
|
||||||
@@ -446,4 +454,3 @@ class CalibrationOverlayViewModel: ObservableObject {
|
|||||||
#Preview {
|
#Preview {
|
||||||
CalibrationOverlayView(onDismiss: {})
|
CalibrationOverlayView(onDismiss: {})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -83,7 +83,9 @@ final class OnboardingWindowPresenter {
|
|||||||
window.titlebarAppearsTransparent = true
|
window.titlebarAppearsTransparent = true
|
||||||
window.center()
|
window.center()
|
||||||
window.isReleasedWhenClosed = true
|
window.isReleasedWhenClosed = true
|
||||||
window.collectionBehavior = [.managed, .participatesInCycle, .moveToActiveSpace, .fullScreenAuxiliary]
|
window.collectionBehavior = [
|
||||||
|
.managed, .participatesInCycle, .moveToActiveSpace, .fullScreenAuxiliary,
|
||||||
|
]
|
||||||
|
|
||||||
window.contentView = NSHostingView(
|
window.contentView = NSHostingView(
|
||||||
rootView: OnboardingContainerView(settingsManager: settingsManager)
|
rootView: OnboardingContainerView(settingsManager: settingsManager)
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
import XCTest
|
import XCTest
|
||||||
|
|
||||||
@testable import Gaze
|
@testable import Gaze
|
||||||
|
|
||||||
@MainActor
|
@MainActor
|
||||||
@@ -30,20 +31,16 @@ final class AppDelegateTestabilityTests: XCTestCase {
|
|||||||
func testWindowManagerReceivesReminderEvents() async throws {
|
func testWindowManagerReceivesReminderEvents() async throws {
|
||||||
let appDelegate = testEnv.createAppDelegate()
|
let appDelegate = testEnv.createAppDelegate()
|
||||||
|
|
||||||
// Simulate app launch
|
|
||||||
let notification = Notification(name: NSApplication.didFinishLaunchingNotification)
|
let notification = Notification(name: NSApplication.didFinishLaunchingNotification)
|
||||||
appDelegate.applicationDidFinishLaunching(notification)
|
appDelegate.applicationDidFinishLaunching(notification)
|
||||||
|
|
||||||
// Give time for setup
|
try await Task.sleep(for: .milliseconds(100))
|
||||||
try await Task.sleep(nanoseconds: 100_000_000) // 100ms
|
|
||||||
|
|
||||||
// Trigger a reminder through timer engine
|
|
||||||
if let timerEngine = appDelegate.timerEngine {
|
if let timerEngine = appDelegate.timerEngine {
|
||||||
let timerId = TimerIdentifier.builtIn(.blink)
|
let timerId = TimerIdentifier.builtIn(.blink)
|
||||||
timerEngine.triggerReminder(for: timerId)
|
timerEngine.triggerReminder(for: timerId)
|
||||||
|
|
||||||
// Give time for reminder to propagate
|
try await Task.sleep(for: .milliseconds(100))
|
||||||
try await Task.sleep(nanoseconds: 100_000_000) // 100ms
|
|
||||||
|
|
||||||
// Verify window manager received the show command
|
// Verify window manager received the show command
|
||||||
XCTAssertTrue(testEnv.windowManager.didPerformOperation(.showSubtleReminder))
|
XCTAssertTrue(testEnv.windowManager.didPerformOperation(.showSubtleReminder))
|
||||||
@@ -58,8 +55,7 @@ final class AppDelegateTestabilityTests: XCTestCase {
|
|||||||
// Change a setting
|
// Change a setting
|
||||||
testEnv.settingsManager.settings.lookAwayTimer.enabled = false
|
testEnv.settingsManager.settings.lookAwayTimer.enabled = false
|
||||||
|
|
||||||
// Give time for observation
|
try await Task.sleep(for: .milliseconds(50))
|
||||||
try await Task.sleep(nanoseconds: 50_000_000) // 50ms
|
|
||||||
|
|
||||||
// Verify the change propagated
|
// Verify the change propagated
|
||||||
XCTAssertFalse(testEnv.settingsManager.settings.lookAwayTimer.enabled)
|
XCTAssertFalse(testEnv.settingsManager.settings.lookAwayTimer.enabled)
|
||||||
@@ -73,7 +69,8 @@ final class AppDelegateTestabilityTests: XCTestCase {
|
|||||||
// Give time for async dispatch
|
// Give time for async dispatch
|
||||||
let expectation = XCTestExpectation(description: "Settings opened")
|
let expectation = XCTestExpectation(description: "Settings opened")
|
||||||
DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) {
|
DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) {
|
||||||
XCTAssertTrue(self.testEnv.windowManager.didPerformOperation(.showSettings(initialTab: 2)))
|
XCTAssertTrue(
|
||||||
|
self.testEnv.windowManager.didPerformOperation(.showSettings(initialTab: 2)))
|
||||||
expectation.fulfill()
|
expectation.fulfill()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
|
|
||||||
import Combine
|
import Combine
|
||||||
import XCTest
|
import XCTest
|
||||||
|
|
||||||
@testable import Gaze
|
@testable import Gaze
|
||||||
|
|
||||||
@MainActor
|
@MainActor
|
||||||
@@ -159,8 +160,7 @@ final class TimerEngineTests: XCTestCase {
|
|||||||
|
|
||||||
timerEngine.triggerReminder(for: firstTimer)
|
timerEngine.triggerReminder(for: firstTimer)
|
||||||
|
|
||||||
// Give time for async operations
|
try await Task.sleep(for: .milliseconds(50))
|
||||||
try await Task.sleep(nanoseconds: 50_000_000)
|
|
||||||
|
|
||||||
XCTAssertNotNil(timerEngine.activeReminder)
|
XCTAssertNotNil(timerEngine.activeReminder)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,8 +5,11 @@
|
|||||||
// Test helpers and utilities for unit testing.
|
// Test helpers and utilities for unit testing.
|
||||||
//
|
//
|
||||||
|
|
||||||
|
// MARK: - Import Statement for Combine
|
||||||
|
import Combine
|
||||||
import Foundation
|
import Foundation
|
||||||
import XCTest
|
import XCTest
|
||||||
|
|
||||||
@testable import Gaze
|
@testable import Gaze
|
||||||
|
|
||||||
// MARK: - Enhanced MockSettingsManager
|
// MARK: - Enhanced MockSettingsManager
|
||||||
@@ -25,7 +28,8 @@ final class EnhancedMockSettingsManager: SettingsProviding {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@ObservationIgnored
|
@ObservationIgnored
|
||||||
private let timerConfigKeyPaths: [TimerType: WritableKeyPath<AppSettings, TimerConfiguration>] = [
|
private let timerConfigKeyPaths: [TimerType: WritableKeyPath<AppSettings, TimerConfiguration>] =
|
||||||
|
[
|
||||||
.lookAway: \.lookAwayTimer,
|
.lookAway: \.lookAwayTimer,
|
||||||
.blink: \.blinkTimer,
|
.blink: \.blinkTimer,
|
||||||
.posture: \.postureTimer,
|
.posture: \.postureTimer,
|
||||||
@@ -245,7 +249,7 @@ extension XCTestCase {
|
|||||||
XCTFail(message)
|
XCTFail(message)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
try await Task.sleep(nanoseconds: 10_000_000) // 10ms
|
try await Task.sleep(for: .milliseconds(100))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -269,6 +273,3 @@ extension XCTestCase {
|
|||||||
cancellable?.cancel()
|
cancellable?.cancel()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Import Statement for Combine
|
|
||||||
import Combine
|
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
|
|
||||||
import Combine
|
import Combine
|
||||||
import XCTest
|
import XCTest
|
||||||
|
|
||||||
@testable import Gaze
|
@testable import Gaze
|
||||||
|
|
||||||
@MainActor
|
@MainActor
|
||||||
@@ -106,8 +107,7 @@ final class TimerEngineTestabilityTests: XCTestCase {
|
|||||||
let timerId = TimerIdentifier.builtIn(.lookAway)
|
let timerId = TimerIdentifier.builtIn(.lookAway)
|
||||||
timerEngine.triggerReminder(for: timerId)
|
timerEngine.triggerReminder(for: timerId)
|
||||||
|
|
||||||
// Give time for publisher to fire
|
try await Task.sleep(for: .milliseconds(10))
|
||||||
try await Task.sleep(nanoseconds: 10_000_000)
|
|
||||||
|
|
||||||
XCTAssertNotNil(receivedReminder)
|
XCTAssertNotNil(receivedReminder)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user