diff --git a/submodules/Camera/Sources/Camera.swift b/submodules/Camera/Sources/Camera.swift index b5771d1346..79f36bd3de 100644 --- a/submodules/Camera/Sources/Camera.swift +++ b/submodules/Camera/Sources/Camera.swift @@ -66,14 +66,6 @@ final class CameraDeviceContext { self.output.configureVideoStabilization() } - func switchOutputWith(_ otherContext: CameraDeviceContext) { -// guard let session = self.session else { -// return -// } -// self.output.reconfigure(for: session, device: self.device, input: self.input, otherPreviewView: otherContext.previewView, otherOutput: otherContext.output) -// otherContext.output.reconfigure(for: session, device: otherContext.device, input: otherContext.input, otherPreviewView: self.previewView, otherOutput: self.output) - } - func invalidate() { guard let session = self.session else { return @@ -142,7 +134,7 @@ private final class CameraContext { private var lastSnapshotTimestamp: Double = CACurrentMediaTime() private var lastAdditionalSnapshotTimestamp: Double = CACurrentMediaTime() - private func savePreviewSnapshot(pixelBuffer: CVPixelBuffer, mirror: Bool, additional: Bool) { + private func savePreviewSnapshot(pixelBuffer: CVPixelBuffer, mirror: Bool) { Queue.concurrentDefaultQueue().async { var ciImage = CIImage(cvImageBuffer: pixelBuffer) let size = ciImage.extent.size @@ -154,10 +146,10 @@ private final class CameraContext { ciImage = ciImage.clampedToExtent().applyingGaussianBlur(sigma: 40.0).cropped(to: CGRect(origin: .zero, size: size)) if let cgImage = self.cameraImageContext.createCGImage(ciImage, from: ciImage.extent) { let uiImage = UIImage(cgImage: cgImage, scale: 1.0, orientation: .right) - if additional { - CameraSimplePreviewView.saveAdditionalLastStateImage(uiImage) + if mirror { + CameraSimplePreviewView.saveLastFrontImage(uiImage) } else { - CameraSimplePreviewView.saveLastStateImage(uiImage) + CameraSimplePreviewView.saveLastBackImage(uiImage) } } } @@ -171,6 +163,8 @@ private final class CameraContext { self.simplePreviewView = previewView self.secondaryPreviewView = secondaryPreviewView + self.dualPosition = configuration.position + self.mainDeviceContext = CameraDeviceContext(session: session, exclusive: true, additional: false) self.configure { self.mainDeviceContext.configure(position: configuration.position, previewView: self.simplePreviewView, audio: configuration.audio, photo: configuration.photo, metadata: configuration.metadata) @@ -188,7 +182,7 @@ private final class CameraContext { if #available(iOS 13.0, *) { mirror = connection.inputPorts.first?.sourceDevicePosition == .front } - self.savePreviewSnapshot(pixelBuffer: pixelBuffer, mirror: mirror, additional: false) + self.savePreviewSnapshot(pixelBuffer: pixelBuffer, mirror: mirror) self.lastSnapshotTimestamp = timestamp } } @@ -256,17 +250,19 @@ private final class CameraContext { return self._positionPromise.get() } - private var tmpPosition: Camera.Position = .back + private var dualPosition: Camera.Position = .back func togglePosition() { if self.isDualCamEnabled { -// let targetPosition: Camera.Position -// if case .back = self.tmpPosition { -// targetPosition = .front -// } else { -// targetPosition = .back -// } -// self.tmpPosition = targetPosition -// self._positionPromise.set(targetPosition) + let targetPosition: Camera.Position + if case .back = self.dualPosition { + targetPosition = .front + } else { + targetPosition = .back + } + self.dualPosition = targetPosition + self._positionPromise.set(targetPosition) + + self.mainDeviceContext.output.markPositionChange(position: targetPosition) } else { self.configure { self.mainDeviceContext.invalidate() @@ -277,6 +273,7 @@ private final class CameraContext { } else { targetPosition = .back } + self.dualPosition = targetPosition self._positionPromise.set(targetPosition) self.modeChange = .position @@ -294,6 +291,7 @@ private final class CameraContext { self.mainDeviceContext.invalidate() self._positionPromise.set(position) + self.dualPosition = position self.modeChange = .position self.mainDeviceContext.configure(position: position, previewView: self.simplePreviewView, audio: self.initialConfiguration.audio, photo: self.initialConfiguration.photo, metadata: self.initialConfiguration.metadata) @@ -333,7 +331,7 @@ private final class CameraContext { if #available(iOS 13.0, *) { mirror = connection.inputPorts.first?.sourceDevicePosition == .front } - self.savePreviewSnapshot(pixelBuffer: pixelBuffer, mirror: mirror, additional: false) + self.savePreviewSnapshot(pixelBuffer: pixelBuffer, mirror: mirror) self.lastSnapshotTimestamp = timestamp } } @@ -347,7 +345,7 @@ private final class CameraContext { if #available(iOS 13.0, *) { mirror = connection.inputPorts.first?.sourceDevicePosition == .front } - self.savePreviewSnapshot(pixelBuffer: pixelBuffer, mirror: mirror, additional: true) + self.savePreviewSnapshot(pixelBuffer: pixelBuffer, mirror: mirror) self.lastAdditionalSnapshotTimestamp = timestamp } } @@ -358,7 +356,7 @@ private final class CameraContext { self.additionalDeviceContext = nil self.mainDeviceContext = CameraDeviceContext(session: self.session, exclusive: true, additional: false) - self.mainDeviceContext.configure(position: .back, previewView: self.simplePreviewView, audio: self.initialConfiguration.audio, photo: self.initialConfiguration.photo, metadata: self.initialConfiguration.metadata) + self.mainDeviceContext.configure(position: self.dualPosition, previewView: self.simplePreviewView, audio: self.initialConfiguration.audio, photo: self.initialConfiguration.photo, metadata: self.initialConfiguration.metadata) } self.mainDeviceContext.output.processSampleBuffer = { [weak self] sampleBuffer, pixelBuffer, connection in guard let self else { @@ -372,7 +370,7 @@ private final class CameraContext { if #available(iOS 13.0, *) { mirror = connection.inputPorts.first?.sourceDevicePosition == .front } - self.savePreviewSnapshot(pixelBuffer: pixelBuffer, mirror: mirror, additional: false) + self.savePreviewSnapshot(pixelBuffer: pixelBuffer, mirror: mirror) self.lastSnapshotTimestamp = timestamp } } @@ -448,12 +446,17 @@ private final class CameraContext { func takePhoto() -> Signal { let orientation = self.videoOrientation ?? .portrait if let additionalDeviceContext = self.additionalDeviceContext { + let dualPosition = self.dualPosition return combineLatest( self.mainDeviceContext.output.takePhoto(orientation: orientation, flashMode: self._flashMode), additionalDeviceContext.output.takePhoto(orientation: orientation, flashMode: self._flashMode) ) |> map { main, additional in if case let .finished(mainImage, _, _) = main, case let .finished(additionalImage, _, _) = additional { - return .finished(mainImage, additionalImage, CACurrentMediaTime()) + if dualPosition == .front { + return .finished(additionalImage, mainImage, CACurrentMediaTime()) + } else { + return .finished(mainImage, additionalImage, CACurrentMediaTime()) + } } else { return .began } @@ -482,8 +485,8 @@ private final class CameraContext { self.mainDeviceContext.output.stopRecording(), additionalDeviceContext.output.stopRecording() ) |> mapToSignal { main, additional in - if case let .finished(mainResult, _, _) = main, case let .finished(additionalResult, _, _) = additional { - return .single(.finished(mainResult, additionalResult, CACurrentMediaTime())) + if case let .finished(mainResult, _, duration, positionChangeTimestamps, _) = main, case let .finished(additionalResult, _, _, _, _) = additional { + return .single(.finished(mainResult, additionalResult, duration, positionChangeTimestamps, CACurrentMediaTime())) } else { return .complete() } diff --git a/submodules/Camera/Sources/CameraOutput.swift b/submodules/Camera/Sources/CameraOutput.swift index ee623b2c4f..f77f6d97a0 100644 --- a/submodules/Camera/Sources/CameraOutput.swift +++ b/submodules/Camera/Sources/CameraOutput.swift @@ -7,7 +7,7 @@ import Vision import VideoToolbox public enum VideoCaptureResult: Equatable { - case finished((String, UIImage), (String, UIImage)?, Double) + case finished((String, UIImage), (String, UIImage)?, Double, [(Bool, Double)], Double) case failed public static func == (lhs: VideoCaptureResult, rhs: VideoCaptureResult) -> Bool { @@ -18,8 +18,11 @@ public enum VideoCaptureResult: Equatable { } else { return false } - case let .finished(_, _, lhsTime): - if case let .finished(_, _, rhsTime) = rhs, lhsTime == rhsTime { + case let .finished(_, _, lhsDuration, lhsChangeTimestamps, lhsTime): + if case let .finished(_, _, rhsDuration, rhsChangeTimestamps, rhsTime) = rhs, lhsDuration == rhsDuration, lhsTime == rhsTime { + if lhsChangeTimestamps.count != rhsChangeTimestamps.count { + return false + } return true } else { return false @@ -85,6 +88,7 @@ final class CameraOutput: NSObject { private var photoCaptureRequests: [Int64: PhotoCaptureContext] = [:] private var videoRecorder: VideoRecorder? + weak var overrideOutput: CameraOutput? var activeFilter: CameraFilter? var faceLandmarks: Bool = false @@ -333,8 +337,8 @@ final class CameraOutput: NSObject { let outputFilePath = NSTemporaryDirectory() + outputFileName + ".mp4" let outputFileURL = URL(fileURLWithPath: outputFilePath) let videoRecorder = VideoRecorder(configuration: VideoRecorder.Configuration(videoSettings: videoSettings, audioSettings: audioSettings), videoTransform: CGAffineTransform(rotationAngle: .pi / 2.0), fileUrl: outputFileURL, completion: { [weak self] result in - if case let .success(transitionImage) = result { - self?.recordingCompletionPipe.putNext(.finished((outputFilePath, transitionImage!), nil, CACurrentMediaTime())) + if case let .success(transitionImage, duration, positionChangeTimestamps) = result { + self?.recordingCompletionPipe.putNext(.finished((outputFilePath, transitionImage!), nil, duration, positionChangeTimestamps.map { ($0 == .front, $1) }, CACurrentMediaTime())) } else { self?.recordingCompletionPipe.putNext(.failed) } @@ -364,6 +368,12 @@ final class CameraOutput: NSObject { self.videoRecorder = nil } } + + func markPositionChange(position: Camera.Position) { + if let videoRecorder = self.videoRecorder { + videoRecorder.markPositionChange(position: position) + } + } } extension CameraOutput: AVCaptureVideoDataOutputSampleBufferDelegate, AVCaptureAudioDataOutputSampleBufferDelegate { diff --git a/submodules/Camera/Sources/CameraPreviewView.swift b/submodules/Camera/Sources/CameraPreviewView.swift index 66ce27f886..68ecdd4f8d 100644 --- a/submodules/Camera/Sources/CameraPreviewView.swift +++ b/submodules/Camera/Sources/CameraPreviewView.swift @@ -10,8 +10,8 @@ import Vision import ImageBlur public class CameraSimplePreviewView: UIView { - static func lastStateImage() -> UIImage { - let imagePath = NSTemporaryDirectory() + "cameraImage.jpg" + static func lastBackImage() -> UIImage { + let imagePath = NSTemporaryDirectory() + "backCameraImage.jpg" if let data = try? Data(contentsOf: URL(fileURLWithPath: imagePath)), let image = UIImage(data: data) { return image } else { @@ -19,15 +19,15 @@ public class CameraSimplePreviewView: UIView { } } - static func saveLastStateImage(_ image: UIImage) { - let imagePath = NSTemporaryDirectory() + "cameraImage.jpg" + static func saveLastBackImage(_ image: UIImage) { + let imagePath = NSTemporaryDirectory() + "backCameraImage.jpg" if let data = image.jpegData(compressionQuality: 0.6) { try? data.write(to: URL(fileURLWithPath: imagePath)) } } - static func lastAdditionalStateImage() -> UIImage { - let imagePath = NSTemporaryDirectory() + "cameraImage2.jpg" + static func lastFrontImage() -> UIImage { + let imagePath = NSTemporaryDirectory() + "frontCameraImage.jpg" if let data = try? Data(contentsOf: URL(fileURLWithPath: imagePath)), let image = UIImage(data: data) { return image } else { @@ -35,30 +35,25 @@ public class CameraSimplePreviewView: UIView { } } - static func saveAdditionalLastStateImage(_ image: UIImage) { - let imagePath = NSTemporaryDirectory() + "cameraImage2.jpg" + static func saveLastFrontImage(_ image: UIImage) { + let imagePath = NSTemporaryDirectory() + "frontCameraImage.jpg" if let data = image.jpegData(compressionQuality: 0.6) { try? data.write(to: URL(fileURLWithPath: imagePath)) } } - - private let additional: Bool - + private var previewingDisposable: Disposable? private let placeholderView = UIImageView() - public init(frame: CGRect, additional: Bool) { - self.additional = additional - + public init(frame: CGRect, main: Bool) { super.init(frame: frame) self.videoPreviewLayer.videoGravity = .resizeAspectFill self.placeholderView.contentMode = .scaleAspectFill - self.placeholderView.image = additional ? CameraSimplePreviewView.lastAdditionalStateImage() : CameraSimplePreviewView.lastStateImage() self.addSubview(self.placeholderView) - if !additional { + if main { if #available(iOS 13.0, *) { self.previewingDisposable = (self.isPreviewing |> filter { $0 } @@ -94,14 +89,11 @@ public class CameraSimplePreviewView: UIView { } } - public func resetPlaceholder() { - guard self.placeholderView.alpha == 0.0 else { - return - } - self.placeholderView.image = self.additional ? CameraSimplePreviewView.lastAdditionalStateImage() : CameraSimplePreviewView.lastStateImage() + public func resetPlaceholder(front: Bool) { + self.placeholderView.image = front ? CameraSimplePreviewView.lastFrontImage() : CameraSimplePreviewView.lastBackImage() self.placeholderView.alpha = 1.0 } - + private var _videoPreviewLayer: AVCaptureVideoPreviewLayer? var videoPreviewLayer: AVCaptureVideoPreviewLayer { if let layer = self._videoPreviewLayer { diff --git a/submodules/Camera/Sources/VideoRecorder.swift b/submodules/Camera/Sources/VideoRecorder.swift index 8c131661e7..0847574472 100644 --- a/submodules/Camera/Sources/VideoRecorder.swift +++ b/submodules/Camera/Sources/VideoRecorder.swift @@ -49,10 +49,12 @@ private final class VideoRecorderImpl { private var recordingStartSampleTime: CMTime = .invalid private var recordingStopSampleTime: CMTime = .invalid + private var positionChangeTimestamps: [(Camera.Position, CMTime)] = [] + private let configuration: VideoRecorder.Configuration private let videoTransform: CGAffineTransform private let url: URL - fileprivate var completion: (Bool, UIImage?) -> Void = { _, _ in } + fileprivate var completion: (Bool, UIImage?, [(Camera.Position, CMTime)]?) -> Void = { _, _, _ in } private let error = Atomic(value: nil) @@ -83,6 +85,17 @@ private final class VideoRecorderImpl { self.recordingStartSampleTime = CMTime(seconds: CACurrentMediaTime(), preferredTimescale: CMTimeScale(NSEC_PER_SEC)) } } + + public func markPositionChange(position: Camera.Position) { + self.queue.async { + guard self.recordingStartSampleTime.isValid else { + return + } + let currentTime = CMTime(seconds: CACurrentMediaTime(), preferredTimescale: CMTimeScale(NSEC_PER_SEC)) + let delta = currentTime - self.recordingStartSampleTime + self.positionChangeTimestamps.append((position, delta)) + } + } public func appendVideoSampleBuffer(_ sampleBuffer: CMSampleBuffer) { if let _ = self.hasError() { @@ -291,21 +304,21 @@ private final class VideoRecorderImpl { let completion = self.completion if self.recordingStopSampleTime == .invalid { DispatchQueue.main.async { - completion(false, nil) + completion(false, nil, nil) } return } if let _ = self.error.with({ $0 }) { DispatchQueue.main.async { - completion(false, nil) + completion(false, nil, nil) } return } if !self.tryAppendingPendingAudioBuffers() { DispatchQueue.main.async { - completion(false, nil) + completion(false, nil, nil) } return } @@ -314,21 +327,21 @@ private final class VideoRecorderImpl { self.assetWriter.finishWriting { if let _ = self.assetWriter.error { DispatchQueue.main.async { - completion(false, nil) + completion(false, nil, nil) } } else { DispatchQueue.main.async { - completion(true, self.transitionImage) + completion(true, self.transitionImage, self.positionChangeTimestamps) } } } } else if let _ = self.assetWriter.error { DispatchQueue.main.async { - completion(false, nil) + completion(false, nil, nil) } } else { DispatchQueue.main.async { - completion(false, nil) + completion(false, nil, nil) } } } @@ -407,7 +420,7 @@ public final class VideoRecorder { case generic } - case success(UIImage?) + case success(UIImage?, Double, [(Camera.Position, Double)]) case initError(Error) case writeError(Error) case finishError(Error) @@ -448,10 +461,17 @@ public final class VideoRecorder { return nil } self.impl = impl - impl.completion = { [weak self] result, transitionImage in + impl.completion = { [weak self] result, transitionImage, positionChangeTimestamps in if let self { + let duration = self.duration ?? 0.0 if result { - self.completion(.success(transitionImage)) + var timestamps: [(Camera.Position, Double)] = [] + if let positionChangeTimestamps { + for (position, time) in positionChangeTimestamps { + timestamps.append((position, time.seconds)) + } + } + self.completion(.success(transitionImage, duration, timestamps)) } else { self.completion(.finishError(.generic)) } @@ -467,6 +487,10 @@ public final class VideoRecorder { self.impl.stopRecording() } + func markPositionChange(position: Camera.Position) { + self.impl.markPositionChange(position: position) + } + func appendSampleBuffer(_ sampleBuffer: CMSampleBuffer) { guard let formatDescriptor = CMSampleBufferGetFormatDescription(sampleBuffer) else { return diff --git a/submodules/TelegramUI/Components/CameraScreen/Sources/CameraScreen.swift b/submodules/TelegramUI/Components/CameraScreen/Sources/CameraScreen.swift index aa47c4e78e..17e9bdacaa 100644 --- a/submodules/TelegramUI/Components/CameraScreen/Sources/CameraScreen.swift +++ b/submodules/TelegramUI/Components/CameraScreen/Sources/CameraScreen.swift @@ -305,8 +305,8 @@ private final class CameraScreenComponent: CombinedComponent { self.cameraState = self.cameraState.updatedRecording(.none).updatedDuration(0.0) self.resultDisposable.set((self.camera.stopRecording() |> deliverOnMainQueue).start(next: { [weak self] result in - if let self, case let .finished(mainResult, additionalResult, _) = result { - self.completion.invoke(.single(.video(mainResult.0, mainResult.1, additionalResult?.0, additionalResult?.1, PixelDimensions(width: 1080, height: 1920), .bottomRight))) + if let self, case let .finished(mainResult, additionalResult, duration, positionChangeTimestamps, _) = result { + self.completion.invoke(.single(.video(mainResult.0, mainResult.1, additionalResult?.0, additionalResult?.1, PixelDimensions(width: 1080, height: 1920), duration, positionChangeTimestamps, .bottomRight))) } })) self.isTransitioning = true @@ -741,8 +741,6 @@ private final class CameraScreenComponent: CombinedComponent { } } -private let useSimplePreviewView = true - private class BlurView: UIVisualEffectView { private func setup() { for subview in self.subviews { @@ -803,7 +801,7 @@ public class CameraScreen: ViewController { public enum Result { case pendingImage case image(UIImage, UIImage?, CameraScreen.PIPPosition) - case video(String, UIImage?, String?, UIImage?, PixelDimensions, CameraScreen.PIPPosition) + case video(String, UIImage?, String?, UIImage?, PixelDimensions, Double, [(Bool, Double)], CameraScreen.PIPPosition) case asset(PHAsset) case draft(MediaEditorDraft) @@ -811,8 +809,8 @@ public class CameraScreen: ViewController { switch self { case let .image(mainImage, additionalImage, _): return .image(mainImage, additionalImage, position) - case let .video(mainPath, mainImage, additionalPath, additionalImage, dimensions, _): - return .video(mainPath, mainImage, additionalPath, additionalImage, dimensions, position) + case let .video(mainPath, mainImage, additionalPath, additionalImage, dimensions, duration, positionChangeTimestamps, _): + return .video(mainPath, mainImage, additionalPath, additionalImage, dimensions, duration, positionChangeTimestamps, position) default: return self } @@ -860,11 +858,15 @@ public class CameraScreen: ViewController { fileprivate let containerView: UIView fileprivate let componentHost: ComponentView private let previewContainerView: UIView - fileprivate let previewView: CameraPreviewView? - fileprivate let simplePreviewView: CameraSimplePreviewView? - fileprivate var additionalPreviewView: CameraSimplePreviewView? + + private let mainPreviewContainerView: UIView + fileprivate var mainPreviewView: CameraSimplePreviewView + + private let additionalPreviewContainerView: UIView + fileprivate var additionalPreviewView: CameraSimplePreviewView + fileprivate let previewBlurView: BlurView - private var previewSnapshotView: UIView? + private var mainPreviewSnapshotView: UIView? private var additionalPreviewSnapshotView: UIView? fileprivate let previewFrameLeftDimView: UIView fileprivate let previewFrameRightDimView: UIView @@ -881,47 +883,7 @@ public class CameraScreen: ViewController { private var cameraPosition: Camera.Position = .back private let completion = ActionSlot>() - - private var effectivePreviewView: UIView { - if let simplePreviewView = self.simplePreviewView { - return simplePreviewView - } else if let previewView = self.previewView { - return previewView - } else { - fatalError() - } - } - - private var currentPreviewView: UIView { - if let simplePreviewView = self.simplePreviewView { - if let additionalPreviewView = self.additionalPreviewView { - if self.isDualCamEnabled && cameraPosition == .front { - return additionalPreviewView - } else { - return simplePreviewView - } - } else { - return simplePreviewView - } - } else if let previewView = self.previewView { - return previewView - } else { - fatalError() - } - } - - private var currentAdditionalPreviewView: UIView? { - if let additionalPreviewView = self.additionalPreviewView { - if self.isDualCamEnabled && cameraPosition == .front { - return self.simplePreviewView - } else { - return additionalPreviewView - } - } else { - return nil - } - } - + fileprivate var previewBlurPromise = ValuePromise(false) private let flipAnimationAction = ActionSlot() @@ -953,36 +915,22 @@ public class CameraScreen: ViewController { self.previewBlurView = BlurView() self.previewBlurView.isUserInteractionEnabled = false - if let holder = controller.holder { - self.simplePreviewView = nil - self.previewView = holder.previewView - self.camera = holder.camera - } else { - if useSimplePreviewView { - self.simplePreviewView = CameraSimplePreviewView(frame: .zero, additional: false) - self.previewView = nil - - self.additionalPreviewView = CameraSimplePreviewView(frame: .zero, additional: true) - self.additionalPreviewView?.clipsToBounds = true - } else { - self.previewView = CameraPreviewView(test: false)! - self.simplePreviewView = nil - } - - var cameraFrontPosition = false - if let useCameraFrontPosition = UserDefaults.standard.object(forKey: "TelegramStoryCameraUseFrontPosition") as? NSNumber, useCameraFrontPosition.boolValue { - cameraFrontPosition = true - } - - self.cameraPosition = cameraFrontPosition ? .front : .back - self.camera = Camera(configuration: Camera.Configuration(preset: .hd1920x1080, position: self.cameraPosition, audio: true, photo: true, metadata: false, preferredFps: 60.0), previewView: self.simplePreviewView, secondaryPreviewView: self.additionalPreviewView) - if !useSimplePreviewView { -#if targetEnvironment(simulator) -#else - self.camera.attachPreviewView(self.previewView!) -#endif - } + self.mainPreviewContainerView = UIView() + self.mainPreviewContainerView.clipsToBounds = true + self.mainPreviewView = CameraSimplePreviewView(frame: .zero, main: true) + + self.additionalPreviewContainerView = UIView() + self.additionalPreviewContainerView.clipsToBounds = true + self.additionalPreviewView = CameraSimplePreviewView(frame: .zero, main: false) + + var cameraFrontPosition = false + if let useCameraFrontPosition = UserDefaults.standard.object(forKey: "TelegramStoryCameraUseFrontPosition") as? NSNumber, useCameraFrontPosition.boolValue { + cameraFrontPosition = true } + self.mainPreviewView.resetPlaceholder(front: cameraFrontPosition) + + self.cameraPosition = cameraFrontPosition ? .front : .back + self.camera = Camera(configuration: Camera.Configuration(preset: .hd1920x1080, position: self.cameraPosition, audio: true, photo: true, metadata: false, preferredFps: 60.0), previewView: self.mainPreviewView, secondaryPreviewView: self.additionalPreviewView) self.previewFrameLeftDimView = UIView() self.previewFrameLeftDimView.backgroundColor = UIColor(rgb: 0x000000, alpha: 0.6) @@ -1006,17 +954,17 @@ public class CameraScreen: ViewController { self.view.addSubview(self.containerView) self.containerView.addSubview(self.previewContainerView) - self.previewContainerView.addSubview(self.effectivePreviewView) + self.previewContainerView.addSubview(self.mainPreviewContainerView) + self.previewContainerView.addSubview(self.additionalPreviewContainerView) self.previewContainerView.addSubview(self.previewBlurView) self.previewContainerView.addSubview(self.previewFrameLeftDimView) self.previewContainerView.addSubview(self.previewFrameRightDimView) self.containerView.addSubview(self.transitionDimView) self.view.addSubview(self.transitionCornersView) - if let additionalPreviewView = self.additionalPreviewView { - self.previewContainerView.insertSubview(additionalPreviewView, at: 1) - } - + self.mainPreviewContainerView.addSubview(self.mainPreviewView) + self.additionalPreviewContainerView.addSubview(self.additionalPreviewView) + self.changingPositionDisposable = combineLatest( queue: Queue.mainQueue(), self.camera.modeChange, @@ -1024,18 +972,36 @@ public class CameraScreen: ViewController { ).start(next: { [weak self] modeChange, forceBlur in if let self { if modeChange != .none { - if let snapshot = self.simplePreviewView?.snapshotView(afterScreenUpdates: false) { - self.simplePreviewView?.addSubview(snapshot) - self.previewSnapshotView = snapshot +// if case .dualCamera = modeChange, self.cameraPosition == .front { +// if self.isDualCamEnabled { +// if let snapshot = self.mainPreviewView.snapshotView(afterScreenUpdates: false) { +// snapshot.frame = CGRect(origin: .zero, size: self.mainPreviewContainerView.bounds.size) +// self.mainPreviewView.addSubview(snapshot) +// self.previewSnapshotView = snapshot +// } +// } else { +// if let snapshot = self.mainPreviewView.snapshotView(afterScreenUpdates: false) { +// snapshot.frame = CGRect(origin: .zero, size: self.mainPreviewContainerView.bounds.size) +// self.additionalPreviewView.addSubview(snapshot) +// self.additionalPreviewSnapshotView = snapshot +// } +// } +// } else { + if case .dualCamera = modeChange, self.cameraPosition == .front { + + } else { + if let snapshot = self.mainPreviewView.snapshotView(afterScreenUpdates: false) { + self.mainPreviewView.addSubview(snapshot) + self.mainPreviewSnapshotView = snapshot + } } if case .position = modeChange { UIView.transition(with: self.previewContainerView, duration: 0.4, options: [.transitionFlipFromLeft, .curveEaseOut], animations: { self.previewBlurView.effect = UIBlurEffect(style: .dark) }) } else { - if let additionalPreviewView = self.additionalPreviewView { - self.previewContainerView.insertSubview(self.previewBlurView, belowSubview: additionalPreviewView) - } + self.previewContainerView.insertSubview(self.previewBlurView, belowSubview: self.additionalPreviewContainerView) + UIView.animate(withDuration: 0.4) { self.previewBlurView.effect = UIBlurEffect(style: .dark) } @@ -1045,16 +1011,16 @@ public class CameraScreen: ViewController { self.previewBlurView.effect = UIBlurEffect(style: .dark) } } else { - UIView.animate(withDuration: 0.4, animations: { - self.previewBlurView.effect = nil - }, completion: { _ in - if let additionalPreviewView = self.additionalPreviewView { - self.previewContainerView.insertSubview(self.previewBlurView, aboveSubview: additionalPreviewView) - } - }) + if self.previewBlurView.effect != nil { + UIView.animate(withDuration: 0.4, animations: { + self.previewBlurView.effect = nil + }, completion: { _ in + self.previewContainerView.insertSubview(self.previewBlurView, aboveSubview: self.additionalPreviewContainerView) + }) + } - if let previewSnapshotView = self.previewSnapshotView { - self.previewSnapshotView = nil + if let previewSnapshotView = self.mainPreviewSnapshotView { + self.mainPreviewSnapshotView = nil UIView.animate(withDuration: 0.25, animations: { previewSnapshotView.alpha = 0.0 }, completion: { _ in @@ -1072,7 +1038,8 @@ public class CameraScreen: ViewController { } if self.isDualCamEnabled { - self.additionalPreviewView?.removePlaceholder() + self.mainPreviewView.removePlaceholder() + self.additionalPreviewView.removePlaceholder() } } } @@ -1093,9 +1060,9 @@ public class CameraScreen: ViewController { } if case .pendingImage = value { Queue.mainQueue().async { - self.simplePreviewView?.isEnabled = false + self.mainPreviewView.isEnabled = false - self.additionalPreviewView?.isEnabled = false + self.additionalPreviewView.isEnabled = false } } else { Queue.mainQueue().async { @@ -1104,8 +1071,8 @@ public class CameraScreen: ViewController { self.previewBlurPromise.set(true) } } - self.simplePreviewView?.isEnabled = false - self.additionalPreviewView?.isEnabled = false + self.mainPreviewView.isEnabled = false + self.additionalPreviewView.isEnabled = false self.camera.stopCapture() } } @@ -1119,25 +1086,32 @@ public class CameraScreen: ViewController { self.updateState.connect { [weak self] state in if let self { let previousPosition = self.cameraPosition - self.cameraPosition = state.position + let newPosition = state.position != .unspecified ? state.position : previousPosition + self.cameraPosition = newPosition let dualCamWasEnabled = self.isDualCamEnabled self.isDualCamEnabled = state.isDualCamEnabled - if self.isDualCamEnabled && previousPosition != state.position, let additionalPreviewView = self.additionalPreviewView { - if state.position == .front { - additionalPreviewView.superview?.sendSubviewToBack(additionalPreviewView) + if self.isDualCamEnabled != dualCamWasEnabled && newPosition == .front { + if self.isDualCamEnabled { + if let cloneView = self.mainPreviewView.snapshotView(afterScreenUpdates: false) { + self.mainPreviewSnapshotView = cloneView + self.mainPreviewContainerView.addSubview(cloneView) + } } else { - additionalPreviewView.superview?.insertSubview(additionalPreviewView, aboveSubview: self.simplePreviewView!) + if let cloneView = self.additionalPreviewView.snapshotView(afterScreenUpdates: false) { + self.mainPreviewSnapshotView = cloneView + self.mainPreviewContainerView.addSubview(cloneView) + } } + } + + if self.isDualCamEnabled && previousPosition != newPosition { CATransaction.begin() CATransaction.setDisableActions(true) self.requestUpdateLayout(hasAppeared: false, transition: .immediate) CATransaction.commit() - } else { - if !dualCamWasEnabled && self.isDualCamEnabled { - - } + } else if dualCamWasEnabled != self.isDualCamEnabled { self.requestUpdateLayout(hasAppeared: false, transition: .spring(duration: 0.4)) } } @@ -1155,17 +1129,17 @@ public class CameraScreen: ViewController { self.view.disablesInteractiveKeyboardGestureRecognizer = true let pinchGestureRecognizer = UIPinchGestureRecognizer(target: self, action: #selector(self.handlePinch(_:))) - self.effectivePreviewView.addGestureRecognizer(pinchGestureRecognizer) + self.mainPreviewContainerView.addGestureRecognizer(pinchGestureRecognizer) let panGestureRecognizer = UIPanGestureRecognizer(target: self, action: #selector(self.handlePan(_:))) panGestureRecognizer.maximumNumberOfTouches = 1 - self.effectivePreviewView.addGestureRecognizer(panGestureRecognizer) + self.mainPreviewContainerView.addGestureRecognizer(panGestureRecognizer) let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(self.handleTap(_:))) - self.effectivePreviewView.addGestureRecognizer(tapGestureRecognizer) + self.mainPreviewContainerView.addGestureRecognizer(tapGestureRecognizer) let pipPanGestureRecognizer = UIPanGestureRecognizer(target: self, action: #selector(self.handlePipPan(_:))) - self.additionalPreviewView?.addGestureRecognizer(pipPanGestureRecognizer) + self.additionalPreviewContainerView.addGestureRecognizer(pipPanGestureRecognizer) self.camera.focus(at: CGPoint(x: 0.5, y: 0.5), autoFocus: true) self.camera.startCapture() @@ -1217,11 +1191,8 @@ public class CameraScreen: ViewController { } @objc private func handleTap(_ gestureRecognizer: UITapGestureRecognizer) { - guard let previewView = self.simplePreviewView else { - return - } - let location = gestureRecognizer.location(in: previewView) - let point = previewView.cameraPoint(for: location) + let location = gestureRecognizer.location(in: mainPreviewView) + let point = mainPreviewView.cameraPoint(for: location) self.camera.focus(at: point, autoFocus: false) } @@ -1347,8 +1318,8 @@ public class CameraScreen: ViewController { } func pauseCameraCapture() { - self.simplePreviewView?.isEnabled = false - self.additionalPreviewView?.isEnabled = false + self.mainPreviewView.isEnabled = false + self.additionalPreviewView.isEnabled = false Queue.mainQueue().after(0.3) { self.previewBlurPromise.set(true) } @@ -1356,21 +1327,21 @@ public class CameraScreen: ViewController { } func resumeCameraCapture() { - if self.simplePreviewView?.isEnabled == false { - if let snapshot = self.simplePreviewView?.snapshotView(afterScreenUpdates: false) { - self.simplePreviewView?.addSubview(snapshot) - self.previewSnapshotView = snapshot + if !self.mainPreviewView.isEnabled { + if let snapshot = self.mainPreviewView.snapshotView(afterScreenUpdates: false) { + self.mainPreviewView.addSubview(snapshot) + self.mainPreviewSnapshotView = snapshot } - if let snapshot = self.additionalPreviewView?.snapshotView(afterScreenUpdates: false) { - self.additionalPreviewView?.addSubview(snapshot) + if let snapshot = self.additionalPreviewView.snapshotView(afterScreenUpdates: false) { + self.additionalPreviewView.addSubview(snapshot) self.additionalPreviewSnapshotView = snapshot } - self.simplePreviewView?.isEnabled = true - self.additionalPreviewView?.isEnabled = true + self.mainPreviewView.isEnabled = true + self.additionalPreviewView.isEnabled = true self.camera.startCapture() - if #available(iOS 13.0, *), let isPreviewing = self.simplePreviewView?.isPreviewing { - let _ = (isPreviewing + if #available(iOS 13.0, *) { + let _ = (self.mainPreviewView.isPreviewing |> filter { $0 } @@ -1467,10 +1438,10 @@ public class CameraScreen: ViewController { override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? { let result = super.hitTest(point, with: event) if result == self.componentHost.view { - if let additionalPreviewView = self.additionalPreviewView, additionalPreviewView.bounds.contains(self.view.convert(point, to: additionalPreviewView)) { - return additionalPreviewView + if self.additionalPreviewView.bounds.contains(self.view.convert(point, to: self.additionalPreviewView)) { + return self.additionalPreviewView } else { - return self.effectivePreviewView + return self.mainPreviewView } } return result @@ -1569,7 +1540,7 @@ public class CameraScreen: ViewController { panelWidth: panelWidth, flipAnimationAction: self.flipAnimationAction, animateShutter: { [weak self] in - self?.effectivePreviewView.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.25) + self?.mainPreviewContainerView.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.25) }, present: { [weak self] c in self?.controller?.present(c, in: .window(.root)) @@ -1605,58 +1576,77 @@ public class CameraScreen: ViewController { transition.setFrame(view: self.transitionDimView, frame: CGRect(origin: .zero, size: layout.size)) transition.setFrame(view: self.previewContainerView, frame: previewFrame) - self.currentPreviewView.layer.cornerRadius = 0.0 - transition.setFrame(view: self.currentPreviewView, frame: CGRect(origin: .zero, size: previewFrame.size)) + transition.setFrame(view: self.mainPreviewContainerView, frame: CGRect(origin: .zero, size: previewFrame.size)) + transition.setFrame(view: self.previewBlurView, frame: CGRect(origin: .zero, size: previewFrame.size)) - if let additionalPreviewView = self.currentAdditionalPreviewView as? CameraSimplePreviewView { - let dualCamUpdated = self.appliedDualCam != self.isDualCamEnabled - self.appliedDualCam = self.isDualCamEnabled - - additionalPreviewView.layer.cornerRadius = 80.0 - - var origin: CGPoint - switch self.pipPosition { - case .topLeft: - origin = CGPoint(x: 10.0, y: 110.0) - if !self.isDualCamEnabled { - origin = origin.offsetBy(dx: -180.0, dy: 0.0) - } - case .topRight: - origin = CGPoint(x: previewFrame.width - 160.0 - 10.0, y: 110.0) - if !self.isDualCamEnabled { - origin = origin.offsetBy(dx: 180.0, dy: 0.0) - } - case .bottomLeft: - origin = CGPoint(x: 10.0, y: previewFrame.height - 160.0 - 110.0) - if !self.isDualCamEnabled { - origin = origin.offsetBy(dx: -180.0, dy: 0.0) - } - case .bottomRight: - origin = CGPoint(x: previewFrame.width - 160.0 - 10.0, y: previewFrame.height - 160.0 - 110.0) - if !self.isDualCamEnabled { - origin = origin.offsetBy(dx: 180.0, dy: 0.0) - } + let dualCamUpdated = self.appliedDualCam != self.isDualCamEnabled + self.appliedDualCam = self.isDualCamEnabled + + var origin: CGPoint + switch self.pipPosition { + case .topLeft: + origin = CGPoint(x: 10.0, y: 110.0) + if !self.isDualCamEnabled { + origin = origin.offsetBy(dx: -180.0, dy: 0.0) } - - if let pipTranslation = self.pipTranslation { - origin = origin.offsetBy(dx: pipTranslation.x, dy: pipTranslation.y) + case .topRight: + origin = CGPoint(x: previewFrame.width - 160.0 - 10.0, y: 110.0) + if !self.isDualCamEnabled { + origin = origin.offsetBy(dx: 180.0, dy: 0.0) } - - let additionalPreviewFrame = CGRect(origin: origin, size: CGSize(width: 160.0, height: 160.0)) - transition.setPosition(view: additionalPreviewView, position: additionalPreviewFrame.center) - transition.setBounds(view: additionalPreviewView, bounds: CGRect(origin: .zero, size: additionalPreviewFrame.size)) - - transition.setScale(view: additionalPreviewView, scale: self.isDualCamEnabled ? 1.0 : 0.1) - transition.setAlpha(view: additionalPreviewView, alpha: self.isDualCamEnabled ? 1.0 : 0.0) - - if dualCamUpdated && !self.isDualCamEnabled { - Queue.mainQueue().after(0.5) { - additionalPreviewView.resetPlaceholder() - } + case .bottomLeft: + origin = CGPoint(x: 10.0, y: previewFrame.height - 160.0 - 110.0) + if !self.isDualCamEnabled { + origin = origin.offsetBy(dx: -180.0, dy: 0.0) + } + case .bottomRight: + origin = CGPoint(x: previewFrame.width - 160.0 - 10.0, y: previewFrame.height - 160.0 - 110.0) + if !self.isDualCamEnabled { + origin = origin.offsetBy(dx: 180.0, dy: 0.0) } } - + + if let pipTranslation = self.pipTranslation { + origin = origin.offsetBy(dx: pipTranslation.x, dy: pipTranslation.y) + } + + let additionalPreviewFrame = CGRect(origin: origin, size: CGSize(width: 160.0, height: 160.0)) + transition.setPosition(view: self.additionalPreviewContainerView, position: additionalPreviewFrame.center) + transition.setBounds(view: self.additionalPreviewContainerView, bounds: CGRect(origin: .zero, size: additionalPreviewFrame.size)) + self.additionalPreviewContainerView.layer.cornerRadius = additionalPreviewFrame.width / 2.0 + + transition.setScale(view: self.additionalPreviewContainerView, scale: self.isDualCamEnabled ? 1.0 : 0.1) + transition.setAlpha(view: self.additionalPreviewContainerView, alpha: self.isDualCamEnabled ? 1.0 : 0.0) + + if dualCamUpdated && self.isDualCamEnabled { + if self.cameraPosition == .back { + self.additionalPreviewView.resetPlaceholder(front: true) + } else { + self.mainPreviewView.resetPlaceholder(front: false) + } + } + + let mainPreviewView: CameraSimplePreviewView + let additionalPreviewView: CameraSimplePreviewView + if self.cameraPosition == .front && self.isDualCamEnabled { + mainPreviewView = self.additionalPreviewView + additionalPreviewView = self.mainPreviewView + } else { + mainPreviewView = self.mainPreviewView + additionalPreviewView = self.additionalPreviewView + } + + if mainPreviewView.superview != self.mainPreviewContainerView { + self.mainPreviewContainerView.insertSubview(mainPreviewView, at: 0) + } + if additionalPreviewView.superview != self.additionalPreviewContainerView { + self.additionalPreviewContainerView.insertSubview(additionalPreviewView, at: 0) + } + + mainPreviewView.frame = CGRect(origin: .zero, size: previewFrame.size) + additionalPreviewView.frame = CGRect(origin: .zero, size: additionalPreviewFrame.size) + self.previewFrameLeftDimView.isHidden = !isTablet transition.setFrame(view: self.previewFrameLeftDimView, frame: CGRect(origin: .zero, size: CGSize(width: viewfinderFrame.minX, height: viewfinderFrame.height))) @@ -1780,6 +1770,7 @@ public class CameraScreen: ViewController { self.node.animateInFromEditor(toGallery: self.galleryController?.displayNode.supernode != nil) } + private var didStopCameraCapture = false func presentGallery(fromGesture: Bool = false) { if !fromGesture { self.hapticFeedback.impact(.light) @@ -1787,20 +1778,20 @@ public class CameraScreen: ViewController { self.dismissAllTooltips() - var didStopCameraCapture = false + self.didStopCameraCapture = false let stopCameraCapture = { [weak self] in - guard !didStopCameraCapture, let self else { + guard let self, !self.didStopCameraCapture else { return } - didStopCameraCapture = true + self.didStopCameraCapture = true self.node.pauseCameraCapture() } let resumeCameraCapture = { [weak self] in - guard didStopCameraCapture, let self else { + guard let self, self.didStopCameraCapture else { return } - didStopCameraCapture = false + self.didStopCameraCapture = false self.node.resumeCameraCapture() } diff --git a/submodules/TelegramUI/Components/MediaEditor/Sources/MediaEditor.swift b/submodules/TelegramUI/Components/MediaEditor/Sources/MediaEditor.swift index 51d8333868..ede1abe5df 100644 --- a/submodules/TelegramUI/Components/MediaEditor/Sources/MediaEditor.swift +++ b/submodules/TelegramUI/Components/MediaEditor/Sources/MediaEditor.swift @@ -25,13 +25,13 @@ public struct MediaEditorPlayerState { public final class MediaEditor { public enum Subject { case image(UIImage, PixelDimensions) - case video(String, UIImage?, PixelDimensions) + case video(String, UIImage?, PixelDimensions, Double) case asset(PHAsset) case draft(MediaEditorDraft) var dimensions: PixelDimensions { switch self { - case let .image(_, dimensions), let .video(_, _, dimensions): + case let .image(_, dimensions), let .video(_, _, dimensions, _): return dimensions case let .asset(asset): return PixelDimensions(width: Int32(asset.pixelWidth), height: Int32(asset.pixelHeight)) @@ -116,6 +116,12 @@ public final class MediaEditor { } private let playerPlaybackStatePromise = Promise<(Double, Double, Bool, Bool)>((0.0, 0.0, false, false)) + public var position: Signal { + return self.playerPlaybackStatePromise.get() + |> map { _, position, _, _ -> Double in + return position + } + } public var duration: Double? { if let _ = self.player { if let trimRange = self.values.videoTrimRange { @@ -275,6 +281,9 @@ public final class MediaEditor { if case let .asset(asset) = subject { self.playerPlaybackState = (asset.duration, 0.0, false, false) self.playerPlaybackStatePromise.set(.single(self.playerPlaybackState)) + } else if case let .video(_, _, _, duration) = subject { + self.playerPlaybackState = (duration, 0.0, false, true) + self.playerPlaybackStatePromise.set(.single(self.playerPlaybackState)) } } @@ -350,7 +359,7 @@ public final class MediaEditor { } textureSource = .single((ImageTextureSource(image: image, renderTarget: renderTarget), image, nil, colors.0, colors.1)) } - case let .video(path, transitionImage, _): + case let .video(path, transitionImage, _, _): textureSource = Signal { subscriber in let url = URL(fileURLWithPath: path) let asset = AVURLAsset(url: url) diff --git a/submodules/TelegramUI/Components/MediaEditorScreen/Sources/MediaEditorScreen.swift b/submodules/TelegramUI/Components/MediaEditorScreen/Sources/MediaEditorScreen.swift index 45afe71c51..0e55ef6602 100644 --- a/submodules/TelegramUI/Components/MediaEditorScreen/Sources/MediaEditorScreen.swift +++ b/submodules/TelegramUI/Components/MediaEditorScreen/Sources/MediaEditorScreen.swift @@ -378,6 +378,7 @@ final class MediaEditorScreenComponent: Component { self.endEditing(true) } + private var animatingButtons = false enum TransitionAnimationSource { case camera case gallery @@ -401,6 +402,7 @@ final class MediaEditorScreenComponent: Component { } if case .camera = source { + self.animatingButtons = true var delay: Double = 0.0 for button in buttons { if let view = button.view { @@ -412,10 +414,12 @@ final class MediaEditorScreenComponent: Component { view.layer.animateScale(from: 0.1, to: 1.0, duration: 0.2, delay: 0.0) }) delay += 0.03 - - Queue.mainQueue().after(0.45, completion) } } + Queue.mainQueue().after(0.45, { + self.animatingButtons = false + completion() + }) if let view = self.saveButton.view { view.layer.animateAlpha(from: 0.0, to: view.alpha, duration: 0.2) @@ -745,7 +749,9 @@ final class MediaEditorScreenComponent: Component { } transition.setPosition(view: drawButtonView, position: drawButtonFrame.center) transition.setBounds(view: drawButtonView, bounds: CGRect(origin: .zero, size: drawButtonFrame.size)) - transition.setAlpha(view: drawButtonView, alpha: component.isDisplayingTool || component.isDismissing || component.isInteractingWithEntities ? 0.0 : 1.0) + if !self.animatingButtons { + transition.setAlpha(view: drawButtonView, alpha: component.isDisplayingTool || component.isDismissing || component.isInteractingWithEntities ? 0.0 : 1.0) + } } let textButtonSize = self.textButton.update( @@ -772,7 +778,9 @@ final class MediaEditorScreenComponent: Component { } transition.setPosition(view: textButtonView, position: textButtonFrame.center) transition.setBounds(view: textButtonView, bounds: CGRect(origin: .zero, size: textButtonFrame.size)) - transition.setAlpha(view: textButtonView, alpha: component.isDisplayingTool || component.isDismissing || component.isInteractingWithEntities ? 0.0 : 1.0) + if !self.animatingButtons { + transition.setAlpha(view: textButtonView, alpha: component.isDisplayingTool || component.isDismissing || component.isInteractingWithEntities ? 0.0 : 1.0) + } } let stickerButtonSize = self.stickerButton.update( @@ -799,7 +807,9 @@ final class MediaEditorScreenComponent: Component { } transition.setPosition(view: stickerButtonView, position: stickerButtonFrame.center) transition.setBounds(view: stickerButtonView, bounds: CGRect(origin: .zero, size: stickerButtonFrame.size)) - transition.setAlpha(view: stickerButtonView, alpha: component.isDisplayingTool || component.isDismissing || component.isInteractingWithEntities ? 0.0 : 1.0) + if !self.animatingButtons { + transition.setAlpha(view: stickerButtonView, alpha: component.isDisplayingTool || component.isDismissing || component.isInteractingWithEntities ? 0.0 : 1.0) + } } let toolsButtonSize = self.toolsButton.update( @@ -826,7 +836,9 @@ final class MediaEditorScreenComponent: Component { } transition.setPosition(view: toolsButtonView, position: toolsButtonFrame.center) transition.setBounds(view: toolsButtonView, bounds: CGRect(origin: .zero, size: toolsButtonFrame.size)) - transition.setAlpha(view: toolsButtonView, alpha: component.isDisplayingTool || component.isDismissing || component.isInteractingWithEntities ? 0.0 : 1.0) + if !self.animatingButtons { + transition.setAlpha(view: toolsButtonView, alpha: component.isDisplayingTool || component.isDismissing || component.isInteractingWithEntities ? 0.0 : 1.0) + } } var mediaEditor: MediaEditor? @@ -876,7 +888,9 @@ final class MediaEditorScreenComponent: Component { self.addSubview(scrubberView) } transition.setFrame(view: scrubberView, frame: scrubberFrame) - transition.setAlpha(view: scrubberView, alpha: component.isDisplayingTool || component.isDismissing || component.isInteractingWithEntities ? 0.0 : 1.0) + if !self.animatingButtons { + transition.setAlpha(view: scrubberView, alpha: component.isDisplayingTool || component.isDismissing || component.isInteractingWithEntities ? 0.0 : 1.0) + } } scrubberBottomInset = scrubberSize.height + 10.0 @@ -1045,7 +1059,9 @@ final class MediaEditorScreenComponent: Component { } let isVisible = inputHeight > 44.0 transition.setFrame(view: inputPanelBackgroundView, frame: CGRect(origin: CGPoint(x: 0.0, y: isVisible ? availableSize.height - inputPanelBackgroundSize.height : availableSize.height), size: inputPanelBackgroundSize)) - transition.setAlpha(view: inputPanelBackgroundView, alpha: isVisible ? 1.0 : 0.0, delay: isVisible ? 0.0 : 0.4) + if !self.animatingButtons { + transition.setAlpha(view: inputPanelBackgroundView, alpha: isVisible ? 1.0 : 0.0, delay: isVisible ? 0.0 : 0.4) + } } var isEditingTextEntity = false @@ -1772,7 +1788,7 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate imageEntity.scale = 1.49 imageEntity.position = position.getPosition(storyDimensions) self.entitiesView.add(imageEntity, announce: false) - } else if case let .video(_, _, additionalVideoPath, additionalVideoImage, _, position) = subject, let additionalVideoPath { + } else if case let .video(_, _, additionalVideoPath, additionalVideoImage, _, _, _, position) = subject, let additionalVideoPath { let videoEntity = DrawingStickerEntity(content: .video(additionalVideoPath, additionalVideoImage)) videoEntity.referenceDrawingSize = storyDimensions videoEntity.scale = 1.49 @@ -2133,7 +2149,7 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate if let view = self.componentHost.view as? MediaEditorScreenComponent.View { view.animateIn(from: .camera, completion: completion) } - if let subject = self.subject, case let .video(_, transitionImage, _, _, _, _) = subject, let transitionImage { + if let subject = self.subject, case let .video(_, transitionImage, _, _, _, _, _, _) = subject, let transitionImage { self.setupTransitionImage(transitionImage) } case let .gallery(transitionIn): @@ -2840,13 +2856,13 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate public enum Subject { case image(UIImage, PixelDimensions, UIImage?, PIPPosition) - case video(String, UIImage?, String?, UIImage?, PixelDimensions, PIPPosition) + case video(String, UIImage?, String?, UIImage?, PixelDimensions, Double, [(Bool, Double)], PIPPosition) case asset(PHAsset) case draft(MediaEditorDraft, Int64?) var dimensions: PixelDimensions { switch self { - case let .image(_, dimensions, _, _), let .video(_, _, _, _, dimensions, _): + case let .image(_, dimensions, _, _), let .video(_, _, _, _, dimensions, _, _, _): return dimensions case let .asset(asset): return PixelDimensions(width: Int32(asset.pixelWidth), height: Int32(asset.pixelHeight)) @@ -2859,8 +2875,8 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate switch self { case let .image(image, dimensions, _, _): return .image(image, dimensions) - case let .video(videoPath, transitionImage, _, _, dimensions, _): - return .video(videoPath, transitionImage, dimensions) + case let .video(videoPath, transitionImage, _, _, dimensions, duration, _, _): + return .video(videoPath, transitionImage, dimensions, duration) case let .asset(asset): return .asset(asset) case let .draft(draft, _): @@ -2872,7 +2888,7 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate switch self { case let .image(image, dimensions, _, _): return .image(image, dimensions) - case let .video(videoPath, _, _, _, dimensions, _): + case let .video(videoPath, _, _, _, dimensions, _, _, _): return .video(videoPath, dimensions) case let .asset(asset): return .asset(asset) @@ -3311,7 +3327,7 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate switch subject { case let .image(image, dimensions, _, _): saveImageDraft(image, dimensions) - case let .video(path, _, _, _, dimensions, _): + case let .video(path, _, _, _, dimensions, _, _, _): saveVideoDraft(path, dimensions, duration) case let .asset(asset): if asset.mediaType == .video { @@ -3404,7 +3420,7 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate duration = 5.0 firstFrame = .single(image) - case let .video(path, _, _, _, _, _): + case let .video(path, _, _, _, _, _, _, _): videoResult = .videoFile(path: path) if let videoTrimRange = mediaEditor.values.videoTrimRange { duration = videoTrimRange.upperBound - videoTrimRange.lowerBound @@ -3592,7 +3608,7 @@ public final class MediaEditorScreen: ViewController, UIDropInteractionDelegate let exportSubject: Signal switch subject { - case let .video(path, _, _, _, _, _): + case let .video(path, _, _, _, _, _, _, _): let asset = AVURLAsset(url: NSURL(fileURLWithPath: path) as URL) exportSubject = .single(.video(asset)) case let .image(image, _, _, _): diff --git a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetContainerComponent.swift b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetContainerComponent.swift index f45b9bbb3a..9e7e93e5b5 100644 --- a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetContainerComponent.swift +++ b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetContainerComponent.swift @@ -2450,7 +2450,13 @@ public final class StoryItemSetContainerComponent: Component { // if let source { // subject = .single(.draft(source, Int64(id))) // } else { - subject = fetchMediaData(context: context, postbox: context.account.postbox, userLocation: .other, mediaReference: .story(peer: peerReference, id: item.id, media: item.media._asMedia())) + + var duration: Double? + let media = item.media._asMedia() + if let file = media as? TelegramMediaFile { + duration = file.duration + } + subject = fetchMediaData(context: context, postbox: context.account.postbox, userLocation: .other, mediaReference: .story(peer: peerReference, id: item.id, media: media)) |> mapToSignal { (value, isImage) -> Signal in guard case let .data(data) = value, data.complete else { return .complete() @@ -2468,7 +2474,7 @@ public final class StoryItemSetContainerComponent: Component { } return .single(nil) |> then( - .single(.video(symlinkPath, nil, nil, nil, PixelDimensions(width: 720, height: 1280), .bottomRight)) + .single(.video(symlinkPath, nil, nil, nil, PixelDimensions(width: 720, height: 1280), duration ?? 0.0, [], .bottomRight)) |> delay(0.1, queue: Queue.mainQueue()) ) } diff --git a/submodules/TelegramUI/Sources/TelegramRootController.swift b/submodules/TelegramUI/Sources/TelegramRootController.swift index 260109549c..473dc3374f 100644 --- a/submodules/TelegramUI/Sources/TelegramRootController.swift +++ b/submodules/TelegramUI/Sources/TelegramRootController.swift @@ -305,8 +305,8 @@ public final class TelegramRootController: NavigationController, TelegramRootCon return nil case let .image(image, additionalImage, pipPosition): return .image(image, PixelDimensions(image.size), additionalImage, editorPIPPosition(pipPosition)) - case let .video(path, transitionImage, additionalPath, additionalTransitionImage, dimensions, pipPosition): - return .video(path, transitionImage, additionalPath, additionalTransitionImage, dimensions, editorPIPPosition(pipPosition)) + case let .video(path, transitionImage, additionalPath, additionalTransitionImage, dimensions, duration, positionChangeTimestamps, pipPosition): + return .video(path, transitionImage, additionalPath, additionalTransitionImage, dimensions, duration, positionChangeTimestamps, editorPIPPosition(pipPosition)) case let .asset(asset): return .asset(asset) case let .draft(draft):