From cdccfb9e3e720e6db642a4d4bc21005912e8de90 Mon Sep 17 00:00:00 2001 From: fmodf Date: Wed, 3 Jul 2024 12:47:59 +0200 Subject: [PATCH] wip --- .../Attachments/AttachmentMediaManager.swift | 91 ++++++---- .../AttachmentMediaPickerView.swift | 162 +++++++++++++----- 2 files changed, 182 insertions(+), 71 deletions(-) diff --git a/ConversationsClassic/View/Screens/Attachments/AttachmentMediaManager.swift b/ConversationsClassic/View/Screens/Attachments/AttachmentMediaManager.swift index c5a9a8d..bef45d6 100644 --- a/ConversationsClassic/View/Screens/Attachments/AttachmentMediaManager.swift +++ b/ConversationsClassic/View/Screens/Attachments/AttachmentMediaManager.swift @@ -4,54 +4,61 @@ import SwiftUI import UIKit class MediaManager: NSObject, ObservableObject { - @Published var photos: [UIImage] = [] + // @Published var photos: [UIImage] = [] @Published var cameraFeed: UIImage? + // @Published var galleryAccessLevel: PHAuthorizationStatus = .notDetermined + @Published var cameraAccessLevel: AVAuthorizationStatus = AVCaptureDevice.authorizationStatus(for: .video) + override init() { super.init() - DispatchQueue.global(qos: .userInitiated).async { [weak self] in - self?.fetchPhotos() - self?.setupCameraFeed() - } + NotificationCenter.default.addObserver(self, selector: #selector(appDidBecomeActive), name: UIApplication.didBecomeActiveNotification, object: nil) + + // DispatchQueue.main.async { [weak self] in + // // self?.fetchPhotos() + // } } - private func fetchPhotos() { - let fetchOptions = PHFetchOptions() - fetchOptions.sortDescriptors = [NSSortDescriptor(key: "creationDate", ascending: false)] - let assets = PHAsset.fetchAssets(with: .image, options: fetchOptions) - - let manager = PHImageManager.default() - let option = PHImageRequestOptions() - option.isSynchronous = true - - assets.enumerateObjects { asset, _, _ in - manager.requestImage(for: asset, targetSize: CGSize(width: 200, height: 200), contentMode: .aspectFill, options: option) { image, _ in - if let image = image { - DispatchQueue.main.async { - self.photos.append(image) - } - } - } - } - } + // private func fetchPhotos() { + // galleryAccessLevel = PHPhotoLibrary.authorizationStatus() + // + // let fetchOptions = PHFetchOptions() + // fetchOptions.sortDescriptors = [NSSortDescriptor(key: "creationDate", ascending: false)] + // let assets = PHAsset.fetchAssets(with: .image, options: fetchOptions) + // + // let manager = PHImageManager.default() + // let option = PHImageRequestOptions() + // option.isSynchronous = true + // + // assets.enumerateObjects { asset, _, _ in + // manager.requestImage(for: asset, targetSize: CGSize(width: 200, height: 200), contentMode: .aspectFill, options: option) { image, _ in + // if let image = image { + // DispatchQueue.main.async { + // self.photos.append(image) + // } + // } + // } + // } + // } private func setupCameraFeed() { + cameraAccessLevel = AVCaptureDevice.authorizationStatus(for: .video) + let captureSession = AVCaptureSession() captureSession.sessionPreset = .medium - guard let captureDevice = AVCaptureDevice.default(for: .video) else { + guard let backCamera = AVCaptureDevice.default(.builtInWideAngleCamera, for: .video, position: .back) else { + print("Unable to access the back camera!") return } - let deviceInput: AVCaptureDeviceInput do { - deviceInput = try AVCaptureDeviceInput(device: captureDevice) + let input = try AVCaptureDeviceInput(device: backCamera) + if captureSession.canAddInput(input) { + captureSession.addInput(input) + } } catch { - return - } - - if captureSession.canAddInput(deviceInput) { - captureSession.addInput(deviceInput) + print("Error Unable to initialize back camera: \(error.localizedDescription)") } let videoOutput = AVCaptureVideoDataOutput() @@ -66,6 +73,7 @@ class MediaManager: NSObject, ObservableObject { extension MediaManager: AVCaptureVideoDataOutputSampleBufferDelegate { func captureOutput(_: AVCaptureOutput, didOutput sampleBuffer: CMSampleBuffer, from _: AVCaptureConnection) { + print("Capturing output started") guard let pixelBuffer = CMSampleBufferGetImageBuffer(sampleBuffer) else { return } @@ -78,6 +86,25 @@ extension MediaManager: AVCaptureVideoDataOutputSampleBufferDelegate { DispatchQueue.main.async { self.cameraFeed = UIImage(cgImage: cgImage) + print("Updated camera feed") } } } + +extension MediaManager { + func openAppSettings() { + if + let appSettingsUrl = URL(string: UIApplication.openSettingsURLString), + UIApplication.shared.canOpenURL(appSettingsUrl) + { + UIApplication.shared.open(appSettingsUrl, completionHandler: nil) + } + } + + @objc private func appDidBecomeActive() { + // Update access levels + // galleryAccessLevel = PHPhotoLibrary.authorizationStatus() + cameraAccessLevel = AVCaptureDevice.authorizationStatus(for: .video) + setupCameraFeed() + } +} diff --git a/ConversationsClassic/View/Screens/Attachments/AttachmentMediaPickerView.swift b/ConversationsClassic/View/Screens/Attachments/AttachmentMediaPickerView.swift index 45619b5..8396a4e 100644 --- a/ConversationsClassic/View/Screens/Attachments/AttachmentMediaPickerView.swift +++ b/ConversationsClassic/View/Screens/Attachments/AttachmentMediaPickerView.swift @@ -1,52 +1,136 @@ import SwiftUI struct AttachmentMediaPickerView: View { - // @StateObject private var mediaManager = MediaManager() - - var hasCam: Bool = false - - let elements = ["A", "B", "C", "D", "E", "F", "G", "H", "I", "J"] + @StateObject private var mediaManager = MediaManager() var body: some View { ScrollView { LazyVGrid(columns: Array(repeating: .init(.flexible()), count: 3)) { - ForEach(0 ..< 10) { index in - if index == 0 { - VStack { - Color.red - .frame(height: 100) - Color.red - .frame(height: 100) - } - } else { - Color.blue - .frame(height: 100) - } + ForEach(elements) { element in + element } } - .padding(.horizontal) - // LazyVGrid(columns: [GridItem(), GridItem(), GridItem()]) { - // if let cameraFeed = mediaManager.cameraFeed { - // Button(action: { - // isPickerPresented = true - // }) { - // Image(uiImage: cameraFeed) - // .resizable() - // .aspectRatio(contentMode: .fill) - // } - // } - // - // ForEach(mediaManager.photos, id: \.self) { photo in - // Button(action: { - // selectedPhoto = photo - // }) { - // Image(uiImage: photo) - // .resizable() - // .aspectRatio(contentMode: .fill) - // } - // } - // } + .padding(.horizontal, 8) } + .padding(.vertical, 8) + } + + private var elements: [GridElement] { + print("Creating elements") + var result: [GridElement] = [] + + // camera + if let feed = mediaManager.cameraFeed, mediaManager.cameraAccessLevel == .authorized { + result.append(GridElement(id: UUID(), type: .cameraFeed, content: feed) { + print("Go to capture???") + }) + print("Added camera feed") + } else if mediaManager.cameraAccessLevel == .restricted { + result.append(GridElement(id: UUID(), type: .cameraRestricted, content: nil) { + print("Show alert") + }) + print("Added camera restricted") + } else { + result.append(GridElement(id: UUID(), type: .cameraAskButton, content: nil) { + mediaManager.openAppSettings() + }) + print("Added camera ask button") + } + + // photos + // if mediaManager.galleryAccessLevel == .restricted { + // result.append(GridElement(id: UUID(), type: .photoRestricted, content: nil)) + // } else { + // for photo in mediaManager.photos { + // result.append(GridElement(id: UUID(), type: .photo, content: photo)) + // } + // if mediaManager.galleryAccessLevel != .authorized { + // result.append(GridElement(id: UUID(), type: .photoAskButton, content: nil)) + // } + // } + + return result + } +} + +private enum GridElementType { + case cameraFeed + case cameraAskButton + case cameraRestricted + case photo + case photoAskButton + case photoRestricted +} + +private struct GridElement: View, Identifiable { + let id: UUID + let type: GridElementType + let content: UIImage? + let action: () -> Void + + var body: some View { + switch type { + case .cameraFeed: + image + .resizable() + .aspectRatio(contentMode: .fill) + .frame(width: 100, height: 100) + .clipped() + + case .cameraAskButton: + Button { + action() + } label: { + RoundedRectangle(cornerRadius: 5) + .stroke(Color.Main.backgroundDark, lineWidth: 2) + .overlay { + Image(systemName: "camera") + .foregroundColor(.Material.tortoiseLight300) + .font(.system(size: 40)) + } + .frame(height: 100) + // .resizable() + // .aspectRatio(contentMode: .fill) + // .frame(width: 100, height: 100) + // .clipped() + } + + case .photo: + image + .resizable() + .aspectRatio(contentMode: .fill) + .frame(width: 100, height: 100) + .clipped() + + case .photoAskButton: + Button { + action() + } label: { + Image(systemName: "photo.badge.plus") + .resizable() + .aspectRatio(contentMode: .fill) + .frame(width: 100, height: 100) + .clipped() + } + + case .photoRestricted, .cameraRestricted: + Button { + action() + } label: { + Image(systemName: "cross") + .resizable() + .aspectRatio(contentMode: .fill) + .frame(width: 100, height: 100) + .clipped() + } + } + } + + private var image: Image { + guard let content = content else { + return Image(systemName: "questionmark.square.dashed") + } + return Image(uiImage: content) } }