fix: preview fixed
This commit is contained in:
@@ -27,6 +27,7 @@ class EnforceModeService: ObservableObject {
|
|||||||
self.settingsManager = SettingsManager.shared
|
self.settingsManager = SettingsManager.shared
|
||||||
self.eyeTrackingService = EyeTrackingService.shared
|
self.eyeTrackingService = EyeTrackingService.shared
|
||||||
setupObservers()
|
setupObservers()
|
||||||
|
initializeEnforceModeState()
|
||||||
}
|
}
|
||||||
|
|
||||||
private func setupObservers() {
|
private func setupObservers() {
|
||||||
@@ -37,6 +38,20 @@ class EnforceModeService: ObservableObject {
|
|||||||
.store(in: &cancellables)
|
.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 {
|
func enableEnforceMode() async {
|
||||||
print("🔒 enableEnforceMode called")
|
print("🔒 enableEnforceMode called")
|
||||||
guard !isEnforceModeEnabled else {
|
guard !isEnforceModeEnabled else {
|
||||||
|
|||||||
@@ -21,11 +21,23 @@ class EyeTrackingService: NSObject, ObservableObject {
|
|||||||
private var captureSession: AVCaptureSession?
|
private var captureSession: AVCaptureSession?
|
||||||
private var videoOutput: AVCaptureVideoDataOutput?
|
private var videoOutput: AVCaptureVideoDataOutput?
|
||||||
private let videoDataOutputQueue = DispatchQueue(label: "com.gaze.videoDataOutput", qos: .userInitiated)
|
private let videoDataOutputQueue = DispatchQueue(label: "com.gaze.videoDataOutput", qos: .userInitiated)
|
||||||
|
private var _previewLayer: AVCaptureVideoPreviewLayer?
|
||||||
|
|
||||||
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)
|
let layer = AVCaptureVideoPreviewLayer(session: session)
|
||||||
layer.videoGravity = .resizeAspectFill
|
layer.videoGravity = .resizeAspectFill
|
||||||
|
_previewLayer = layer
|
||||||
return layer
|
return layer
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -66,6 +78,7 @@ class EyeTrackingService: NSObject, ObservableObject {
|
|||||||
captureSession?.stopRunning()
|
captureSession?.stopRunning()
|
||||||
captureSession = nil
|
captureSession = nil
|
||||||
videoOutput = nil
|
videoOutput = nil
|
||||||
|
_previewLayer = nil
|
||||||
isEyeTrackingActive = false
|
isEyeTrackingActive = false
|
||||||
isEyesClosed = false
|
isEyesClosed = false
|
||||||
userLookingAtScreen = true
|
userLookingAtScreen = true
|
||||||
|
|||||||
@@ -12,20 +12,36 @@ struct CameraPreviewView: NSViewRepresentable {
|
|||||||
let previewLayer: AVCaptureVideoPreviewLayer
|
let previewLayer: AVCaptureVideoPreviewLayer
|
||||||
let borderColor: NSColor
|
let borderColor: NSColor
|
||||||
|
|
||||||
func makeNSView(context: Context) -> NSView {
|
func makeNSView(context: Context) -> PreviewContainerView {
|
||||||
let view = PreviewContainerView()
|
let view = PreviewContainerView()
|
||||||
view.wantsLayer = true
|
view.wantsLayer = true
|
||||||
|
|
||||||
|
// Add the preview layer once
|
||||||
|
if view.layer?.sublayers?.first as? AVCaptureVideoPreviewLayer !== previewLayer {
|
||||||
|
view.layer?.sublayers?.forEach { $0.removeFromSuperlayer() }
|
||||||
previewLayer.frame = view.bounds
|
previewLayer.frame = view.bounds
|
||||||
view.layer?.addSublayer(previewLayer)
|
view.layer?.addSublayer(previewLayer)
|
||||||
|
}
|
||||||
|
|
||||||
updateBorder(view: view, color: borderColor)
|
updateBorder(view: view, color: borderColor)
|
||||||
|
|
||||||
return view
|
return view
|
||||||
}
|
}
|
||||||
|
|
||||||
func updateNSView(_ nsView: NSView, context: Context) {
|
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
|
previewLayer.frame = nsView.bounds
|
||||||
|
nsView.layer?.addSublayer(previewLayer)
|
||||||
|
} else {
|
||||||
|
// Same layer, just update frame
|
||||||
|
previewLayer.frame = nsView.bounds
|
||||||
|
}
|
||||||
|
|
||||||
updateBorder(view: nsView, color: borderColor)
|
updateBorder(view: nsView, color: borderColor)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
// Created by Mike Freno on 1/13/26.
|
// Created by Mike Freno on 1/13/26.
|
||||||
//
|
//
|
||||||
|
|
||||||
|
import AVFoundation
|
||||||
import SwiftUI
|
import SwiftUI
|
||||||
|
|
||||||
struct EnforceModeSetupView: View {
|
struct EnforceModeSetupView: View {
|
||||||
@@ -15,6 +16,7 @@ struct EnforceModeSetupView: View {
|
|||||||
|
|
||||||
@State private var isProcessingToggle = false
|
@State private var isProcessingToggle = false
|
||||||
@State private var isTestModeActive = false
|
@State private var isTestModeActive = false
|
||||||
|
@State private var cachedPreviewLayer: AVCaptureVideoPreviewLayer?
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
VStack(spacing: 0) {
|
VStack(spacing: 0) {
|
||||||
@@ -102,9 +104,13 @@ struct EnforceModeSetupView: View {
|
|||||||
if isTestModeActive {
|
if isTestModeActive {
|
||||||
enforceModeService.stopTestMode()
|
enforceModeService.stopTestMode()
|
||||||
isTestModeActive = false
|
isTestModeActive = false
|
||||||
|
cachedPreviewLayer = nil
|
||||||
} else {
|
} else {
|
||||||
await enforceModeService.startTestMode()
|
await enforceModeService.startTestMode()
|
||||||
isTestModeActive = enforceModeService.isCameraActive
|
isTestModeActive = enforceModeService.isCameraActive
|
||||||
|
if isTestModeActive {
|
||||||
|
cachedPreviewLayer = eyeTrackingService.previewLayer
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}) {
|
}) {
|
||||||
@@ -123,13 +129,21 @@ struct EnforceModeSetupView: View {
|
|||||||
|
|
||||||
private var testModePreviewView: some View {
|
private var testModePreviewView: some View {
|
||||||
VStack(spacing: 16) {
|
VStack(spacing: 16) {
|
||||||
if let previewLayer = eyeTrackingService.previewLayer {
|
|
||||||
let lookingAway = !eyeTrackingService.userLookingAtScreen
|
let lookingAway = !eyeTrackingService.userLookingAtScreen
|
||||||
let borderColor: NSColor = lookingAway ? .systemGreen : .systemRed
|
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)
|
.frame(height: 300)
|
||||||
.glassEffectIfAvailable(GlassStyle.regular, in: .rect(cornerRadius: 12))
|
.glassEffectIfAvailable(GlassStyle.regular, in: .rect(cornerRadius: 12))
|
||||||
|
.onAppear {
|
||||||
|
if cachedPreviewLayer == nil {
|
||||||
|
cachedPreviewLayer = eyeTrackingService.previewLayer
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
VStack(alignment: .leading, spacing: 12) {
|
VStack(alignment: .leading, spacing: 12) {
|
||||||
Text("Live Tracking Status")
|
Text("Live Tracking Status")
|
||||||
@@ -149,7 +163,10 @@ struct EnforceModeSetupView: View {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
Text(lookingAway ? "✓ Break compliance detected" : "⚠️ Please look away from screen")
|
Text(
|
||||||
|
lookingAway
|
||||||
|
? "✓ Break compliance detected" : "⚠️ Please look away from screen"
|
||||||
|
)
|
||||||
.font(.caption)
|
.font(.caption)
|
||||||
.foregroundColor(lookingAway ? .green : .orange)
|
.foregroundColor(lookingAway ? .green : .orange)
|
||||||
.frame(maxWidth: .infinity, alignment: .center)
|
.frame(maxWidth: .infinity, alignment: .center)
|
||||||
@@ -281,7 +298,8 @@ struct EnforceModeSetupView: View {
|
|||||||
.foregroundColor(.secondary)
|
.foregroundColor(.secondary)
|
||||||
}
|
}
|
||||||
.padding()
|
.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 {
|
private func privacyBullet(_ text: String) -> some View {
|
||||||
|
|||||||
Reference in New Issue
Block a user