fix: preview fixed

This commit is contained in:
Michael Freno
2026-01-14 01:01:03 -05:00
parent 780da2951b
commit 23000589cf
4 changed files with 116 additions and 54 deletions

View File

@@ -27,6 +27,7 @@ class EnforceModeService: ObservableObject {
self.settingsManager = SettingsManager.shared
self.eyeTrackingService = EyeTrackingService.shared
setupObservers()
initializeEnforceModeState()
}
private func setupObservers() {
@@ -37,6 +38,20 @@ class EnforceModeService: ObservableObject {
.store(in: &cancellables)
}
private func initializeEnforceModeState() {
let cameraService = CameraAccessService.shared
let settingsEnabled = settingsManager.settings.enforcementMode
// If settings say it's enabled AND camera is authorized, mark as enabled
if settingsEnabled && cameraService.isCameraAuthorized {
isEnforceModeEnabled = true
print("✓ Enforce mode initialized as enabled (camera authorized)")
} else {
isEnforceModeEnabled = false
print("🔒 Enforce mode initialized as disabled")
}
}
func enableEnforceMode() async {
print("🔒 enableEnforceMode called")
guard !isEnforceModeEnabled else {

View File

@@ -21,11 +21,23 @@ class EyeTrackingService: NSObject, ObservableObject {
private var captureSession: AVCaptureSession?
private var videoOutput: AVCaptureVideoDataOutput?
private let videoDataOutputQueue = DispatchQueue(label: "com.gaze.videoDataOutput", qos: .userInitiated)
private var _previewLayer: AVCaptureVideoPreviewLayer?
var previewLayer: AVCaptureVideoPreviewLayer? {
guard let session = captureSession else { return nil }
guard let session = captureSession else {
_previewLayer = nil
return nil
}
// Reuse existing layer if session hasn't changed
if let existing = _previewLayer, existing.session === session {
return existing
}
// Create new layer only when session changes
let layer = AVCaptureVideoPreviewLayer(session: session)
layer.videoGravity = .resizeAspectFill
_previewLayer = layer
return layer
}
@@ -66,6 +78,7 @@ class EyeTrackingService: NSObject, ObservableObject {
captureSession?.stopRunning()
captureSession = nil
videoOutput = nil
_previewLayer = nil
isEyeTrackingActive = false
isEyesClosed = false
userLookingAtScreen = true

View File

@@ -12,20 +12,36 @@ struct CameraPreviewView: NSViewRepresentable {
let previewLayer: AVCaptureVideoPreviewLayer
let borderColor: NSColor
func makeNSView(context: Context) -> NSView {
func makeNSView(context: Context) -> PreviewContainerView {
let view = PreviewContainerView()
view.wantsLayer = true
previewLayer.frame = view.bounds
view.layer?.addSublayer(previewLayer)
// Add the preview layer once
if view.layer?.sublayers?.first as? AVCaptureVideoPreviewLayer !== previewLayer {
view.layer?.sublayers?.forEach { $0.removeFromSuperlayer() }
previewLayer.frame = view.bounds
view.layer?.addSublayer(previewLayer)
}
updateBorder(view: view, color: borderColor)
return view
}
func updateNSView(_ nsView: NSView, context: Context) {
previewLayer.frame = nsView.bounds
func updateNSView(_ nsView: PreviewContainerView, context: Context) {
// Only update border color and frame, don't recreate layer
let currentLayer = nsView.layer?.sublayers?.first as? AVCaptureVideoPreviewLayer
if currentLayer !== previewLayer {
// Layer changed, need to replace
nsView.layer?.sublayers?.forEach { $0.removeFromSuperlayer() }
previewLayer.frame = nsView.bounds
nsView.layer?.addSublayer(previewLayer)
} else {
// Same layer, just update frame
previewLayer.frame = nsView.bounds
}
updateBorder(view: nsView, color: borderColor)
}

View File

@@ -5,6 +5,7 @@
// Created by Mike Freno on 1/13/26.
//
import AVFoundation
import SwiftUI
struct EnforceModeSetupView: View {
@@ -15,6 +16,7 @@ struct EnforceModeSetupView: View {
@State private var isProcessingToggle = false
@State private var isTestModeActive = false
@State private var cachedPreviewLayer: AVCaptureVideoPreviewLayer?
var body: some View {
VStack(spacing: 0) {
@@ -102,9 +104,13 @@ struct EnforceModeSetupView: View {
if isTestModeActive {
enforceModeService.stopTestMode()
isTestModeActive = false
cachedPreviewLayer = nil
} else {
await enforceModeService.startTestMode()
isTestModeActive = enforceModeService.isCameraActive
if isTestModeActive {
cachedPreviewLayer = eyeTrackingService.previewLayer
}
}
}
}) {
@@ -123,13 +129,21 @@ struct EnforceModeSetupView: View {
private var testModePreviewView: some View {
VStack(spacing: 16) {
if let previewLayer = eyeTrackingService.previewLayer {
let lookingAway = !eyeTrackingService.userLookingAtScreen
let borderColor: NSColor = lookingAway ? .systemGreen : .systemRed
let lookingAway = !eyeTrackingService.userLookingAtScreen
let borderColor: NSColor = lookingAway ? .systemGreen : .systemRed
CameraPreviewView(previewLayer: previewLayer, borderColor: borderColor)
// Cache the preview layer to avoid recreating it
let previewLayer = eyeTrackingService.previewLayer ?? cachedPreviewLayer
if let layer = previewLayer {
CameraPreviewView(previewLayer: layer, borderColor: borderColor)
.frame(height: 300)
.glassEffectIfAvailable(GlassStyle.regular, in: .rect(cornerRadius: 12))
.onAppear {
if cachedPreviewLayer == nil {
cachedPreviewLayer = eyeTrackingService.previewLayer
}
}
VStack(alignment: .leading, spacing: 12) {
Text("Live Tracking Status")
@@ -149,11 +163,14 @@ struct EnforceModeSetupView: View {
)
}
Text(lookingAway ? "✓ Break compliance detected" : "⚠️ Please look away from screen")
.font(.caption)
.foregroundColor(lookingAway ? .green : .orange)
.frame(maxWidth: .infinity, alignment: .center)
.padding(.top, 4)
Text(
lookingAway
? "✓ Break compliance detected" : "⚠️ Please look away from screen"
)
.font(.caption)
.foregroundColor(lookingAway ? .green : .orange)
.frame(maxWidth: .infinity, alignment: .center)
.padding(.top, 4)
}
.padding()
.glassEffectIfAvailable(GlassStyle.regular, in: .rect(cornerRadius: 12))
@@ -281,7 +298,8 @@ struct EnforceModeSetupView: View {
.foregroundColor(.secondary)
}
.padding()
.glassEffectIfAvailable(GlassStyle.regular.tint(.blue.opacity(0.1)), in: .rect(cornerRadius: 12))
.glassEffectIfAvailable(
GlassStyle.regular.tint(.blue.opacity(0.1)), in: .rect(cornerRadius: 12))
}
private func privacyBullet(_ text: String) -> some View {