diff --git a/submodules/Camera/Sources/Camera.swift b/submodules/Camera/Sources/Camera.swift index 3de1bfe9f3..f0bf380219 100644 --- a/submodules/Camera/Sources/Camera.swift +++ b/submodules/Camera/Sources/Camera.swift @@ -119,12 +119,6 @@ private final class CameraContext { private let detectedCodesPipe = ValuePipe<[CameraCode]>() fileprivate let modeChangePromise = ValuePromise(.none) - var previewNode: CameraPreviewNode? { - didSet { - self.previewNode?.prepare() - } - } - var previewView: CameraPreviewView? var simplePreviewView: CameraSimplePreviewView? @@ -310,9 +304,7 @@ private final class CameraContext { self.mainDeviceContext?.output.processSampleBuffer = { [weak self] sampleBuffer, pixelBuffer, connection in guard let self, let mainDeviceContext = self.mainDeviceContext else { return - } - self.previewNode?.enqueue(sampleBuffer) - + } let timestamp = CACurrentMediaTime() if timestamp > self.lastSnapshotTimestamp + 2.5, !mainDeviceContext.output.isRecording { var front = false @@ -350,8 +342,6 @@ private final class CameraContext { guard let self, let mainDeviceContext = self.mainDeviceContext else { return } - self.previewNode?.enqueue(sampleBuffer) - let timestamp = CACurrentMediaTime() if timestamp > self.lastSnapshotTimestamp + 2.5, !mainDeviceContext.output.isRecording { var front = false @@ -814,20 +804,6 @@ public final class Camera { return disposable } } - - public func attachPreviewNode(_ node: CameraPreviewNode) { - let nodeRef: Unmanaged = Unmanaged.passRetained(node) - self.queue.async { - if let context = self.contextRef?.takeUnretainedValue() { - context.previewNode = nodeRef.takeUnretainedValue() - nodeRef.release() - } else { - Queue.mainQueue().async { - nodeRef.release() - } - } - } - } public func attachPreviewView(_ view: CameraPreviewView) { self.previewView = view diff --git a/submodules/Camera/Sources/CameraOutput.swift b/submodules/Camera/Sources/CameraOutput.swift index c0209e34c2..e312b335f7 100644 --- a/submodules/Camera/Sources/CameraOutput.swift +++ b/submodules/Camera/Sources/CameraOutput.swift @@ -245,7 +245,7 @@ final class CameraOutput: NSObject { } let settings = AVCapturePhotoSettings(format: [kCVPixelBufferPixelFormatTypeKey as String: Int(kCVPixelFormatType_32BGRA)]) - settings.flashMode = flashMode + settings.flashMode = mirror ? .off : flashMode if let previewPhotoPixelFormatType = settings.availablePreviewPhotoPixelFormatTypes.first { settings.previewPhotoFormat = [kCVPixelBufferPixelFormatTypeKey as String: previewPhotoPixelFormatType] } diff --git a/submodules/Camera/Sources/CameraPreviewNode.swift b/submodules/Camera/Sources/CameraPreviewNode.swift deleted file mode 100644 index 019e73a47e..0000000000 --- a/submodules/Camera/Sources/CameraPreviewNode.swift +++ /dev/null @@ -1,69 +0,0 @@ -import Foundation -import AsyncDisplayKit -import Display -import AVFoundation -import SwiftSignalKit - -private final class CameraPreviewNodeLayerNullAction: NSObject, CAAction { - @objc func run(forKey event: String, object anObject: Any, arguments dict: [AnyHashable : Any]?) { - } -} - -private final class CameraPreviewNodeLayer: AVSampleBufferDisplayLayer { - override func action(forKey event: String) -> CAAction? { - return CameraPreviewNodeLayerNullAction() - } -} - -public final class CameraPreviewNode: ASDisplayNode { - private var displayLayer: AVSampleBufferDisplayLayer - - private let fadeNode: ASDisplayNode - private var fadedIn = false - - public override init() { - self.displayLayer = AVSampleBufferDisplayLayer() - self.displayLayer.videoGravity = .resizeAspectFill - - self.fadeNode = ASDisplayNode() - self.fadeNode.backgroundColor = .black - self.fadeNode.isUserInteractionEnabled = false - - super.init() - - self.clipsToBounds = true - - self.layer.addSublayer(self.displayLayer) - - self.addSubnode(self.fadeNode) - } - - func prepare() { - DispatchQueue.main.async { - self.displayLayer.flushAndRemoveImage() - } - } - - func enqueue(_ sampleBuffer: CMSampleBuffer) { - self.displayLayer.enqueue(sampleBuffer) - - if !self.fadedIn { - self.fadedIn = true - Queue.mainQueue().after(0.2) { - self.fadeNode.alpha = 0.0 - self.fadeNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.3) - } - } - } - - override public func layout() { - super.layout() - - var transform = CGAffineTransform(rotationAngle: CGFloat.pi / 2.0) - transform = transform.scaledBy(x: 1.0, y: 1.0) - self.displayLayer.setAffineTransform(transform) - - self.displayLayer.frame = self.bounds - self.fadeNode.frame = self.bounds - } -} diff --git a/submodules/LegacyComponents/PublicHeaders/LegacyComponents/PGCameraCaptureSession.h b/submodules/LegacyComponents/PublicHeaders/LegacyComponents/PGCameraCaptureSession.h index e8854fd941..2a816abd14 100644 --- a/submodules/LegacyComponents/PublicHeaders/LegacyComponents/PGCameraCaptureSession.h +++ b/submodules/LegacyComponents/PublicHeaders/LegacyComponents/PGCameraCaptureSession.h @@ -20,6 +20,7 @@ @property (nonatomic, assign) bool alwaysSetFlash; @property (nonatomic, assign) PGCameraMode currentMode; @property (nonatomic, assign) PGCameraFlashMode currentFlashMode; +@property (nonatomic, readonly) AVCaptureFlashMode currentDeviceFlashMode; @property (nonatomic, assign) PGCameraPosition currentCameraPosition; @property (nonatomic, readonly) PGCameraPosition preferredCameraPosition; diff --git a/submodules/LegacyComponents/Sources/PGCamera.m b/submodules/LegacyComponents/Sources/PGCamera.m index bc18a47e01..2e4348a9eb 100644 --- a/submodules/LegacyComponents/Sources/PGCamera.m +++ b/submodules/LegacyComponents/Sources/PGCamera.m @@ -391,6 +391,7 @@ NSString *const PGCameraAdjustingFocusKey = @"adjustingFocus"; _currentPhotoOrientation = orientation; AVCapturePhotoSettings *photoSettings = [AVCapturePhotoSettings photoSettings]; + photoSettings.flashMode = self.captureSession.currentDeviceFlashMode; [self.captureSession.imageOutput capturePhotoWithSettings:photoSettings delegate:self]; }]; }; diff --git a/submodules/LegacyComponents/Sources/PGCameraCaptureSession.m b/submodules/LegacyComponents/Sources/PGCameraCaptureSession.m index f34f96244a..28b7f4a23c 100644 --- a/submodules/LegacyComponents/Sources/PGCameraCaptureSession.m +++ b/submodules/LegacyComponents/Sources/PGCameraCaptureSession.m @@ -703,6 +703,10 @@ const NSInteger PGCameraFrameRate = 30; #pragma mark - Flash +- (AVCaptureFlashMode)currentDeviceFlashMode { + return [PGCameraCaptureSession _deviceFlashModeForCameraFlashMode:self.currentFlashMode]; +} + - (PGCameraFlashMode)currentFlashMode { switch (self.currentMode) diff --git a/submodules/QrCodeUI/Sources/QrCodeScanScreen.swift b/submodules/QrCodeUI/Sources/QrCodeScanScreen.swift index 9cfd90de3f..b5dfcbedb5 100644 --- a/submodules/QrCodeUI/Sources/QrCodeScanScreen.swift +++ b/submodules/QrCodeUI/Sources/QrCodeScanScreen.swift @@ -390,7 +390,7 @@ private final class QrCodeScanScreenNode: ViewControllerTracingNode, UIScrollVie private weak var controller: QrCodeScanScreen? private let subject: QrCodeScanScreen.Subject - private let previewNode: CameraPreviewNode + private let previewView: CameraSimplePreviewView private let fadeNode: ASDisplayNode private let topDimNode: ASDisplayNode private let bottomDimNode: ASDisplayNode @@ -436,8 +436,8 @@ private final class QrCodeScanScreenNode: ViewControllerTracingNode, UIScrollVie self.controller = controller self.subject = subject - self.previewNode = CameraPreviewNode() - self.previewNode.backgroundColor = .black + self.previewView = CameraSimplePreviewView(frame: .zero, main: true) + self.previewView.backgroundColor = .black self.fadeNode = ASDisplayNode() self.fadeNode.alpha = 0.0 @@ -513,7 +513,7 @@ private final class QrCodeScanScreenNode: ViewControllerTracingNode, UIScrollVie self.errorTextNode.textAlignment = .center self.errorTextNode.isHidden = true - self.camera = Camera(configuration: .init(preset: .hd1920x1080, position: .back, audio: false, photo: true, metadata: true, preferredFps: 60)) + self.camera = Camera(configuration: .init(preset: .hd1920x1080, position: .back, audio: false, photo: true, metadata: true, preferredFps: 60), previewView: self.previewView) super.init() @@ -526,7 +526,6 @@ private final class QrCodeScanScreenNode: ViewControllerTracingNode, UIScrollVie } }) - self.addSubnode(self.previewNode) self.addSubnode(self.fadeNode) self.addSubnode(self.topDimNode) self.addSubnode(self.bottomDimNode) @@ -544,6 +543,20 @@ private final class QrCodeScanScreenNode: ViewControllerTracingNode, UIScrollVie self.galleryButtonNode.addTarget(self, action: #selector(self.galleryPressed), forControlEvents: .touchUpInside) self.torchButtonNode.addTarget(self, action: #selector(self.torchPressed), forControlEvents: .touchUpInside) + + self.previewView.resetPlaceholder(front: false) + if #available(iOS 13.0, *) { + let _ = (self.previewView.isPreviewing + |> filter { $0 } + |> take(1) + |> deliverOnMainQueue).startStandalone(next: { [weak self] _ in + self?.previewView.removePlaceholder(delay: 0.15) + }) + } else { + Queue.mainQueue().after(0.35) { + self.previewView.removePlaceholder(delay: 0.15) + } + } } deinit { @@ -564,7 +577,7 @@ private final class QrCodeScanScreenNode: ViewControllerTracingNode, UIScrollVie override func didLoad() { super.didLoad() - self.camera.attachPreviewNode(self.previewNode) + self.view.insertSubview(self.previewView, at: 0) self.camera.startCapture() let throttledSignal = self.camera.detectedCodes @@ -671,14 +684,14 @@ private final class QrCodeScanScreenNode: ViewControllerTracingNode, UIScrollVie if case .tablet = layout.deviceMetrics.type { if UIDevice.current.orientation == .landscapeLeft { - self.previewNode.transform = CATransform3DMakeRotation(-CGFloat.pi / 2.0, 0.0, 0.0, 1.0) + self.previewView.layer.transform = CATransform3DMakeRotation(-CGFloat.pi / 2.0, 0.0, 0.0, 1.0) } else if UIDevice.current.orientation == .landscapeRight { - self.previewNode.transform = CATransform3DMakeRotation(CGFloat.pi / 2.0, 0.0, 0.0, 1.0) + self.previewView.layer.transform = CATransform3DMakeRotation(CGFloat.pi / 2.0, 0.0, 0.0, 1.0) } else { - self.previewNode.transform = CATransform3DIdentity + self.previewView.layer.transform = CATransform3DIdentity } } - transition.updateFrame(node: self.previewNode, frame: bounds) + transition.updateFrame(view: self.previewView, frame: bounds) transition.updateFrame(node: self.fadeNode, frame: bounds) let frameSide = max(240.0, layout.size.width - sideInset * 2.0) diff --git a/submodules/TelegramUI/Components/CameraScreen/Sources/CameraScreen.swift b/submodules/TelegramUI/Components/CameraScreen/Sources/CameraScreen.swift index 6df09dea53..54b97bfd79 100644 --- a/submodules/TelegramUI/Components/CameraScreen/Sources/CameraScreen.swift +++ b/submodules/TelegramUI/Components/CameraScreen/Sources/CameraScreen.swift @@ -409,17 +409,23 @@ private final class CameraScreenComponent: CombinedComponent { controller.updateCameraState({ $0.updatedMode(mode) }, transition: .spring(duration: 0.3)) + var flashOn = controller.cameraState.flashMode == .on if case .video = mode, case .auto = controller.cameraState.flashMode { camera.setFlashMode(.on) + flashOn = true } + + self.updateScreenBrightness(flashOn: flashOn) } func toggleFlashMode() { guard let controller = self.getController(), let camera = controller.camera else { return } + var flashOn = false switch controller.cameraState.flashMode { case .off: + flashOn = true camera.setFlashMode(.on) case .on: if controller.cameraState.mode == .video { @@ -431,6 +437,8 @@ private final class CameraScreenComponent: CombinedComponent { camera.setFlashMode(.off) } self.hapticFeedback.impact(.light) + + self.updateScreenBrightness(flashOn: flashOn) } func updateFlashTint(_ tint: CameraState.FlashTint?) { @@ -441,6 +449,7 @@ private final class CameraScreenComponent: CombinedComponent { controller.updateCameraState({ $0.updatedFlashTint(tint) }, transition: .easeInOut(duration: 0.2)) } else { camera.setFlashMode(.off) + self.updateScreenBrightness(flashOn: false) } } @@ -460,6 +469,8 @@ private final class CameraScreenComponent: CombinedComponent { self.displayingFlashTint = true self.updated(transition: .immediate) + + self.updateScreenBrightness(flashOn: true) } private var lastFlipTimestamp: Double? @@ -515,7 +526,7 @@ private final class CameraScreenComponent: CombinedComponent { self.updated(transition: .easeInOut(duration: 0.2)) } - private var isTakingPhoto = false + var isTakingPhoto = false func takePhoto() { guard let controller = self.getController(), let camera = controller.camera else { return @@ -527,20 +538,47 @@ private final class CameraScreenComponent: CombinedComponent { controller.node.dismissAllTooltips() - let takePhoto = camera.takePhoto() - |> mapToSignal { value -> Signal in - switch value { - case .began: - return .single(.pendingImage) - case let .finished(image, additionalImage, _): - return .single(.image(CameraScreen.Result.Image(image: image, additionalImage: additionalImage, additionalImagePosition: .topRight))) - case .failed: - return .complete() + let takePhoto = { + let takePhoto = camera.takePhoto() + |> mapToSignal { value -> Signal in + switch value { + case .began: + return .single(.pendingImage) + case let .finished(image, additionalImage, _): + return .single(.image(CameraScreen.Result.Image(image: image, additionalImage: additionalImage, additionalImagePosition: .topRight))) + case .failed: + return .complete() + } } + self.completion.invoke(takePhoto) } - self.completion.invoke(takePhoto) - Queue.mainQueue().after(1.0) { - self.isTakingPhoto = false + + let isFrontCamera = controller.cameraState.position == .front + let isFlashOn = controller.cameraState.flashMode == .on + + if isFrontCamera && isFlashOn { + let previousBrightness = UIScreen.main.brightness + UIScreen.main.brightness = 1.0 + + let flashController = CameraFrontFlashOverlayController(color: controller.cameraState.flashTint.color) + controller.presentInGlobalOverlay(flashController) + + Queue.mainQueue().after(0.1, { + takePhoto() + + Queue.mainQueue().after(0.5, { + self.isTakingPhoto = false + + self.brightnessArguments = (CACurrentMediaTime(), 0.25, UIScreen.main.brightness, previousBrightness) + self.animateBrightnessChange() + flashController.dismissAnimated() + }) + }) + } else { + takePhoto() + Queue.mainQueue().after(1.0) { + self.isTakingPhoto = false + } } } @@ -548,10 +586,33 @@ private final class CameraScreenComponent: CombinedComponent { private var brightnessArguments: (Double, Double, CGFloat, CGFloat)? private var brightnessAnimator: ConstantDisplayLinkAnimator? - private func updateBrightness() { + func updateScreenBrightness(flashOn: Bool?) { + guard let controller = self.getController() else { + return + } + let isFrontCamera = controller.cameraState.position == .front + let isVideo = controller.cameraState.mode == .video + let isFlashOn = flashOn ?? (controller.cameraState.flashMode == .on) + + if isFrontCamera && isVideo && isFlashOn { + if self.initialBrightness == nil { + self.initialBrightness = UIScreen.main.brightness + self.brightnessArguments = (CACurrentMediaTime(), 0.2, UIScreen.main.brightness, 1.0) + self.animateBrightnessChange() + } + } else { + if let initialBrightness = self.initialBrightness { + self.initialBrightness = nil + self.brightnessArguments = (CACurrentMediaTime(), 0.2, UIScreen.main.brightness, initialBrightness) + self.animateBrightnessChange() + } + } + } + + private func animateBrightnessChange() { if self.brightnessAnimator == nil { self.brightnessAnimator = ConstantDisplayLinkAnimator(update: { [weak self] in - self?.updateBrightness() + self?.animateBrightnessChange() }) self.brightnessAnimator?.isPaused = true } @@ -601,16 +662,7 @@ private final class CameraScreenComponent: CombinedComponent { controller.updateCameraState({ $0.updatedRecording(pressing ? .holding : .handsFree).updatedDuration(0.0) }, transition: .spring(duration: 0.4)) - if case .front = controller.cameraState.position { - self.initialBrightness = UIScreen.main.brightness - UIScreen.main.brightness = 1.0 - - Queue.mainQueue().after(0.2, { - startRecording() - }) - } else { - startRecording() - } + startRecording() } func stopVideoRecording() { @@ -635,7 +687,7 @@ private final class CameraScreenComponent: CombinedComponent { if case .front = controller.cameraState.position, let initialBrightness = self.initialBrightness { self.initialBrightness = nil self.brightnessArguments = (CACurrentMediaTime(), 0.2, UIScreen.main.brightness, initialBrightness) - self.updateBrightness() + self.animateBrightnessChange() } } @@ -776,7 +828,9 @@ private final class CameraScreenComponent: CombinedComponent { .disappear(.default(alpha: true)) ) - controlsTintColor = .black + if !state.isTakingPhoto { + controlsTintColor = .black + } } let shutterState: ShutterButtonState @@ -1190,6 +1244,7 @@ private final class CameraScreenComponent: CombinedComponent { .disappear(.default(alpha: true)) ) } + return availableSize } } @@ -1564,7 +1619,7 @@ public class CameraScreen: ViewController { self.additionalPreviewView.isPreviewing ) |> filter { $0 && $1 } - |> take(1)).start(next: { [weak self] _, _ in + |> take(1)).startStandalone(next: { [weak self] _, _ in self?.mainPreviewView.removePlaceholder(delay: 0.35) self?.additionalPreviewView.removePlaceholder(delay: 0.35) }) @@ -1572,7 +1627,7 @@ public class CameraScreen: ViewController { let _ = (self.mainPreviewView.isPreviewing |> filter { $0 } |> take(1) - |> deliverOnMainQueue).start(next: { [weak self] _ in + |> deliverOnMainQueue).startStandalone(next: { [weak self] _ in self?.mainPreviewView.removePlaceholder(delay: 0.35) }) } diff --git a/submodules/TelegramUI/Components/CameraScreen/Sources/FlashTintControlComponent.swift b/submodules/TelegramUI/Components/CameraScreen/Sources/FlashTintControlComponent.swift index 769817e9bc..55287c4c53 100644 --- a/submodules/TelegramUI/Components/CameraScreen/Sources/FlashTintControlComponent.swift +++ b/submodules/TelegramUI/Components/CameraScreen/Sources/FlashTintControlComponent.swift @@ -1,6 +1,7 @@ import Foundation import UIKit import Display +import AsyncDisplayKit import ComponentFlow import RoundedRectWithTailPath @@ -431,3 +432,35 @@ private final class SliderView: UIView { func performAction() { } } + +final class CameraFrontFlashOverlayController: ViewController { + class Node: ASDisplayNode { + init(color: UIColor) { + super.init() + + self.backgroundColor = color + } + } + + private let color: UIColor + init(color: UIColor) { + self.color = color + + super.init(navigationBarPresentationData: nil) + } + + required init(coder aDecoder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + override func loadDisplayNode() { + self.displayNode = Node(color: self.color) + self.displayNodeDidLoad() + } + + func dismissAnimated() { + self.displayNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.25, removeOnCompletion: false, completion: { _ in + self.dismiss() + }) + } +} diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageAttachedContentNode/Sources/ChatMessageAttachedContentNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageAttachedContentNode/Sources/ChatMessageAttachedContentNode.swift index 197276317e..e685f45bba 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageAttachedContentNode/Sources/ChatMessageAttachedContentNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageAttachedContentNode/Sources/ChatMessageAttachedContentNode.swift @@ -158,15 +158,17 @@ public final class ChatMessageAttachedContentNode: ASDisplayNode { } let messageTheme = incoming ? presentationData.theme.theme.chat.message.incoming : presentationData.theme.theme.chat.message.outgoing - + let author = message.author let mainColor: UIColor var secondaryColor: UIColor? if !incoming { mainColor = messageTheme.accentTextColor + if let _ = author?.nameColor?.dashColors.1 { + secondaryColor = messageTheme.accentTextColor + } } else { var authorNameColor: UIColor? - let author = message.author - if [Namespaces.Peer.CloudGroup, Namespaces.Peer.CloudChannel].contains(message.id.peerId.namespace), author?.id.namespace == Namespaces.Peer.CloudUser { +// if [Namespaces.Peer.CloudGroup, Namespaces.Peer.CloudChannel].contains(message.id.peerId.namespace), author?.id.namespace == Namespaces.Peer.CloudUser { authorNameColor = author?.nameColor?.color secondaryColor = author?.nameColor?.dashColors.1 @@ -186,7 +188,7 @@ public final class ChatMessageAttachedContentNode: ASDisplayNode { // authorNameColor = UIColor(hue: hue, saturation: saturation * 0.7, brightness: min(1.0, brightness * 1.2), alpha: 1.0) // } // } - } +// } if let authorNameColor { mainColor = authorNameColor