Skip to content

Commit 3de0597

Browse files
committed
make cam/mic permission requests consistant
1 parent 4d8474d commit 3de0597

File tree

1 file changed

+108
-38
lines changed

1 file changed

+108
-38
lines changed

AllSpark-ios/CameraViewController.swift

Lines changed: 108 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -72,21 +72,15 @@ class CameraViewController: UIViewController, UIDocumentPickerDelegate, UINaviga
7272
override func viewDidAppear(_ animated: Bool) {
7373
super.viewDidAppear(animated)
7474

75-
DispatchQueue.global(qos: .userInitiated).async { [weak self] in
76-
self?.captureSession.startRunning()
77-
}
78-
79-
// Reconnect WebSocket if not connected
80-
if !isConnected && !isAttemptingConnection {
81-
setupWebSocketConnection()
82-
}
75+
// Check and request permissions each time the view appears
76+
checkAndRequestPermissions()
8377
}
8478

8579
override func viewWillDisappear(_ animated: Bool) {
8680
super.viewWillDisappear(animated)
8781

88-
if captureSession.isRunning {
89-
captureSession.stopRunning()
82+
if let session = captureSession, session.isRunning {
83+
session.stopRunning()
9084
}
9185

9286
// Close WebSocket connection
@@ -515,67 +509,143 @@ class CameraViewController: UIViewController, UIDocumentPickerDelegate, UINaviga
515509
}
516510

517511
private func setupCamera() {
518-
captureSession = AVCaptureSession()
519-
captureSession.sessionPreset = .high
512+
// Camera initialization will be handled in checkAndRequestPermissions
513+
setupFaceDetection()
514+
}
520515

521-
guard let videoCaptureDevice = AVCaptureDevice.default(.builtInWideAngleCamera, for: .video, position: currentCameraPosition) else {
522-
print("Failed to get camera device")
516+
private func setupAudioInput() {
517+
guard let audioDevice = AVCaptureDevice.default(for: .audio) else {
518+
print("Failed to get audio device")
523519
return
524520
}
525521

526-
guard let videoInput = try? AVCaptureDeviceInput(device: videoCaptureDevice) else {
527-
print("Failed to create video input")
522+
guard let audioInput = try? AVCaptureDeviceInput(device: audioDevice) else {
523+
print("Failed to create audio input")
528524
return
529525
}
530526

531-
if captureSession.canAddInput(videoInput) {
532-
captureSession.addInput(videoInput)
527+
if captureSession.canAddInput(audioInput) {
528+
captureSession.addInput(audioInput)
533529
}
534530

535-
videoOutput = AVCaptureVideoDataOutput()
536-
videoOutput.setSampleBufferDelegate(self, queue: DispatchQueue(label: "videoQueue"))
537-
videoOutput.videoSettings = [kCVPixelBufferPixelFormatTypeKey as String: kCVPixelFormatType_32BGRA]
531+
audioOutput = AVCaptureAudioDataOutput()
532+
audioOutput.setSampleBufferDelegate(self, queue: DispatchQueue(label: "audioQueue"))
538533

539-
if captureSession.canAddOutput(videoOutput) {
540-
captureSession.addOutput(videoOutput)
534+
if captureSession.canAddOutput(audioOutput) {
535+
captureSession.addOutput(audioOutput)
541536
}
537+
}
542538

543-
// Request microphone access and setup audio
539+
private func checkAndRequestPermissions() {
540+
let cameraStatus = AVCaptureDevice.authorizationStatus(for: .video)
541+
542+
switch cameraStatus {
543+
case .authorized:
544+
// Camera permission already granted
545+
initializeCameraIfNeeded()
546+
requestMicrophonePermission()
547+
case .denied, .restricted:
548+
// Permission was denied or restricted
549+
showPermissionDeniedAlert(permissionType: "Camera")
550+
case .notDetermined:
551+
// Request permission
552+
AVCaptureDevice.requestAccess(for: .video) { [weak self] granted in
553+
DispatchQueue.main.async {
554+
if granted {
555+
self?.initializeCameraIfNeeded()
556+
self?.requestMicrophonePermission()
557+
} else {
558+
self?.showPermissionDeniedAlert(permissionType: "Camera")
559+
}
560+
}
561+
}
562+
@unknown default:
563+
print("Unknown camera permission status")
564+
}
565+
}
566+
567+
private func requestMicrophonePermission() {
544568
AVAudioApplication.requestRecordPermission { [weak self] granted in
545569
if granted {
546570
DispatchQueue.main.async {
547571
self?.setupAudioInput()
548572
}
549573
} else {
550574
print("Microphone permission denied")
575+
DispatchQueue.main.async {
576+
self?.showPermissionDeniedAlert(permissionType: "Microphone")
577+
}
551578
}
552579
}
553-
554-
// Set initial video orientation
555-
updateVideoOrientation()
556580
}
557581

558-
private func setupAudioInput() {
559-
guard let audioDevice = AVCaptureDevice.default(for: .audio) else {
560-
print("Failed to get audio device")
582+
private func initializeCameraIfNeeded() {
583+
guard captureSession == nil else {
584+
// Camera already initialized, just start running
585+
if !captureSession.isRunning {
586+
DispatchQueue.global(qos: .userInitiated).async { [weak self] in
587+
self?.captureSession?.startRunning()
588+
}
589+
}
590+
// Reconnect WebSocket if not connected
591+
if !isConnected && !isAttemptingConnection {
592+
setupWebSocketConnection()
593+
}
561594
return
562595
}
563596

564-
guard let audioInput = try? AVCaptureDeviceInput(device: audioDevice) else {
565-
print("Failed to create audio input")
597+
captureSession = AVCaptureSession()
598+
captureSession.sessionPreset = .high
599+
600+
// Load saved camera position preference
601+
let savedCameraPosition = UserDefaults.standard.string(forKey: "cameraPosition") ?? "front"
602+
currentCameraPosition = savedCameraPosition == "back" ? .back : .front
603+
604+
guard let videoCaptureDevice = AVCaptureDevice.default(.builtInWideAngleCamera, for: .video, position: currentCameraPosition) else {
605+
print("Failed to get camera device")
566606
return
567607
}
568608

569-
if captureSession.canAddInput(audioInput) {
570-
captureSession.addInput(audioInput)
609+
guard let videoInput = try? AVCaptureDeviceInput(device: videoCaptureDevice) else {
610+
print("Failed to create video input")
611+
return
571612
}
572613

573-
audioOutput = AVCaptureAudioDataOutput()
574-
audioOutput.setSampleBufferDelegate(self, queue: DispatchQueue(label: "audioQueue"))
614+
if captureSession.canAddInput(videoInput) {
615+
captureSession.addInput(videoInput)
616+
}
575617

576-
if captureSession.canAddOutput(audioOutput) {
577-
captureSession.addOutput(audioOutput)
618+
videoOutput = AVCaptureVideoDataOutput()
619+
videoOutput.setSampleBufferDelegate(self, queue: DispatchQueue(label: "videoQueue"))
620+
videoOutput.videoSettings = [kCVPixelBufferPixelFormatTypeKey as String: kCVPixelFormatType_32BGRA]
621+
622+
if captureSession.canAddOutput(videoOutput) {
623+
captureSession.addOutput(videoOutput)
578624
}
625+
626+
// Set initial video orientation
627+
updateVideoOrientation()
628+
629+
// Start running
630+
DispatchQueue.global(qos: .userInitiated).async { [weak self] in
631+
self?.captureSession?.startRunning()
632+
}
633+
634+
// Reconnect WebSocket if not connected
635+
if !isConnected && !isAttemptingConnection {
636+
setupWebSocketConnection()
637+
}
638+
}
639+
640+
private func showPermissionDeniedAlert(permissionType: String) {
641+
let alert = UIAlertController(title: "\(permissionType) Permission Required", message: "This app needs \(permissionType.lowercased()) access to function properly. Please enable it in Settings.", preferredStyle: .alert)
642+
alert.addAction(UIAlertAction(title: "Go to Settings", style: .default) { _ in
643+
if let url = URL(string: UIApplication.openSettingsURLString) {
644+
UIApplication.shared.open(url)
645+
}
646+
})
647+
alert.addAction(UIAlertAction(title: "Cancel", style: .cancel))
648+
self.present(alert, animated: true)
579649
}
580650

581651
private func updateVideoOrientation() {

0 commit comments

Comments
 (0)