From a34b7402be6587ccf2134e3361d22cbe2240fe35 Mon Sep 17 00:00:00 2001 From: Ilya Laktyushin Date: Wed, 24 Jun 2020 18:06:08 +0300 Subject: [PATCH] Video avatar fixes --- .../Sources/IsMediaStreamable.swift | 14 +- .../Sources/GalleryControllerNode.swift | 2 +- .../GalleryUI/Sources/GalleryItem.swift | 4 +- .../GalleryUI/Sources/GalleryPagerNode.swift | 37 +- .../Items/ChatAnimationGalleryItem.swift | 4 +- .../Items/ChatDocumentGalleryItem.swift | 4 +- .../Items/ChatExternalFileGalleryItem.swift | 4 +- .../Sources/Items/ChatImageGalleryItem.swift | 4 +- .../Items/UniversalVideoGalleryItem.swift | 4 +- .../Sources/InstantImageGalleryItem.swift | 4 +- .../InstantPageGalleryController.swift | 2 +- .../LegacyComponents/LegacyComponents.h | 1 - .../PublicHeaders/LegacyComponents/PGCamera.h | 3 +- .../TGCameraPhotoPreviewController.h | 33 - .../TGMediaAssetsController.h | 3 +- .../TGPhotoEditorController.h | 5 + .../TGPhotoEditorTabController.h | 1 + .../LegacyComponents/TGPhotoToolbarView.h | 6 +- .../LegacyComponents/Sources/PGCamera.m | 6 +- .../Sources/PGCameraCaptureSession.m | 9 +- .../Sources/PGPhotoBlurPass.m | 28 + .../Sources/TGAttachmentCarouselItemView.m | 8 +- .../Sources/TGCameraController.m | 302 ++--- .../Sources/TGCameraMainView.m | 4 +- .../Sources/TGCameraModeControl.m | 32 +- .../Sources/TGCameraPhotoPreviewController.m | 1203 ----------------- .../Sources/TGMediaAssetsController.m | 3 +- .../Sources/TGMediaAssetsPickerController.m | 2 +- .../Sources/TGMediaAvatarEditorTransition.m | 3 +- .../Sources/TGMediaPickerGalleryModel.m | 2 +- .../TGMediaPickerGalleryVideoScrubber.h | 5 +- .../TGMediaPickerGalleryVideoScrubber.m | 167 ++- .../Sources/TGMediaVideoConverter.m | 6 +- .../Sources/TGOverlayControllerWindow.m | 3 + .../Sources/TGPhotoAvatarCropController.m | 6 +- .../Sources/TGPhotoAvatarPreviewController.m | 114 +- .../Sources/TGPhotoCropController.m | 108 +- .../Sources/TGPhotoEditorController.m | 63 +- .../Sources/TGPhotoEditorTabController.m | 7 + .../Sources/TGPhotoPaintController.m | 48 +- .../Sources/TGPhotoToolbarView.m | 87 +- .../Sources/TGPhotoToolsController.m | 64 +- .../Sources/TGVideoEditAdjustments.m | 2 +- .../Sources/LegacyAvatarPicker.swift | 4 +- .../TelegramInitializeLegacyComponents.swift | 2 +- .../SecureIdDocumentGalleryController.swift | 2 +- .../SecureIdDocumentImageGalleryItem.swift | 4 +- .../Sources/AvatarGalleryController.swift | 9 +- .../Sources/PeerAvatarImageGalleryItem.swift | 18 +- .../Sources/ChannelInfoController.swift | 2 +- .../Sources/EditSettingsController.swift | 2 +- .../Themes/WallpaperGalleryController.swift | 2 +- .../Sources/Themes/WallpaperGalleryItem.swift | 4 +- .../Sources/CloudFileMediaResource.swift | 16 +- .../Sources/PeerPhotoUpdater.swift | 39 +- .../Sources/TelegramMediaImage.swift | 10 +- .../Sources/UpdatesApiUtils.swift | 2 +- .../Sources/PresentationThemeCodable.swift | 21 +- .../Sources/PeerInfo/PeerInfoHeaderNode.swift | 149 +- .../Sources/WallpaperResources.swift | 6 +- .../Sources/WebSearchGalleryController.swift | 2 +- .../Sources/WebSearchVideoGalleryItem.swift | 4 +- 62 files changed, 753 insertions(+), 1962 deletions(-) delete mode 100644 submodules/LegacyComponents/PublicHeaders/LegacyComponents/TGCameraPhotoPreviewController.h delete mode 100644 submodules/LegacyComponents/Sources/TGCameraPhotoPreviewController.m diff --git a/submodules/AccountContext/Sources/IsMediaStreamable.swift b/submodules/AccountContext/Sources/IsMediaStreamable.swift index 22e0da2821..ab7e940f3d 100644 --- a/submodules/AccountContext/Sources/IsMediaStreamable.swift +++ b/submodules/AccountContext/Sources/IsMediaStreamable.swift @@ -3,6 +3,8 @@ import Postbox import TelegramCore import SyncCore +private let minimalStreamableSize: Int = 384 * 1024 + public func isMediaStreamable(message: Message, media: TelegramMediaFile) -> Bool { if message.containsSecretMedia { return false @@ -13,7 +15,7 @@ public func isMediaStreamable(message: Message, media: TelegramMediaFile) -> Boo guard let size = media.size else { return false } - if size < 256 * 1024 { + if size < minimalStreamableSize { return false } for attribute in media.attributes { @@ -36,7 +38,7 @@ public func isMediaStreamable(media: TelegramMediaFile) -> Bool { guard let size = media.size else { return false } - if size < 1 * 1024 * 1024 { + if size < minimalStreamableSize { return false } for attribute in media.attributes { @@ -49,3 +51,11 @@ public func isMediaStreamable(media: TelegramMediaFile) -> Bool { } return false } + +public func isMediaStreamable(resource: MediaResource) -> Bool { + if let size = resource.size, size >= minimalStreamableSize { + return true + } else { + return false + } +} diff --git a/submodules/GalleryUI/Sources/GalleryControllerNode.swift b/submodules/GalleryUI/Sources/GalleryControllerNode.swift index 9f7895cdeb..e506895b82 100644 --- a/submodules/GalleryUI/Sources/GalleryControllerNode.swift +++ b/submodules/GalleryUI/Sources/GalleryControllerNode.swift @@ -184,7 +184,7 @@ open class GalleryControllerNode: ASDisplayNode, UIScrollViewDelegate, UIGesture node?.itemChanged = { [weak self] index in if let strongSelf = self { let pagerIndex = indexes[index] - strongSelf.pager.transaction(GalleryPagerTransaction(deleteItems: [], insertItems: [], updateItems: [], focusOnItem: pagerIndex)) + strongSelf.pager.transaction(GalleryPagerTransaction(deleteItems: [], insertItems: [], updateItems: [], focusOnItem: pagerIndex, synchronous: false)) } } } diff --git a/submodules/GalleryUI/Sources/GalleryItem.swift b/submodules/GalleryUI/Sources/GalleryItem.swift index c0b41d8194..d8c3ded14d 100644 --- a/submodules/GalleryUI/Sources/GalleryItem.swift +++ b/submodules/GalleryUI/Sources/GalleryItem.swift @@ -23,7 +23,7 @@ public struct GalleryItemIndexData: Equatable { public protocol GalleryItem { var id: AnyHashable { get } - func node() -> GalleryItemNode - func updateNode(node: GalleryItemNode) + func node(synchronous: Bool) -> GalleryItemNode + func updateNode(node: GalleryItemNode, synchronous: Bool) func thumbnailItem() -> (Int64, GalleryThumbnailItem)? } diff --git a/submodules/GalleryUI/Sources/GalleryPagerNode.swift b/submodules/GalleryUI/Sources/GalleryPagerNode.swift index 36f4b973b6..182d400bd1 100644 --- a/submodules/GalleryUI/Sources/GalleryPagerNode.swift +++ b/submodules/GalleryUI/Sources/GalleryPagerNode.swift @@ -64,12 +64,14 @@ public struct GalleryPagerTransaction { public let insertItems: [GalleryPagerInsertItem] public let updateItems: [GalleryPagerUpdateItem] public let focusOnItem: Int? + public let synchronous: Bool - public init(deleteItems: [Int], insertItems: [GalleryPagerInsertItem], updateItems: [GalleryPagerUpdateItem], focusOnItem: Int?) { + public init(deleteItems: [Int], insertItems: [GalleryPagerInsertItem], updateItems: [GalleryPagerUpdateItem], focusOnItem: Int?, synchronous: Bool) { self.deleteItems = deleteItems self.insertItems = insertItems self.updateItems = updateItems self.focusOnItem = focusOnItem + self.synchronous = synchronous } } @@ -310,12 +312,7 @@ public final class GalleryPagerNode: ASDisplayNode, UIScrollViewDelegate, UIGest } } - public func replaceItems(_ items: [GalleryItem], centralItemIndex: Int?, keepFirst: Bool = false) { - var items = items - if keepFirst && !self.items.isEmpty && !items.isEmpty { - items[0] = self.items[0] - } - + public func replaceItems(_ items: [GalleryItem], centralItemIndex: Int?, synchronous: Bool = false) { var updateItems: [GalleryPagerUpdateItem] = [] var deleteItems: [Int] = [] var insertItems: [GalleryPagerInsertItem] = [] @@ -333,7 +330,7 @@ public final class GalleryPagerNode: ASDisplayNode, UIScrollViewDelegate, UIGest insertItems.append(GalleryPagerInsertItem(index: i, item: items[i], previousIndex: previousIndexById[items[i].id])) } - self.transaction(GalleryPagerTransaction(deleteItems: deleteItems, insertItems: insertItems, updateItems: updateItems, focusOnItem: centralItemIndex)) + self.transaction(GalleryPagerTransaction(deleteItems: deleteItems, insertItems: insertItems, updateItems: updateItems, focusOnItem: centralItemIndex, synchronous: synchronous)) } public func transaction(_ transaction: GalleryPagerTransaction) { @@ -341,7 +338,7 @@ public final class GalleryPagerNode: ASDisplayNode, UIScrollViewDelegate, UIGest self.items[updatedItem.previousIndex] = updatedItem.item if let itemNode = self.visibleItemNode(at: updatedItem.previousIndex) { //print("update visible node at \(updatedItem.previousIndex)") - updatedItem.item.updateNode(node: itemNode) + updatedItem.item.updateNode(node: itemNode, synchronous: transaction.synchronous) } } @@ -395,7 +392,7 @@ public final class GalleryPagerNode: ASDisplayNode, UIScrollViewDelegate, UIGest self.centralItemIndex = focusOnItem } - self.updateItemNodes(transition: .immediate, notify: transaction.focusOnItem != nil) + self.updateItemNodes(transition: .immediate, notify: transaction.focusOnItem != nil, synchronous: transaction.synchronous) //print("visible indices after update \(self.itemNodes.map { $0.index })") } @@ -425,18 +422,18 @@ public final class GalleryPagerNode: ASDisplayNode, UIScrollViewDelegate, UIGest private func goToPreviousItem() { if let index = self.centralItemIndex, index > 0 { - self.transaction(GalleryPagerTransaction(deleteItems: [], insertItems: [], updateItems: [], focusOnItem: index - 1)) + self.transaction(GalleryPagerTransaction(deleteItems: [], insertItems: [], updateItems: [], focusOnItem: index - 1, synchronous: false)) } } private func goToNextItem() { if let index = self.centralItemIndex, index < self.items.count - 1 { - self.transaction(GalleryPagerTransaction(deleteItems: [], insertItems: [], updateItems: [], focusOnItem: index + 1)) + self.transaction(GalleryPagerTransaction(deleteItems: [], insertItems: [], updateItems: [], focusOnItem: index + 1, synchronous: false)) } } - private func makeNodeForItem(at index: Int) -> GalleryItemNode { - let node = self.items[index].node() + private func makeNodeForItem(at index: Int, synchronous: Bool) -> GalleryItemNode { + let node = self.items[index].node(synchronous: synchronous) node.toggleControlsVisibility = self.toggleControlsVisibility node.dismiss = self.dismiss node.beginCustomDismiss = self.beginCustomDismiss @@ -475,7 +472,7 @@ public final class GalleryPagerNode: ASDisplayNode, UIScrollViewDelegate, UIGest self.itemNodes.remove(at: internalIndex) } - private func updateItemNodes(transition: ContainedViewLayoutTransition, forceOffsetReset: Bool = false, notify: Bool = false, forceLoad: Bool = false) { + private func updateItemNodes(transition: ContainedViewLayoutTransition, forceOffsetReset: Bool = false, notify: Bool = false, forceLoad: Bool = false, synchronous: Bool = false) { if self.items.isEmpty || self.containerLayout == nil { return } @@ -487,7 +484,7 @@ public final class GalleryPagerNode: ASDisplayNode, UIScrollViewDelegate, UIGest } while self.itemNodes.count > 0 } if self.itemNodes.isEmpty { - let node = self.makeNodeForItem(at: self.centralItemIndex ?? 0) + let node = self.makeNodeForItem(at: self.centralItemIndex ?? 0, synchronous: synchronous) node.frame = CGRect(origin: CGPoint(), size: scrollView.bounds.size) if let containerLayout = self.containerLayout { node.containerLayoutUpdated(containerLayout.0, navigationBarHeight: containerLayout.1, transition: .immediate) @@ -502,7 +499,7 @@ public final class GalleryPagerNode: ASDisplayNode, UIScrollViewDelegate, UIGest if let centralItemIndex = self.centralItemIndex, let centralItemNode = self.visibleItemNode(at: centralItemIndex) { if centralItemIndex != 0 { if self.shouldLoadItems(force: forceLoad) && self.visibleItemNode(at: centralItemIndex - 1) == nil { - let node = self.makeNodeForItem(at: centralItemIndex - 1) + let node = self.makeNodeForItem(at: centralItemIndex - 1, synchronous: synchronous) node.frame = centralItemNode.frame.offsetBy(dx: -centralItemNode.frame.size.width - self.pageGap, dy: 0.0) if let containerLayout = self.containerLayout { node.containerLayoutUpdated(containerLayout.0, navigationBarHeight: containerLayout.1, transition: .immediate) @@ -513,7 +510,7 @@ public final class GalleryPagerNode: ASDisplayNode, UIScrollViewDelegate, UIGest if centralItemIndex != self.items.count - 1 { if self.shouldLoadItems(force: forceLoad) && self.visibleItemNode(at: centralItemIndex + 1) == nil { - let node = self.makeNodeForItem(at: centralItemIndex + 1) + let node = self.makeNodeForItem(at: centralItemIndex + 1, synchronous: synchronous) node.frame = centralItemNode.frame.offsetBy(dx: centralItemNode.frame.size.width + self.pageGap, dy: 0.0) if let containerLayout = self.containerLayout { node.containerLayoutUpdated(containerLayout.0, navigationBarHeight: containerLayout.1, transition: .immediate) @@ -549,7 +546,7 @@ public final class GalleryPagerNode: ASDisplayNode, UIScrollViewDelegate, UIGest if centralItemCandidateNode.index != 0 { if self.shouldLoadItems(force: forceLoad) && self.visibleItemNode(at: centralItemCandidateNode.index - 1) == nil { - let node = self.makeNodeForItem(at: centralItemCandidateNode.index - 1) + let node = self.makeNodeForItem(at: centralItemCandidateNode.index - 1, synchronous: synchronous) node.frame = centralItemCandidateNode.frame.offsetBy(dx: -centralItemCandidateNode.frame.size.width - self.pageGap, dy: 0.0) if let containerLayout = self.containerLayout { node.containerLayoutUpdated(containerLayout.0, navigationBarHeight: containerLayout.1, transition: .immediate) @@ -560,7 +557,7 @@ public final class GalleryPagerNode: ASDisplayNode, UIScrollViewDelegate, UIGest if centralItemCandidateNode.index != items.count - 1 { if self.shouldLoadItems(force: forceLoad) && self.visibleItemNode(at: centralItemCandidateNode.index + 1) == nil { - let node = self.makeNodeForItem(at: centralItemCandidateNode.index + 1) + let node = self.makeNodeForItem(at: centralItemCandidateNode.index + 1, synchronous: synchronous) node.frame = centralItemCandidateNode.frame.offsetBy(dx: centralItemCandidateNode.frame.size.width + self.pageGap, dy: 0.0) if let containerLayout = self.containerLayout { node.containerLayoutUpdated(containerLayout.0, navigationBarHeight: containerLayout.1, transition: .immediate) diff --git a/submodules/GalleryUI/Sources/Items/ChatAnimationGalleryItem.swift b/submodules/GalleryUI/Sources/Items/ChatAnimationGalleryItem.swift index d3cb9493f7..7d67b546ac 100644 --- a/submodules/GalleryUI/Sources/Items/ChatAnimationGalleryItem.swift +++ b/submodules/GalleryUI/Sources/Items/ChatAnimationGalleryItem.swift @@ -31,7 +31,7 @@ class ChatAnimationGalleryItem: GalleryItem { self.location = location } - func node() -> GalleryItemNode { + func node(synchronous: Bool) -> GalleryItemNode { let node = ChatAnimationGalleryItemNode(context: self.context, presentationData: self.presentationData) for media in self.message.media { @@ -46,7 +46,7 @@ class ChatAnimationGalleryItem: GalleryItem { return node } - func updateNode(node: GalleryItemNode) { + func updateNode(node: GalleryItemNode, synchronous: Bool) { if let node = node as? ChatAnimationGalleryItemNode { node.setMessage(self.message) } diff --git a/submodules/GalleryUI/Sources/Items/ChatDocumentGalleryItem.swift b/submodules/GalleryUI/Sources/Items/ChatDocumentGalleryItem.swift index 7d3aeb87af..e1fa63f3f6 100644 --- a/submodules/GalleryUI/Sources/Items/ChatDocumentGalleryItem.swift +++ b/submodules/GalleryUI/Sources/Items/ChatDocumentGalleryItem.swift @@ -28,7 +28,7 @@ class ChatDocumentGalleryItem: GalleryItem { self.location = location } - func node() -> GalleryItemNode { + func node(synchronous: Bool) -> GalleryItemNode { let node = ChatDocumentGalleryItemNode(context: self.context, presentationData: self.presentationData) for media in self.message.media { @@ -51,7 +51,7 @@ class ChatDocumentGalleryItem: GalleryItem { return node } - func updateNode(node: GalleryItemNode) { + func updateNode(node: GalleryItemNode, synchronous: Bool) { if let node = node as? ChatDocumentGalleryItemNode, let location = self.location { node._title.set(.single(self.presentationData.strings.Items_NOfM("\(location.index + 1)", "\(location.count)").0)) node.setMessage(self.message) diff --git a/submodules/GalleryUI/Sources/Items/ChatExternalFileGalleryItem.swift b/submodules/GalleryUI/Sources/Items/ChatExternalFileGalleryItem.swift index 7dee5b3a98..1b0964ad77 100644 --- a/submodules/GalleryUI/Sources/Items/ChatExternalFileGalleryItem.swift +++ b/submodules/GalleryUI/Sources/Items/ChatExternalFileGalleryItem.swift @@ -29,7 +29,7 @@ class ChatExternalFileGalleryItem: GalleryItem { self.location = location } - func node() -> GalleryItemNode { + func node(synchronous: Bool) -> GalleryItemNode { let node = ChatExternalFileGalleryItemNode(context: self.context, presentationData: self.presentationData) for media in self.message.media { @@ -52,7 +52,7 @@ class ChatExternalFileGalleryItem: GalleryItem { return node } - func updateNode(node: GalleryItemNode) { + func updateNode(node: GalleryItemNode, synchronous: Bool) { if let node = node as? ChatExternalFileGalleryItemNode, let location = self.location { node._title.set(.single(self.presentationData.strings.Items_NOfM("\(location.index + 1)", "\(location.count)").0)) node.setMessage(self.message) diff --git a/submodules/GalleryUI/Sources/Items/ChatImageGalleryItem.swift b/submodules/GalleryUI/Sources/Items/ChatImageGalleryItem.swift index a085fb93ee..72b22f6c55 100644 --- a/submodules/GalleryUI/Sources/Items/ChatImageGalleryItem.swift +++ b/submodules/GalleryUI/Sources/Items/ChatImageGalleryItem.swift @@ -101,7 +101,7 @@ class ChatImageGalleryItem: GalleryItem { self.present = present } - func node() -> GalleryItemNode { + func node(synchronous: Bool) -> GalleryItemNode { let node = ChatImageGalleryItemNode(context: self.context, presentationData: self.presentationData, performAction: self.performAction, openActionOptions: self.openActionOptions, present: self.present) for media in self.message.media { @@ -131,7 +131,7 @@ class ChatImageGalleryItem: GalleryItem { return node } - func updateNode(node: GalleryItemNode) { + func updateNode(node: GalleryItemNode, synchronous: Bool) { if let node = node as? ChatImageGalleryItemNode, let location = self.location { node._title.set(.single(self.presentationData.strings.Items_NOfM("\(location.index + 1)", "\(location.count)").0)) diff --git a/submodules/GalleryUI/Sources/Items/UniversalVideoGalleryItem.swift b/submodules/GalleryUI/Sources/Items/UniversalVideoGalleryItem.swift index 66e1f24dd4..3248033daa 100644 --- a/submodules/GalleryUI/Sources/Items/UniversalVideoGalleryItem.swift +++ b/submodules/GalleryUI/Sources/Items/UniversalVideoGalleryItem.swift @@ -66,7 +66,7 @@ public class UniversalVideoGalleryItem: GalleryItem { self.present = present } - public func node() -> GalleryItemNode { + public func node(synchronous: Bool) -> GalleryItemNode { let node = UniversalVideoGalleryItemNode(context: self.context, presentationData: self.presentationData, performAction: self.performAction, openActionOptions: self.openActionOptions, present: self.present) if let indexData = self.indexData { @@ -78,7 +78,7 @@ public class UniversalVideoGalleryItem: GalleryItem { return node } - public func updateNode(node: GalleryItemNode) { + public func updateNode(node: GalleryItemNode, synchronous: Bool) { if let node = node as? UniversalVideoGalleryItemNode { if let indexData = self.indexData { node._title.set(.single(self.presentationData.strings.Items_NOfM("\(indexData.position + 1)", "\(indexData.totalCount)").0)) diff --git a/submodules/InstantPageUI/Sources/InstantImageGalleryItem.swift b/submodules/InstantPageUI/Sources/InstantImageGalleryItem.swift index f29c7a2ef7..4c2c5bf418 100644 --- a/submodules/InstantPageUI/Sources/InstantImageGalleryItem.swift +++ b/submodules/InstantPageUI/Sources/InstantImageGalleryItem.swift @@ -62,7 +62,7 @@ class InstantImageGalleryItem: GalleryItem { self.openUrlOptions = openUrlOptions } - func node() -> GalleryItemNode { + func node(synchronous: Bool) -> GalleryItemNode { let node = InstantImageGalleryItemNode(context: self.context, presentationData: self.presentationData, openUrl: self.openUrl, openUrlOptions: self.openUrlOptions) node.setImage(imageReference: self.imageReference) @@ -76,7 +76,7 @@ class InstantImageGalleryItem: GalleryItem { return node } - func updateNode(node: GalleryItemNode) { + func updateNode(node: GalleryItemNode, synchronous: Bool) { if let node = node as? InstantImageGalleryItemNode { if let location = self.location { node._title.set(.single(self.presentationData.strings.Items_NOfM("\(location.position + 1)", "\(location.totalCount)").0)) diff --git a/submodules/InstantPageUI/Sources/InstantPageGalleryController.swift b/submodules/InstantPageUI/Sources/InstantPageGalleryController.swift index 5d0ecb188d..7a7cdc3a09 100644 --- a/submodules/InstantPageUI/Sources/InstantPageGalleryController.swift +++ b/submodules/InstantPageUI/Sources/InstantPageGalleryController.swift @@ -221,7 +221,7 @@ public class InstantPageGalleryController: ViewController, StandalonePresentable if strongSelf.isViewLoaded { strongSelf.galleryNode.pager.replaceItems(strongSelf.entries.map({ $0.item(context: context, webPage: webPage, message: message, presentationData: strongSelf.presentationData, fromPlayingVideo: fromPlayingVideo, landscape: landscape, openUrl: strongSelf.innerOpenUrl, openUrlOptions: strongSelf.openUrlOptions) - }), centralItemIndex: centralIndex, keepFirst: false) + }), centralItemIndex: centralIndex) let ready = strongSelf.galleryNode.pager.ready() |> timeout(2.0, queue: Queue.mainQueue(), alternate: .single(Void())) |> afterNext { [weak strongSelf] _ in strongSelf?.didSetReady = true diff --git a/submodules/LegacyComponents/PublicHeaders/LegacyComponents/LegacyComponents.h b/submodules/LegacyComponents/PublicHeaders/LegacyComponents/LegacyComponents.h index 54dac0dba1..dc724dfd84 100644 --- a/submodules/LegacyComponents/PublicHeaders/LegacyComponents/LegacyComponents.h +++ b/submodules/LegacyComponents/PublicHeaders/LegacyComponents/LegacyComponents.h @@ -81,7 +81,6 @@ #import #import #import -#import #import #import #import diff --git a/submodules/LegacyComponents/PublicHeaders/LegacyComponents/PGCamera.h b/submodules/LegacyComponents/PublicHeaders/LegacyComponents/PGCamera.h index d6148ab8f0..23ac166b8d 100644 --- a/submodules/LegacyComponents/PublicHeaders/LegacyComponents/PGCamera.h +++ b/submodules/LegacyComponents/PublicHeaders/LegacyComponents/PGCamera.h @@ -25,7 +25,8 @@ typedef enum PGCameraModePhoto, PGCameraModeVideo, PGCameraModeSquarePhoto, - PGCameraModeSquareVideo + PGCameraModeSquareVideo, + PGCameraModeSquareSwing } PGCameraMode; typedef enum diff --git a/submodules/LegacyComponents/PublicHeaders/LegacyComponents/TGCameraPhotoPreviewController.h b/submodules/LegacyComponents/PublicHeaders/LegacyComponents/TGCameraPhotoPreviewController.h deleted file mode 100644 index 19ad139c2a..0000000000 --- a/submodules/LegacyComponents/PublicHeaders/LegacyComponents/TGCameraPhotoPreviewController.h +++ /dev/null @@ -1,33 +0,0 @@ -#import -#import - -@class PGCameraShotMetadata; -@class PGPhotoEditorValues; -@class TGSuggestionContext; - -@interface TGCameraPhotoPreviewController : TGOverlayController - -@property (nonatomic, assign) bool allowCaptions; - -@property (nonatomic, copy) CGRect(^beginTransitionIn)(void); -@property (nonatomic, copy) CGRect(^beginTransitionOut)(CGRect referenceFrame); - -@property (nonatomic, copy) void(^finishedTransitionIn)(void); - -@property (nonatomic, copy) void (^photoEditorShown)(void); -@property (nonatomic, copy) void (^photoEditorHidden)(void); - -@property (nonatomic, copy) void(^retakePressed)(void); -@property (nonatomic, copy) void(^sendPressed)(TGOverlayController *controller, UIImage *resultImage, NSString *caption, NSArray *entities, NSArray *stickers, NSNumber *timer); - -@property (nonatomic, strong) TGSuggestionContext *suggestionContext; -@property (nonatomic, assign) bool shouldStoreAssets; -@property (nonatomic, assign) bool hasTimer; -@property (nonatomic, assign) bool hasSilentPosting; -@property (nonatomic, assign) bool hasSchedule; - -- (instancetype)initWithContext:(id)context image:(UIImage *)image metadata:(PGCameraShotMetadata *)metadata recipientName:(NSString *)recipientName saveCapturedMedia:(bool)saveCapturedMedia saveEditedPhotos:(bool)saveEditedPhotos; -- (instancetype)initWithContext:(id)context image:(UIImage *)image metadata:(PGCameraShotMetadata *)metadata recipientName:(NSString *)recipientName backButtonTitle:(NSString *)backButtonTitle doneButtonTitle:(NSString *)doneButtonTitle saveCapturedMedia:(bool)saveCapturedMedia saveEditedPhotos:(bool)saveEditedPhotos; - - -@end diff --git a/submodules/LegacyComponents/PublicHeaders/LegacyComponents/TGMediaAssetsController.h b/submodules/LegacyComponents/PublicHeaders/LegacyComponents/TGMediaAssetsController.h index 6e5fde3d24..a0ae9554a1 100644 --- a/submodules/LegacyComponents/PublicHeaders/LegacyComponents/TGMediaAssetsController.h +++ b/submodules/LegacyComponents/PublicHeaders/LegacyComponents/TGMediaAssetsController.h @@ -38,10 +38,11 @@ typedef enum @property (nonatomic, readonly) UIImage *badge; @property (nonatomic, readonly) UIColor *badgeTextColor; @property (nonatomic, readonly) UIImage *sendIconImage; +@property (nonatomic, readonly) UIImage *doneIconImage; @property (nonatomic, readonly) UIColor *maybeAccentColor; -+ (instancetype)palleteWithDark:(bool)dark backgroundColor:(UIColor *)backgroundColor selectionColor:(UIColor *)selectionColor separatorColor:(UIColor *)separatorColor textColor:(UIColor *)textColor secondaryTextColor:(UIColor *)secondaryTextColor accentColor:(UIColor *)accentColor barBackgroundColor:(UIColor *)barBackgroundColor barSeparatorColor:(UIColor *)barSeparatorColor navigationTitleColor:(UIColor *)navigationTitleColor badge:(UIImage *)badge badgeTextColor:(UIColor *)badgeTextColor sendIconImage:(UIImage *)sendIconImage maybeAccentColor:(UIColor *)maybeAccentColor; ++ (instancetype)palleteWithDark:(bool)dark backgroundColor:(UIColor *)backgroundColor selectionColor:(UIColor *)selectionColor separatorColor:(UIColor *)separatorColor textColor:(UIColor *)textColor secondaryTextColor:(UIColor *)secondaryTextColor accentColor:(UIColor *)accentColor barBackgroundColor:(UIColor *)barBackgroundColor barSeparatorColor:(UIColor *)barSeparatorColor navigationTitleColor:(UIColor *)navigationTitleColor badge:(UIImage *)badge badgeTextColor:(UIColor *)badgeTextColor sendIconImage:(UIImage *)sendIconImage doneIconImage:(UIImage *)doneIconImage maybeAccentColor:(UIColor *)maybeAccentColor; @end diff --git a/submodules/LegacyComponents/PublicHeaders/LegacyComponents/TGPhotoEditorController.h b/submodules/LegacyComponents/PublicHeaders/LegacyComponents/TGPhotoEditorController.h index 32eb676b29..bc75e09de4 100644 --- a/submodules/LegacyComponents/PublicHeaders/LegacyComponents/TGPhotoEditorController.h +++ b/submodules/LegacyComponents/PublicHeaders/LegacyComponents/TGPhotoEditorController.h @@ -10,6 +10,7 @@ @class PGCameraShotMetadata; @class TGSuggestionContext; @class TGPhotoEditorController; +@class AVPlayer; @protocol TGPhotoPaintStickersContext; @class TGPhotoEntitiesContainerView; @@ -57,6 +58,10 @@ typedef enum { @property (nonatomic, strong) PGCameraShotMetadata *metadata; @property (nonatomic, strong) NSArray *faces; +@property (nonatomic, strong) NSArray *cachedVideoThumbnails; + +@property (nonatomic, strong) AVPlayer *player; + @property (nonatomic, strong) TGPhotoEntitiesContainerView *entitiesView; - (instancetype)initWithContext:(id)context item:(id)item intent:(TGPhotoEditorControllerIntent)intent adjustments:(id)adjustments caption:(NSString *)caption screenImage:(UIImage *)screenImage availableTabs:(TGPhotoEditorTab)availableTabs selectedTab:(TGPhotoEditorTab)selectedTab; diff --git a/submodules/LegacyComponents/PublicHeaders/LegacyComponents/TGPhotoEditorTabController.h b/submodules/LegacyComponents/PublicHeaders/LegacyComponents/TGPhotoEditorTabController.h index c296229041..14033dc050 100644 --- a/submodules/LegacyComponents/PublicHeaders/LegacyComponents/TGPhotoEditorTabController.h +++ b/submodules/LegacyComponents/PublicHeaders/LegacyComponents/TGPhotoEditorTabController.h @@ -63,6 +63,7 @@ - (bool)isDismissAllowed; +- (bool)hasOnScreenNavigation; - (UIInterfaceOrientation)effectiveOrientation; - (UIInterfaceOrientation)effectiveOrientation:(UIInterfaceOrientation)orientation; diff --git a/submodules/LegacyComponents/PublicHeaders/LegacyComponents/TGPhotoToolbarView.h b/submodules/LegacyComponents/PublicHeaders/LegacyComponents/TGPhotoToolbarView.h index 04689f0b6f..7b49f99965 100644 --- a/submodules/LegacyComponents/PublicHeaders/LegacyComponents/TGPhotoToolbarView.h +++ b/submodules/LegacyComponents/PublicHeaders/LegacyComponents/TGPhotoToolbarView.h @@ -28,7 +28,8 @@ typedef enum typedef enum { TGPhotoEditorDoneButtonSend, - TGPhotoEditorDoneButtonCheck + TGPhotoEditorDoneButtonCheck, + TGPhotoEditorDoneButtonDone } TGPhotoEditorDoneButton; @interface TGPhotoToolbarView : UIView @@ -47,6 +48,9 @@ typedef enum @property (nonatomic, readonly) CGRect cancelButtonFrame; @property (nonatomic, readonly) CGRect doneButtonFrame; +@property (nonatomic, assign) TGPhotoEditorBackButton backButtonType; +@property (nonatomic, assign) TGPhotoEditorDoneButton doneButtonType; + - (instancetype)initWithBackButton:(TGPhotoEditorBackButton)backButton doneButton:(TGPhotoEditorDoneButton)doneButton solidBackground:(bool)solidBackground; - (void)transitionInAnimated:(bool)animated; diff --git a/submodules/LegacyComponents/Sources/PGCamera.m b/submodules/LegacyComponents/Sources/PGCamera.m index 0c48c66fff..6431e15773 100644 --- a/submodules/LegacyComponents/Sources/PGCamera.m +++ b/submodules/LegacyComponents/Sources/PGCamera.m @@ -384,7 +384,7 @@ NSString *const PGCameraAdjustingFocusKey = @"adjustingFocus"; NSData *imageData = [AVCaptureStillImageOutput jpegStillImageNSDataRepresentation:imageDataSampleBuffer]; UIImage *image = [[UIImage alloc] initWithData:imageData]; - if (self.cameraMode == PGCameraModeSquarePhoto || self.cameraMode == PGCameraModeSquareVideo) + if (self.cameraMode == PGCameraModeSquarePhoto || self.cameraMode == PGCameraModeSquareVideo || self.cameraMode == PGCameraModeSquareSwing) { CGFloat shorterSide = MIN(image.size.width, image.size.height); CGFloat longerSide = MAX(image.size.width, image.size.height); @@ -636,7 +636,7 @@ NSString *const PGCameraAdjustingFocusKey = @"adjustingFocus"; - (bool)flashActive { - if (self.cameraMode == PGCameraModeVideo || self.cameraMode == PGCameraModeSquareVideo) + if (self.cameraMode == PGCameraModeVideo || self.cameraMode == PGCameraModeSquareVideo || self.cameraMode == PGCameraModeSquareSwing) return self.captureSession.videoDevice.torchActive; return self.captureSession.videoDevice.flashActive; @@ -644,7 +644,7 @@ NSString *const PGCameraAdjustingFocusKey = @"adjustingFocus"; - (bool)flashAvailable { - if (self.cameraMode == PGCameraModeVideo || self.cameraMode == PGCameraModeSquareVideo) + if (self.cameraMode == PGCameraModeVideo || self.cameraMode == PGCameraModeSquareVideo || self.cameraMode == PGCameraModeSquareSwing) return self.captureSession.videoDevice.torchAvailable; return self.captureSession.videoDevice.flashAvailable; diff --git a/submodules/LegacyComponents/Sources/PGCameraCaptureSession.m b/submodules/LegacyComponents/Sources/PGCameraCaptureSession.m index 734230d551..e651da1ae3 100644 --- a/submodules/LegacyComponents/Sources/PGCameraCaptureSession.m +++ b/submodules/LegacyComponents/Sources/PGCameraCaptureSession.m @@ -188,7 +188,7 @@ const NSInteger PGCameraFrameRate = 30; if (self.currentCameraPosition != _preferredCameraPosition) return true; - if (self.currentMode == PGCameraModeVideo || self.currentMode == PGCameraModeSquareVideo) + if (self.currentMode == PGCameraModeVideo || self.currentMode == PGCameraModeSquareVideo || self.currentMode == PGCameraModeSquareSwing) return true; if (self.zoomLevel > FLT_EPSILON) @@ -270,11 +270,12 @@ const NSInteger PGCameraFrameRate = 30; case PGCameraModeVideo: case PGCameraModeSquareVideo: + case PGCameraModeSquareSwing: { self.sessionPreset = AVCaptureSessionPresetInputPriority; [self switchToBestVideoFormatForDevice:_videoDevice]; [self _addAudioInputRequestAudioSession:true]; - [self setFrameRate:PGCameraFrameRate forDevice:_videoDevice]; + [self setFrameRate:mode == PGCameraFrameRate forDevice:_videoDevice]; } break; @@ -529,6 +530,7 @@ const NSInteger PGCameraFrameRate = 30; { case PGCameraModeVideo: case PGCameraModeSquareVideo: + case PGCameraModeSquareSwing: return _videoFlashMode; default: @@ -544,6 +546,7 @@ const NSInteger PGCameraFrameRate = 30; { case PGCameraModeVideo: case PGCameraModeSquareVideo: + case PGCameraModeSquareSwing: { AVCaptureTorchMode torchMode = [PGCameraCaptureSession _deviceTorchModeForCameraFlashMode:mode]; if (device.hasTorch && [device isTorchModeSupported:torchMode]) @@ -660,7 +663,7 @@ const NSInteger PGCameraFrameRate = 30; [self commitConfiguration]; - if (self.currentMode == PGCameraModeVideo || self.currentMode == PGCameraModeSquareVideo) + if (self.currentMode == PGCameraModeVideo || self.currentMode == PGCameraModeSquareVideo || self.currentMode == PGCameraModeSquareSwing) [self setFrameRate:PGCameraFrameRate forDevice:deviceForTargetPosition]; else [self setFrameRate:0 forDevice:deviceForTargetPosition]; diff --git a/submodules/LegacyComponents/Sources/PGPhotoBlurPass.m b/submodules/LegacyComponents/Sources/PGPhotoBlurPass.m index 50e5ef9b7e..5ca11f9f0c 100644 --- a/submodules/LegacyComponents/Sources/PGPhotoBlurPass.m +++ b/submodules/LegacyComponents/Sources/PGPhotoBlurPass.m @@ -1,6 +1,7 @@ #import "PGPhotoBlurPass.h" #import "GPUImageTwoInputFilter.h" +#import "GPUImageThreeInputFilter.h" #import "PGPhotoGaussianBlurFilter.h" NSString *const PGPhotoRadialBlurShaderString = PGShaderString @@ -54,12 +55,33 @@ NSString *const PGPhotoLinearBlurShaderString = PGShaderString } ); +NSString *const PGPhotoMaskedBlurShaderString = PGShaderString +( + varying highp vec2 texCoord; + varying highp vec2 texCoord2; + varying highp vec2 texCoord3; + + uniform sampler2D sourceImage; + uniform sampler2D inputImageTexture2; + uniform sampler2D inputImageTexture3; + + void main() + { + lowp vec4 sharpImageColor = texture2D(sourceImage, texCoord); + lowp vec4 blurredImageColor = texture2D(inputImageTexture2, texCoord2); + lowp vec4 maskImageColor = texture2D(inputImageTexture3, texCoord3); + + gl_FragColor = mix(blurredImageColor, sharpImageColor, maskImageColor.r); + } +); + @interface PGPhotoBlurFilter : GPUImageOutput { PGPhotoGaussianBlurFilter *_blurFilter; GPUImageTwoInputFilter *_radialFocusFilter; GPUImageTwoInputFilter *_linearFocusFilter; + GPUImageThreeInputFilter *_maskedFilter; GPUImageOutput *_currentFocusFilter; @@ -104,6 +126,10 @@ NSString *const PGPhotoLinearBlurShaderString = PGShaderString } break; + case PGBlurToolTypePortrait: + { + _currentFocusFilter = _maskedFilter; + } default: break; } @@ -192,6 +218,7 @@ NSString *const PGPhotoLinearBlurShaderString = PGShaderString [_blurFilter setInputSize:newSize atIndex:textureIndex]; [_radialFocusFilter setInputSize:newSize atIndex:textureIndex]; [_linearFocusFilter setInputSize:newSize atIndex:textureIndex]; + [_maskedFilter setInputSize:newSize atIndex:textureIndex]; CGFloat aspectRatio = newSize.height / newSize.width; [_radialFocusFilter setFloat:(float)aspectRatio forUniformName:@"aspectRatio"]; @@ -203,6 +230,7 @@ NSString *const PGPhotoLinearBlurShaderString = PGShaderString [_blurFilter setInputRotation:newInputRotation atIndex:textureIndex]; [_radialFocusFilter setInputRotation:newInputRotation atIndex:textureIndex]; [_linearFocusFilter setInputRotation:newInputRotation atIndex:textureIndex]; + [_maskedFilter setInputRotation:newInputRotation atIndex:textureIndex]; } - (CGSize)maximumOutputSize diff --git a/submodules/LegacyComponents/Sources/TGAttachmentCarouselItemView.m b/submodules/LegacyComponents/Sources/TGAttachmentCarouselItemView.m index 3d408137f2..c416a4cb3d 100644 --- a/submodules/LegacyComponents/Sources/TGAttachmentCarouselItemView.m +++ b/submodules/LegacyComponents/Sources/TGAttachmentCarouselItemView.m @@ -937,7 +937,7 @@ const NSUInteger TGAttachmentDisplayedAssetLimit = 500; { if (editableItem.isVideo) { if ([editableItem isKindOfClass:[TGMediaAsset class]]) { - return [TGMediaAssetImageSignals avAssetForVideoAsset:(TGMediaAsset *)editableItem]; + return [TGMediaAssetImageSignals avAssetForVideoAsset:(TGMediaAsset *)editableItem allowNetworkAccess:true]; } else if ([editableItem isKindOfClass:[TGCameraCapturedVideo class]]) { return ((TGCameraCapturedVideo *)editableItem).avAsset; } else { @@ -951,7 +951,7 @@ const NSUInteger TGAttachmentDisplayedAssetLimit = 500; TGOverlayControllerWindow *controllerWindow = [[TGOverlayControllerWindow alloc] initWithManager:windowManager parentController:_parentController contentController:controller]; controllerWindow.hidden = false; controller.view.clipsToBounds = true; - + transition.referenceFrame = ^CGRect { UIView *referenceView = referenceViewForAsset(asset); @@ -972,14 +972,14 @@ const NSUInteger TGAttachmentDisplayedAssetLimit = 500; __strong TGAttachmentCarouselItemView *strongSelf = weakSelf; if (strongSelf == nil) return; - + transition.outReferenceFrame = outReferenceFrame; transition.repView = repView; [transition dismissAnimated:true completion:^ { strongSelf->_hiddenItem = nil; [strongSelf updateHiddenCellAnimated:false]; - + dispatch_async(dispatch_get_main_queue(), ^ { if (completion != nil) diff --git a/submodules/LegacyComponents/Sources/TGCameraController.m b/submodules/LegacyComponents/Sources/TGCameraController.m index d2c7db8292..0a871d8dd2 100644 --- a/submodules/LegacyComponents/Sources/TGCameraController.m +++ b/submodules/LegacyComponents/Sources/TGCameraController.m @@ -921,6 +921,7 @@ static CGPoint TGCameraControllerClampPointToScreenSize(__unused id self, __unus case PGCameraModeVideo: case PGCameraModeSquareVideo: + case PGCameraModeSquareSwing: { if (!_camera.isRecordingVideo) { @@ -994,7 +995,7 @@ static CGPoint TGCameraControllerClampPointToScreenSize(__unused id self, __unus }); }]; } - else if (cameraMode == PGCameraModeVideo) + else if (cameraMode == PGCameraModeVideo || cameraMode == PGCameraModeSquareVideo || cameraMode == PGCameraModeSquareSwing) { if (!_camera.isRecordingVideo) { @@ -1009,12 +1010,16 @@ static CGPoint TGCameraControllerClampPointToScreenSize(__unused id self, __unus if (success) { TGCameraCapturedVideo *capturedVideo = [[TGCameraCapturedVideo alloc] initWithURL:outputURL]; - [strongSelf addResultItem:capturedVideo]; - - if (![strongSelf maybePresentResultControllerForItem:capturedVideo completion:nil]) + if (strongSelf->_intent == TGCameraControllerAvatarIntent || strongSelf->_intent == TGCameraControllerSignupAvatarIntent) { - strongSelf->_camera.disabled = false; - [strongSelf->_interfaceView setRecordingVideo:false animated:true]; + [strongSelf presentPhotoResultControllerWithImage:capturedVideo metadata:nil completion:^{}]; + } else { + [strongSelf addResultItem:capturedVideo]; + if (![strongSelf maybePresentResultControllerForItem:capturedVideo completion:nil]) + { + strongSelf->_camera.disabled = false; + [strongSelf->_interfaceView setRecordingVideo:false animated:true]; + } } } else @@ -1655,15 +1660,29 @@ static CGPoint TGCameraControllerClampPointToScreenSize(__unused id self, __unus #pragma mark - Legacy Photo Result -- (void)presentPhotoResultControllerWithImage:(UIImage *)image metadata:(PGCameraShotMetadata *)metadata completion:(void (^)(void))completion +- (void)presentPhotoResultControllerWithImage:(id)input metadata:(PGCameraShotMetadata *)metadata completion:(void (^)(void))completion { [[[LegacyComponentsGlobals provider] applicationInstance] setIdleTimerDisabled:false]; - if (image == nil || image.size.width < FLT_EPSILON) + if (input == nil || ([input isKindOfClass:[UIImage class]] && ((UIImage *)input).size.width < FLT_EPSILON)) { [self beginTransitionOutWithVelocity:0.0f]; return; } + + UIImage *image = nil; + if ([input isKindOfClass:[UIImage class]]) { + image = (UIImage *)input; + } else if ([input isKindOfClass:[TGCameraCapturedVideo class]]) { + AVAsset *asset = ((TGCameraCapturedVideo *)input).immediateAVAsset; + + AVAssetImageGenerator *generator = [[AVAssetImageGenerator alloc] initWithAsset:asset]; + generator.appliesPreferredTrackTransform = true; + generator.maximumSize = CGSizeMake(640.0f, 640.0f); + CGImageRef imageRef = [generator copyCGImageAtTime:kCMTimeZero actualTime:NULL error:NULL]; + image = [[UIImage alloc] initWithCGImage:imageRef]; + CGImageRelease(imageRef); + } id windowManager = nil; id windowContext = nil; @@ -1679,179 +1698,101 @@ static CGPoint TGCameraControllerClampPointToScreenSize(__unused id self, __unus _focusControl.ignoreAutofocusing = true; - switch (_intent) - { - case TGCameraControllerAvatarIntent: - case TGCameraControllerSignupAvatarIntent: - { - TGPhotoEditorControllerIntent intent = TGPhotoEditorControllerAvatarIntent; - if (_intent == TGCameraControllerSignupAvatarIntent) { - intent = TGPhotoEditorControllerSignupAvatarIntent; - } - TGPhotoEditorController *controller = [[TGPhotoEditorController alloc] initWithContext:windowContext item:image intent:(TGPhotoEditorControllerFromCameraIntent | intent) adjustments:nil caption:nil screenImage:image availableTabs:[TGPhotoEditorController defaultTabsForAvatarIntent] selectedTab:TGPhotoEditorCropTab]; - controller.stickersContext = _stickersContext; - __weak TGPhotoEditorController *weakController = controller; - controller.beginTransitionIn = ^UIView *(CGRect *referenceFrame, __unused UIView **parentView) - { - __strong TGCameraController *strongSelf = weakSelf; - if (strongSelf == nil) - return nil; - - strongSelf->_previewView.hidden = true; - *referenceFrame = strongSelf->_previewView.frame; - - UIImageView *imageView = [[UIImageView alloc] initWithFrame:strongSelf->_previewView.frame]; - imageView.image = image; - - return imageView; - }; - - controller.beginTransitionOut = ^UIView *(CGRect *referenceFrame, __unused UIView **parentView) - { - __strong TGCameraController *strongSelf = weakSelf; - if (strongSelf == nil) - return nil; - - CGRect startFrame = CGRectZero; - if (referenceFrame != NULL) - { - startFrame = *referenceFrame; - *referenceFrame = strongSelf->_previewView.frame; - } - - [strongSelf transitionBackFromResultControllerWithReferenceFrame:startFrame]; - - return strongSelf->_previewView; - }; - - controller.didFinishEditing = ^(PGPhotoEditorValues *editorValues, UIImage *resultImage, __unused UIImage *thumbnailImage, bool hasChanges) - { - if (!hasChanges) - return; - - __strong TGCameraController *strongSelf = weakSelf; - if (strongSelf == nil) - return; - - TGDispatchOnMainThread(^ - { - if (strongSelf.finishedWithPhoto != nil) - strongSelf.finishedWithPhoto(nil, resultImage, nil, nil, nil, nil); - - if (strongSelf.shouldStoreCapturedAssets) - { - [strongSelf _savePhotoToCameraRollWithOriginalImage:image editedImage:[editorValues toolsApplied] ? resultImage : nil]; - } - - __strong TGPhotoEditorController *strongController = weakController; - if (strongController != nil) - { - [strongController updateStatusBarAppearanceForDismiss]; - [strongSelf _dismissTransitionForResultController:(TGOverlayController *)strongController]; - } - }); - }; - - controller.requestThumbnailImage = ^(id editableItem) - { - return [editableItem thumbnailImageSignal]; - }; - - controller.requestOriginalScreenSizeImage = ^(id editableItem, NSTimeInterval position) - { - return [editableItem screenImageSignal:position]; - }; - - controller.requestOriginalFullSizeImage = ^(id editableItem, NSTimeInterval position) - { - if (editableItem.isVideo) { - if ([editableItem isKindOfClass:[TGMediaAsset class]]) { - return [TGMediaAssetImageSignals avAssetForVideoAsset:(TGMediaAsset *)editableItem]; - } else if ([editableItem isKindOfClass:[TGCameraCapturedVideo class]]) { - return ((TGCameraCapturedVideo *)editableItem).avAsset; - } else { - return [editableItem originalImageSignal:position]; - } - } else { - return [editableItem originalImageSignal:position]; - } - }; - - overlayController = (TGOverlayController *)controller; - } - break; - - default: - { - TGCameraPhotoPreviewController *controller = _shortcut ? [[TGCameraPhotoPreviewController alloc] initWithContext:windowContext image:image metadata:metadata recipientName:self.recipientName backButtonTitle:TGLocalized(@"Camera.Retake") doneButtonTitle:TGLocalized(@"Common.Next") saveCapturedMedia:_saveCapturedMedia saveEditedPhotos:_saveEditedPhotos] : [[TGCameraPhotoPreviewController alloc] initWithContext:windowContext image:image metadata:metadata recipientName:self.recipientName saveCapturedMedia:_saveCapturedMedia saveEditedPhotos:_saveEditedPhotos]; - controller.allowCaptions = self.allowCaptions; - controller.shouldStoreAssets = self.shouldStoreCapturedAssets; - controller.suggestionContext = self.suggestionContext; - controller.hasTimer = self.hasTimer; - controller.hasSilentPosting = self.hasSilentPosting; - controller.hasSchedule = self.hasSchedule; - - __weak TGCameraPhotoPreviewController *weakController = controller; - controller.beginTransitionIn = ^CGRect - { - __strong TGCameraController *strongSelf = weakSelf; - if (strongSelf == nil) - return CGRectZero; - - strongSelf->_previewView.hidden = true; - - return strongSelf->_previewView.frame; - }; - - controller.finishedTransitionIn = ^ - { - __strong TGCameraController *strongSelf = weakSelf; - if (strongSelf != nil) - [strongSelf->_camera stopCaptureForPause:true completion:nil]; - }; - - controller.beginTransitionOut = ^CGRect(CGRect referenceFrame) - { - __strong TGCameraController *strongSelf = weakSelf; - if (strongSelf == nil) - return CGRectZero; - - [strongSelf->_camera startCaptureForResume:true completion:nil]; - - return [strongSelf transitionBackFromResultControllerWithReferenceFrame:referenceFrame]; - }; - - controller.retakePressed = ^ - { - __strong TGCameraController *strongSelf = weakSelf; - if (strongSelf == nil) - return; - - [[[LegacyComponentsGlobals provider] applicationInstance] setIdleTimerDisabled:true]; - }; - - controller.sendPressed = ^(TGOverlayController *controller, UIImage *resultImage, NSString *caption, NSArray *entities, NSArray *stickers, NSNumber *timer) - { - __strong TGCameraController *strongSelf = weakSelf; - if (strongSelf == nil) - return; - - if (strongSelf.finishedWithPhoto != nil) - strongSelf.finishedWithPhoto(controller, resultImage, caption, entities, stickers, timer); - - if (strongSelf->_shortcut) - return; - - __strong TGOverlayController *strongController = weakController; - if (strongController != nil) - [strongSelf _dismissTransitionForResultController:strongController]; - }; - - overlayController = controller; - } - break; + TGPhotoEditorControllerIntent intent = TGPhotoEditorControllerAvatarIntent; + if (_intent == TGCameraControllerSignupAvatarIntent) { + intent = TGPhotoEditorControllerSignupAvatarIntent; } + TGPhotoEditorController *controller = [[TGPhotoEditorController alloc] initWithContext:windowContext item:input intent:(TGPhotoEditorControllerFromCameraIntent | intent) adjustments:nil caption:nil screenImage:image availableTabs:[TGPhotoEditorController defaultTabsForAvatarIntent] selectedTab:TGPhotoEditorCropTab]; + controller.stickersContext = _stickersContext; + __weak TGPhotoEditorController *weakController = controller; + controller.beginTransitionIn = ^UIView *(CGRect *referenceFrame, __unused UIView **parentView) + { + __strong TGCameraController *strongSelf = weakSelf; + if (strongSelf == nil) + return nil; + + strongSelf->_previewView.hidden = true; + *referenceFrame = strongSelf->_previewView.frame; + + UIImageView *imageView = [[UIImageView alloc] initWithFrame:strongSelf->_previewView.frame]; + imageView.image = image; + + return imageView; + }; + controller.beginTransitionOut = ^UIView *(CGRect *referenceFrame, __unused UIView **parentView) + { + __strong TGCameraController *strongSelf = weakSelf; + if (strongSelf == nil) + return nil; + + CGRect startFrame = CGRectZero; + if (referenceFrame != NULL) + { + startFrame = *referenceFrame; + *referenceFrame = strongSelf->_previewView.frame; + } + + [strongSelf transitionBackFromResultControllerWithReferenceFrame:startFrame]; + + return strongSelf->_previewView; + }; + + controller.didFinishEditing = ^(PGPhotoEditorValues *editorValues, UIImage *resultImage, __unused UIImage *thumbnailImage, bool hasChanges) + { + if (!hasChanges) + return; + + __strong TGCameraController *strongSelf = weakSelf; + if (strongSelf == nil) + return; + + TGDispatchOnMainThread(^ + { + if (strongSelf.finishedWithPhoto != nil) + strongSelf.finishedWithPhoto(nil, resultImage, nil, nil, nil, nil); + + if (strongSelf.shouldStoreCapturedAssets && [input isKindOfClass:[UIImage class]]) + { + [strongSelf _savePhotoToCameraRollWithOriginalImage:image editedImage:[editorValues toolsApplied] ? resultImage : nil]; + } + + __strong TGPhotoEditorController *strongController = weakController; + if (strongController != nil) + { + [strongController updateStatusBarAppearanceForDismiss]; + [strongSelf _dismissTransitionForResultController:(TGOverlayController *)strongController]; + } + }); + }; + + controller.requestThumbnailImage = ^(id editableItem) + { + return [editableItem thumbnailImageSignal]; + }; + + controller.requestOriginalScreenSizeImage = ^(id editableItem, NSTimeInterval position) + { + return [editableItem screenImageSignal:position]; + }; + + controller.requestOriginalFullSizeImage = ^(id editableItem, NSTimeInterval position) + { + if (editableItem.isVideo) { + if ([editableItem isKindOfClass:[TGMediaAsset class]]) { + return [TGMediaAssetImageSignals avAssetForVideoAsset:(TGMediaAsset *)editableItem allowNetworkAccess:true]; + } else if ([editableItem isKindOfClass:[TGCameraCapturedVideo class]]) { + return ((TGCameraCapturedVideo *)editableItem).avAsset; + } else { + return [editableItem originalImageSignal:position]; + } + } else { + return [editableItem originalImageSignal:position]; + } + }; + + overlayController = (TGOverlayController *)controller; + if (windowManager != nil) { TGOverlayController *contentController = overlayController; @@ -2375,6 +2316,7 @@ static CGPoint TGCameraControllerClampPointToScreenSize(__unused id self, __unus case PGCameraModeSquarePhoto: case PGCameraModeSquareVideo: + case PGCameraModeSquareSwing: { CGRect rect = [self _cameraPreviewFrameForScreenSize:screenSize mode:PGCameraModePhoto]; CGFloat topOffset = CGRectGetMidY(rect) - rect.size.width / 2; @@ -2406,7 +2348,7 @@ static CGPoint TGCameraControllerClampPointToScreenSize(__unused id self, __unus } else { - if (mode == PGCameraModeSquarePhoto || mode == PGCameraModeSquareVideo) + if (mode == PGCameraModeSquarePhoto || mode == PGCameraModeSquareVideo || mode == PGCameraModeSquareSwing) return CGRectMake(0, (screenSize.height - screenSize.width) / 2, screenSize.width, screenSize.width); return CGRectMake(0, 0, screenSize.width, screenSize.height); diff --git a/submodules/LegacyComponents/Sources/TGCameraMainView.m b/submodules/LegacyComponents/Sources/TGCameraMainView.m index 92febedde9..4254f41c16 100644 --- a/submodules/LegacyComponents/Sources/TGCameraMainView.m +++ b/submodules/LegacyComponents/Sources/TGCameraMainView.m @@ -50,17 +50,17 @@ break; case PGCameraModeVideo: + case PGCameraModeSquareVideo: { [_shutterButton setButtonMode:TGCameraShutterButtonVideoMode animated:true]; [_timecodeView setHidden:false animated:true]; } break; - case PGCameraModeSquareVideo: + case PGCameraModeSquareSwing: { [_shutterButton setButtonMode:TGCameraShutterButtonVideoMode animated:true]; [_timecodeView setHidden:true animated:true]; - } break; diff --git a/submodules/LegacyComponents/Sources/TGCameraModeControl.m b/submodules/LegacyComponents/Sources/TGCameraModeControl.m index c395da0599..f6744d8e1e 100644 --- a/submodules/LegacyComponents/Sources/TGCameraModeControl.m +++ b/submodules/LegacyComponents/Sources/TGCameraModeControl.m @@ -9,7 +9,6 @@ const CGFloat TGCameraModeControlVerticalInteritemSpace = 29.0f; @interface TGCameraModeControl () { - UIImageView *_dotView; UIControl *_wrapperView; CGFloat _kerning; @@ -27,24 +26,6 @@ const CGFloat TGCameraModeControlVerticalInteritemSpace = 29.0f; self = [super initWithFrame:frame]; if (self != nil) { - static UIImage *dotImage = nil; - static dispatch_once_t onceToken; - dispatch_once(&onceToken, ^ - { - UIGraphicsBeginImageContextWithOptions(CGSizeMake(6, 6), false, 0.0f); - CGContextRef context = UIGraphicsGetCurrentContext(); - - CGContextSetFillColorWithColor(context, [TGCameraInterfaceAssets accentColor].CGColor); - CGContextFillEllipseInRect(context, CGRectMake(0, 0, 6, 6)); - - dotImage = UIGraphicsGetImageFromCurrentImageContext(); - UIGraphicsEndImageContext(); - }); - - _dotView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 6, 6)]; - _dotView.image = dotImage; - //[self addSubview:_dotView]; - if (frame.size.width > frame.size.height) _kerning = 3.5f; else @@ -64,8 +45,9 @@ const CGFloat TGCameraModeControlVerticalInteritemSpace = 29.0f; _buttons = @ [ [self _createButtonForMode:PGCameraModeSquareVideo title:TGLocalized(@"Camera.VideoMode")], - [self _createButtonForMode:PGCameraModePhoto title:TGLocalized(@"Camera.PhotoMode")] - ]; + [self _createButtonForMode:PGCameraModePhoto title:TGLocalized(@"Camera.PhotoMode")], + [self _createButtonForMode:PGCameraModeSquareSwing title:@"SWING"] + ]; } else { _buttons = @ [ @@ -119,7 +101,6 @@ const CGFloat TGCameraModeControlVerticalInteritemSpace = 29.0f; + (CGFloat)_buttonHorizontalSpacing { - //return 22; return 19; } @@ -282,14 +263,7 @@ const CGFloat TGCameraModeControlVerticalInteritemSpace = 29.0f; - (void)layoutSubviews { if (self.frame.size.width > self.frame.size.height) - { - _dotView.frame = CGRectMake((self.frame.size.width - _dotView.frame.size.width) / 2, self.frame.size.height / 2 - 12, _dotView.frame.size.width, _dotView.frame.size.height); _maskLayer.frame = CGRectMake(0, 0, _maskView.frame.size.width, _maskView.frame.size.height); - } - else - { - _dotView.frame = CGRectMake(13, (self.frame.size.height - _dotView.frame.size.height) / 2, _dotView.frame.size.width, _dotView.frame.size.height); - } } @end diff --git a/submodules/LegacyComponents/Sources/TGCameraPhotoPreviewController.m b/submodules/LegacyComponents/Sources/TGCameraPhotoPreviewController.m deleted file mode 100644 index 087b786a57..0000000000 --- a/submodules/LegacyComponents/Sources/TGCameraPhotoPreviewController.m +++ /dev/null @@ -1,1203 +0,0 @@ -#import "TGCameraPhotoPreviewController.h" - -#import "LegacyComponentsInternal.h" - -#import - -#import -#import -#import -#import - -#import "TGImageView.h" -#import - -#import -#import "TGPhotoEditorController.h" -#import "TGPhotoEditorTabController.h" -#import "TGPhotoToolbarView.h" -#import "TGPhotoEditorButton.h" -#import - -#import "TGSecretTimerMenu.h" - -#import -#import -#import - -#import "TGPhotoEditorInterfaceAssets.h" -#import "TGPhotoCaptionInputMixin.h" - -@interface TGCameraPhotoPreviewWrapperView : UIView - -@end - -@implementation TGCameraPhotoPreviewWrapperView - -- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event -{ - UIView *view = [super hitTest:point withEvent:event]; - if (view != self) - return view; - - return nil; -} - -@end - -@interface TGCameraPhotoPreviewController () -{ - TGMediaEditingContext *_editingContext; - - UIImage *_image; - PGCameraShotMetadata *_metadata; - - TGCameraPhotoPreviewWrapperView *_wrapperView; - UIView *_transitionParentView; - TGModernGalleryZoomableScrollView *_scrollView; - TGImageView *_imageView; - UIView *_temporaryRepView; - CGSize _imageSize; - - UIImageView *_arrowView; - UILabel *_recipientLabel; - - TGPhotoToolbarView *_portraitToolbarView; - TGPhotoToolbarView *_landscapeToolbarView; - - bool _transitionInProgress; - bool _dismissing; - bool _appeared; - - NSString *_recipientName; - NSString *_backButtonTitle; - NSString *_doneButtonTitle; - - TGPhotoCaptionInputMixin *_captionMixin; - CGFloat _scrollViewVerticalOffset; - - bool _saveCapturedMedia; - bool _saveEditedPhotos; - - id _context; -} - -@property (nonatomic, weak) TGPhotoEditorController *editorController; - -@end - -@implementation TGCameraPhotoPreviewController - -- (instancetype)initWithContext:(id)context image:(UIImage *)image metadata:(PGCameraShotMetadata *)metadata recipientName:(NSString *)recipientName saveCapturedMedia:(bool)saveCapturedMedia saveEditedPhotos:(bool)saveEditedPhotos -{ - return [self initWithContext:context image:image metadata:metadata recipientName:recipientName backButtonTitle:TGLocalized(@"Camera.Retake") doneButtonTitle:TGLocalized(@"MediaPicker.Send") saveCapturedMedia:saveCapturedMedia saveEditedPhotos:saveEditedPhotos]; -} - -- (instancetype)initWithContext:(id)context image:(UIImage *)image metadata:(PGCameraShotMetadata *)metadata recipientName:(NSString *)recipientName backButtonTitle:(NSString *)backButtonTitle doneButtonTitle:(NSString *)doneButtonTitle saveCapturedMedia:(bool)saveCapturedMedia saveEditedPhotos:(bool)saveEditedPhotos -{ - self = [super initWithContext:context]; - if (self != nil) - { - _context = context; - _image = image; - _metadata = metadata; - _imageSize = image.size; - _recipientName = recipientName; - - _editingContext = [[TGMediaEditingContext alloc] init]; - - self.automaticallyManageScrollViewInsets = false; - - _backButtonTitle = backButtonTitle; - _doneButtonTitle = doneButtonTitle; - - _saveCapturedMedia = saveCapturedMedia; - _saveEditedPhotos = saveEditedPhotos; - } - return self; -} - -- (void)loadView -{ - [super loadView]; - object_setClass(self.view, [TGFullscreenContainerView class]); - - if (iosMajorVersion() >= 11) - self.view.accessibilityIgnoresInvertColors = true; - - self.view.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; - self.view.backgroundColor = [UIColor clearColor]; - - _transitionParentView = [[UIView alloc] initWithFrame:self.view.bounds]; - _transitionParentView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; - [self.view addSubview:_transitionParentView]; - - CGRect containerFrame = self.view.bounds; - CGSize fittedSize = TGScaleToSize(_image.size, containerFrame.size); - - _scrollView = [[TGModernGalleryZoomableScrollView alloc] initWithFrame:self.view.bounds hasDoubleTap:true]; - _scrollView.clipsToBounds = false; - _scrollView.delegate = self; - _scrollView.showsHorizontalScrollIndicator = false; - _scrollView.showsVerticalScrollIndicator = false; - [self.view addSubview:_scrollView]; - - _imageView = [[TGImageView alloc] initWithFrame:CGRectMake(0, 0, fittedSize.width, fittedSize.height)]; - [self.view addSubview:_imageView]; - - __weak TGCameraPhotoPreviewController *weakSelf = self; - void (^fadeOutRepView)(void) = ^ - { - __strong TGCameraPhotoPreviewController *strongSelf = weakSelf; - if (strongSelf == nil) - return; - - if (strongSelf->_temporaryRepView == nil) - return; - - UIView *repView = strongSelf->_temporaryRepView; - strongSelf->_temporaryRepView = nil; - [UIView animateWithDuration:0.2f animations:^ - { - repView.alpha = 0.0f; - } completion:^(__unused BOOL finished) - { - [repView removeFromSuperview]; - }]; - }; - - TGMediaEditingContext *editingContext = _editingContext; - - SSignal *assetSignal = [SSignal single:_image]; - SSignal *imageSignal = assetSignal; - if (editingContext != nil) - { - imageSignal = [[[editingContext imageSignalForItem:_image] deliverOn:[SQueue mainQueue]] mapToSignal:^SSignal *(id result) - { - __strong TGCameraPhotoPreviewController *strongSelf = weakSelf; - if (strongSelf == nil) - return [SSignal complete]; - - if (result == nil) - { - return [[assetSignal deliverOn:[SQueue mainQueue]] afterNext:^(__unused id next) - { - fadeOutRepView(); - }]; - } - else if ([result isKindOfClass:[UIView class]]) - { - [strongSelf _setTemporaryRepView:result]; - return [[SSignal single:nil] deliverOn:[SQueue mainQueue]]; - } - else - { - return [[[SSignal single:result] deliverOn:[SQueue mainQueue]] afterNext:^(__unused id next) - { - fadeOutRepView(); - }]; - } - }]; - } - - [_imageView setSignal:[[imageSignal deliverOn:[SQueue mainQueue]] afterNext:^(id next) - { - __strong TGCameraPhotoPreviewController *strongSelf = weakSelf; - if (strongSelf == nil) - return; - - if ([next isKindOfClass:[UIImage class]]) - strongSelf->_imageSize = ((UIImage *)next).size; - - [strongSelf reset]; - }]]; - - _wrapperView = [[TGCameraPhotoPreviewWrapperView alloc] initWithFrame:CGRectZero]; - [self.view addSubview:_wrapperView]; - - void (^cancelPressed)(void) = ^ - { - __strong TGCameraPhotoPreviewController *strongSelf = weakSelf; - if (strongSelf == nil) - return; - - if (strongSelf.retakePressed != nil) - strongSelf.retakePressed(); - - [strongSelf transitionOutWithCompletion:^ - { - [strongSelf dismiss]; - }]; - }; - - void (^donePressed)(void) = ^ - { - __strong TGCameraPhotoPreviewController *strongSelf = weakSelf; - if (strongSelf == nil || strongSelf->_dismissing) - return; - - [strongSelf.view.window endEditing:true]; - - strongSelf->_dismissing = true; - strongSelf.view.userInteractionEnabled = false; - - if (strongSelf.shouldStoreAssets && [strongSelf->_editingContext timerForItem:strongSelf->_image] == nil) - { - if (strongSelf->_saveCapturedMedia) - [[[TGMediaAssetsLibrary sharedLibrary] saveAssetWithImage:strongSelf->_image] startWithNext:nil]; - - if (strongSelf->_saveEditedPhotos) - { - [[[[[[editingContext fullSizeImageUrlForItem:strongSelf->_image] filter:^bool(id result) - { - return [result isKindOfClass:[NSURL class]]; - }] startOn:[SQueue concurrentDefaultQueue]] deliverOn:[SQueue mainQueue]] mapToSignal:^SSignal *(NSURL *url) - { - return [[[TGMediaAssetsLibrary sharedLibrary] saveAssetWithImageAtUrl:url] onCompletion:^ - { - __strong TGMediaEditingContext *strongEditingContext = editingContext; - [strongEditingContext description]; - }]; - }] startWithNext:nil]; - } - } - - SSignal *originalSignal = [[[SSignal single:strongSelf->_image] map:^id(UIImage *image) - { - return TGPhotoEditorCrop(image, nil, UIImageOrientationUp, 0, CGRectMake(0, 0, image.size.width, image.size.height), false, CGSizeMake(1280, 1280), image.size, true); - }] startOn:[SQueue concurrentDefaultQueue]]; - - SSignal *imageSignal = originalSignal; - if (editingContext != nil) - { - imageSignal = [[[[editingContext imageSignalForItem:strongSelf->_image withUpdates:true] filter:^bool(id result) - { - return result == nil || ([result isKindOfClass:[UIImage class]] && !((UIImage *)result).degraded); - }] take:1] mapToSignal:^SSignal *(id result) - { - if (result == nil) - { - return originalSignal; - } - else if ([result isKindOfClass:[UIImage class]]) - { - UIImage *image = (UIImage *)result; - image.edited = true; - return [SSignal single:image]; - } - - return [SSignal complete]; - }]; - } - - NSString *caption = [editingContext captionForItem:strongSelf->_image]; - NSArray *entities = [editingContext entitiesForItem:strongSelf->_image]; - NSArray *stickers = [editingContext adjustmentsForItem:strongSelf->_image].paintingData.stickers; - NSNumber *timer = [editingContext timerForItem:strongSelf->_image]; - [[imageSignal deliverOn:[SQueue mainQueue]] startWithNext:^(UIImage *result) - { - strongSelf.sendPressed(self, result, caption, entities, stickers, timer); - strongSelf.view.userInteractionEnabled = true; - strongSelf->_dismissing = false; - }]; - }; - - void (^tabPressed)(TGPhotoEditorTab) = ^(TGPhotoEditorTab tab) - { - __strong TGCameraPhotoPreviewController *strongSelf = weakSelf; - if (strongSelf == nil) - return; - - if (tab == TGPhotoEditorTimerTab) - [strongSelf openTimerSetup]; - else - [strongSelf presentPhotoEditorWithTab:tab]; - }; - - TGPhotoEditorTab tabs = TGPhotoEditorCropTab; - if (iosMajorVersion() >= 7) - { - tabs |= TGPhotoEditorPaintTab; - tabs |= TGPhotoEditorToolsTab; - } - - if (self.hasTimer) - tabs |= TGPhotoEditorTimerTab; - - _captionMixin = [[TGPhotoCaptionInputMixin alloc] initWithKeyCommandController:[_context keyCommandController]]; - _captionMixin.panelParentView = ^UIView * - { - __strong TGCameraPhotoPreviewController *strongSelf = weakSelf; - return strongSelf->_wrapperView; - }; - - _captionMixin.panelFocused = ^ - { - __strong TGCameraPhotoPreviewController *strongSelf = weakSelf; - if (strongSelf == nil) - return; - - [strongSelf setInterfaceHidden:true animated:true]; - }; - - _captionMixin.finishedWithCaption = ^(NSString *caption, NSArray *entities) - { - __strong TGCameraPhotoPreviewController *strongSelf = weakSelf; - if (strongSelf == nil) - return; - - [strongSelf->_editingContext setCaption:caption entities:entities forItem:strongSelf->_image]; - - PGPhotoEditorValues *values = (PGPhotoEditorValues *)[strongSelf->_editingContext adjustmentsForItem:strongSelf->_image]; - [strongSelf updateEditorButtonsForEditorValues:values]; - - [strongSelf setInterfaceHidden:false animated:true]; - }; - - _captionMixin.keyboardHeightChanged = ^(CGFloat keyboardHeight, NSTimeInterval duration, NSInteger animationCurve) - { - __strong TGCameraPhotoPreviewController *strongSelf = weakSelf; - if (strongSelf == nil) - return; - - CGFloat offset = 0.0f; - if (keyboardHeight > 0) - offset = -keyboardHeight / 2.0f; - - [UIView animateWithDuration:duration delay:0.0f options:animationCurve animations:^ - { - [strongSelf setScrollViewVerticalOffset:offset]; - } completion:nil]; - }; - _captionMixin.suggestionContext = self.suggestionContext; - [_captionMixin createInputPanelIfNeeded]; - - _portraitToolbarView = [[TGPhotoToolbarView alloc] initWithBackButton:TGPhotoEditorBackButtonBack doneButton:TGPhotoEditorDoneButtonSend solidBackground:false]; - [_portraitToolbarView setToolbarTabs:tabs animated:false]; - _portraitToolbarView.cancelPressed = cancelPressed; - _portraitToolbarView.donePressed = donePressed; - _portraitToolbarView.tabPressed = tabPressed; - [_wrapperView addSubview:_portraitToolbarView]; - - _landscapeToolbarView = [[TGPhotoToolbarView alloc] initWithBackButton:TGPhotoEditorBackButtonBack doneButton:TGPhotoEditorDoneButtonSend solidBackground:false]; - [_landscapeToolbarView setToolbarTabs:tabs animated:false]; - _landscapeToolbarView.cancelPressed = cancelPressed; - _landscapeToolbarView.donePressed = donePressed; - _landscapeToolbarView.tabPressed = tabPressed; - [_wrapperView addSubview:_landscapeToolbarView]; - - if (_recipientName.length > 0) - { - _arrowView = [[UIImageView alloc] initWithImage:TGComponentsImageNamed(@"PhotoPickerArrow")]; - _arrowView.alpha = 0.45f; - [_wrapperView addSubview:_arrowView]; - - _recipientLabel = [[UILabel alloc] init]; - _recipientLabel.backgroundColor = [UIColor clearColor]; - _recipientLabel.font = TGBoldSystemFontOfSize(13.0f); - _recipientLabel.textColor = UIColorRGBA(0xffffff, 0.45f); - _recipientLabel.text = _recipientName; - _recipientLabel.userInteractionEnabled = false; - [_recipientLabel sizeToFit]; - [_wrapperView addSubview:_recipientLabel]; - } -} - -- (void)openTimerSetup -{ - id editableMediaItem = _image; - - NSString *description = TGLocalized(@"SecretTimer.ImageDescription"); - - NSString *lastValueKey = @"mediaPickerLastTimerValue_v0"; - NSNumber *value = [_editingContext timerForItem:editableMediaItem]; - if (value == nil) - value = [[NSUserDefaults standardUserDefaults] objectForKey:lastValueKey]; - - __strong TGCameraPhotoPreviewController *weakSelf = self; - [TGSecretTimerMenu presentInParentController:self context:_context dark:true description:description values:[TGSecretTimerMenu secretMediaTimerValues] value:value completed:^(NSNumber *value) - { - __strong TGCameraPhotoPreviewController *strongSelf = weakSelf; - if (strongSelf != nil) - { - if (value == nil) - [[NSUserDefaults standardUserDefaults] removeObjectForKey:lastValueKey]; - else - [[NSUserDefaults standardUserDefaults] setObject:value forKey:lastValueKey]; - - [strongSelf->_editingContext setTimer:value forItem:editableMediaItem]; - - PGPhotoEditorValues *values = (PGPhotoEditorValues *)[strongSelf->_editingContext adjustmentsForItem:strongSelf->_image]; - [strongSelf updateEditorButtonsForEditorValues:values]; - } - } dismissed:^ - { - __strong TGCameraPhotoPreviewController *strongSelf = weakSelf; - if (strongSelf != nil) - [strongSelf setAllInterfaceHidden:false animated:true]; - } sourceView:self.view sourceRect:^CGRect - { - __strong TGCameraPhotoPreviewController *strongSelf = weakSelf; - if (strongSelf == nil) - return CGRectZero; - - return [[strongSelf timerButton] convertRect:[strongSelf timerButton].bounds toView:strongSelf.view]; - }]; - - if (!TGIsPad()) - [self setAllInterfaceHidden:true animated:true]; -} - -- (UIView *)timerButton -{ - if (UIInterfaceOrientationIsPortrait(self.interfaceOrientation)) - return [_portraitToolbarView buttonForTab:TGPhotoEditorTimerTab]; - else - return [_landscapeToolbarView buttonForTab:TGPhotoEditorTimerTab]; -} - -- (void)_setTemporaryRepView:(UIView *)view -{ - [_temporaryRepView removeFromSuperview]; - _temporaryRepView = view; - - _imageSize = TGScaleToSize(view.frame.size, self.view.frame.size); - - view.hidden = _imageView.hidden; - view.frame = CGRectMake((self.view.frame.size.width - _imageSize.width) / 2.0f, (self.view.frame.size.height - _imageSize.height) / 2.0f, _imageSize.width, _imageSize.height); - - [self.view insertSubview:view belowSubview:_wrapperView]; -} - -- (void)setInterfaceHidden:(bool)hidden animated:(bool)animated -{ - CGFloat alpha = (hidden ? 0.0f : 1.0f); - if (animated) - { - [UIView animateWithDuration:0.2 delay:0.0 options:UIViewAnimationOptionCurveLinear | UIViewAnimationOptionBeginFromCurrentState animations:^ - { - _arrowView.alpha = alpha * 0.45f; - _recipientLabel.alpha = alpha; - } completion:nil]; - } - else - { - _arrowView.alpha = alpha * 0.45f; - _recipientLabel.alpha = alpha; - } -} - -- (void)setAllInterfaceHidden:(bool)hidden animated:(bool)animated -{ - CGFloat alpha = (hidden ? 0.0f : 1.0f); - if (animated) - { - [UIView animateWithDuration:0.2 delay:0.0 options:UIViewAnimationOptionCurveLinear | UIViewAnimationOptionBeginFromCurrentState animations:^ - { - _arrowView.alpha = alpha * 0.45f; - _recipientLabel.alpha = alpha; - _portraitToolbarView.alpha = alpha; - _landscapeToolbarView.alpha = alpha; - _captionMixin.inputPanel.alpha = alpha; - } completion:^(BOOL finished) - { - if (finished) - { - _portraitToolbarView.userInteractionEnabled = !hidden; - _landscapeToolbarView.userInteractionEnabled = !hidden; - _captionMixin.inputPanel.userInteractionEnabled = !hidden; - } - }]; - } - else - { - _arrowView.alpha = alpha * 0.45f; - _recipientLabel.alpha = alpha; - - _portraitToolbarView.alpha = alpha; - _portraitToolbarView.userInteractionEnabled = !hidden; - - _landscapeToolbarView.alpha = alpha; - _landscapeToolbarView.userInteractionEnabled = !hidden; - - _captionMixin.inputPanel.alpha = alpha; - _captionMixin.inputPanel.userInteractionEnabled = !hidden; - } -} - -- (void)dismiss -{ - if (self.navigationController != nil) - { - TGOverlayController *parentController = (TGOverlayController *)self.navigationController.parentViewController; - [parentController dismiss]; - } - else if (self.overlayWindow != nil) - { - [super dismiss]; - } - else - { - [self.view removeFromSuperview]; - [self removeFromParentViewController]; - } -} - -- (UIRectEdge)preferredScreenEdgesDeferringSystemGestures -{ - if (self.childViewControllers.count > 0) - return [self.childViewControllers.lastObject preferredScreenEdgesDeferringSystemGestures]; - - return [super preferredScreenEdgesDeferringSystemGestures]; -} - -- (BOOL)prefersStatusBarHidden -{ - if (self.childViewControllers.count > 0) - return [self.childViewControllers.lastObject prefersStatusBarHidden]; - - return [super prefersStatusBarHidden]; -} - -- (UIBarStyle)requiredNavigationBarStyle -{ - return UIBarStyleDefault; -} - -- (bool)navigationBarShouldBeHidden -{ - return true; -} - -- (void)viewWillAppear:(BOOL)animated -{ - [super viewWillAppear:animated]; - - [self transitionIn]; -} - -#pragma mark - Transition - -- (void)transitionIn -{ - [_context setApplicationStatusBarAlpha:0.0f]; - - if (_appeared) - return; - - _appeared = true; - _transitionInProgress = true; - - _captionMixin.inputPanel.alpha = 0.0f; - _portraitToolbarView.alpha = 0.0f; - _landscapeToolbarView.alpha = 0.0f; - _arrowView.alpha = 0.0f; - _recipientLabel.alpha = 0.0f; - - [UIView animateWithDuration:0.3f delay:0.1f options:UIViewAnimationOptionCurveLinear animations:^ - { - _captionMixin.inputPanel.alpha = 1.0f; - _portraitToolbarView.alpha = 1.0f; - _landscapeToolbarView.alpha = 1.0f; - _arrowView.alpha = 0.45f; - _recipientLabel.alpha = 1.0f; - } completion:nil]; - - CGSize referenceSize = [self referenceViewSizeForOrientation:self.interfaceOrientation]; - CGRect referenceFrame = CGRectZero; - if (self.beginTransitionIn != nil) - referenceFrame = self.beginTransitionIn(); - - if (self.interfaceOrientation == UIInterfaceOrientationLandscapeLeft) - { - referenceFrame = CGRectMake(referenceSize.width - referenceFrame.size.height - referenceFrame.origin.y, - referenceFrame.origin.x, - referenceFrame.size.height, referenceFrame.size.width); - } - else if (self.interfaceOrientation == UIInterfaceOrientationLandscapeRight) - { - referenceFrame = CGRectMake(referenceFrame.origin.y, - referenceSize.height - referenceFrame.size.width - referenceFrame.origin.x, - referenceFrame.size.height, referenceFrame.size.width); - } - - CGRect containerFrame = CGRectMake(0, 0, referenceSize.width, referenceSize.height); - CGSize fittedSize = TGScaleToSize(_imageView.image.size, containerFrame.size); - CGRect targetFrame = CGRectMake(containerFrame.origin.x + (containerFrame.size.width - fittedSize.width) / 2, - containerFrame.origin.y + (containerFrame.size.height - fittedSize.height) / 2, - fittedSize.width, - fittedSize.height); - - CGFloat referenceAspectRatio = referenceFrame.size.width / referenceFrame.size.height; - CGFloat targetAspectRatio = targetFrame.size.width / targetFrame.size.height; - - if (ABS(targetAspectRatio - referenceAspectRatio) > 0.03f) - { - CGSize newSize = CGSizeZero; - if (referenceFrame.size.width > referenceFrame.size.height) - newSize = CGSizeMake(referenceFrame.size.width, _imageView.image.size.height * referenceFrame.size.width / _imageView.image.size.width); - else - newSize = CGSizeMake(_imageView.image.size.width * referenceFrame.size.height / _imageView.image.size.height, referenceFrame.size.height); - - referenceFrame = CGRectMake(CGRectGetMidX(referenceFrame) - newSize.width / 2, - CGRectGetMidY(referenceFrame) - newSize.height / 2, - newSize.width, newSize.height); - } - - _imageView.frame = referenceFrame; - - POPSpringAnimation *animation = [TGPhotoEditorAnimation prepareTransitionAnimationForPropertyNamed:kPOPViewFrame]; - animation.fromValue = [NSValue valueWithCGRect:referenceFrame]; - animation.toValue = [NSValue valueWithCGRect:targetFrame]; - animation.completionBlock = ^(__unused POPAnimation *animation, __unused BOOL finished) - { - _transitionInProgress = false; - [_scrollView addSubview:_imageView]; - _imageView.frame = CGRectMake(0, 0, _scrollView.frame.size.width, _scrollView.frame.size.height); - self.view.backgroundColor = [UIColor blackColor]; - - [self reset]; - - if (self.finishedTransitionIn != nil) - self.finishedTransitionIn(); - }; - - [_imageView pop_addAnimation:animation forKey:@"frame"]; -} - -- (void)transitionOutWithCompletion:(void (^)(void))completion -{ - _transitionInProgress = true; - - self.view.backgroundColor = [UIColor clearColor]; - - CGRect frame = [self.view convertRect:_imageView.frame fromView:_scrollView]; - [self.view addSubview:_imageView]; - _imageView.frame = frame; - - CGSize referenceSize = [self referenceViewSizeForOrientation:self.interfaceOrientation]; - CGRect referenceFrame = _imageView.frame; - - if (self.interfaceOrientation == UIInterfaceOrientationLandscapeLeft) - { - referenceFrame = CGRectMake(referenceSize.height - referenceFrame.size.height - referenceFrame.origin.y, - referenceFrame.origin.x, - referenceFrame.size.height, referenceFrame.size.width); - } - else if (self.interfaceOrientation == UIInterfaceOrientationLandscapeRight) - { - referenceFrame = CGRectMake(referenceFrame.origin.y, - referenceSize.width - referenceFrame.size.width - referenceFrame.origin.x, - referenceFrame.size.height, referenceFrame.size.width); - } - - CGRect targetFrame = CGRectZero; - if (self.beginTransitionOut != nil) - targetFrame = self.beginTransitionOut(referenceFrame); - - if (self.interfaceOrientation == UIInterfaceOrientationLandscapeLeft) - { - targetFrame = CGRectMake(referenceSize.width - targetFrame.size.height - targetFrame.origin.y, - targetFrame.origin.x, - targetFrame.size.height, targetFrame.size.width); - } - else if (self.interfaceOrientation == UIInterfaceOrientationLandscapeRight) - { - targetFrame = CGRectMake(targetFrame.origin.y, - referenceSize.height - targetFrame.size.width - targetFrame.origin.x, - targetFrame.size.height, targetFrame.size.width); - } - - CGFloat referenceAspectRatio = referenceFrame.size.width / referenceFrame.size.height; - CGFloat targetAspectRatio = targetFrame.size.width / targetFrame.size.height; - - if (ABS(targetAspectRatio - referenceAspectRatio) > 0.03f) - { - CGSize newSize = CGSizeZero; - if (targetFrame.size.width > targetFrame.size.height) - newSize = CGSizeMake(targetFrame.size.width, _imageView.image.size.height * targetFrame.size.width / _imageView.image.size.width); - else - newSize = CGSizeMake(_imageView.image.size.width * targetFrame.size.height / _imageView.image.size.height, targetFrame.size.height); - - targetFrame = CGRectMake(CGRectGetMidX(targetFrame) - newSize.width / 2, - CGRectGetMidY(targetFrame) - newSize.height / 2, - newSize.width, newSize.height); - } - - POPSpringAnimation *animation = [TGPhotoEditorAnimation prepareTransitionAnimationForPropertyNamed:kPOPViewFrame]; - animation.fromValue = [NSValue valueWithCGRect:_imageView.frame]; - animation.toValue = [NSValue valueWithCGRect:targetFrame]; - [_imageView pop_addAnimation:animation forKey:@"frame"]; - - [UIView animateWithDuration:0.3f animations:^ - { - _imageView.alpha = 0.0f; - _portraitToolbarView.alpha = 0.0f; - _landscapeToolbarView.alpha = 0.0f; - _captionMixin.inputPanel.alpha = 0.0f; - _arrowView.alpha = 0.0f; - _recipientLabel.alpha = 0.0f; - } completion:^(__unused BOOL finished) - { - if (completion != nil) - completion(); - }]; -} - -#pragma mark - Scroll View - -- (void)scrollViewDidZoom:(UIScrollView *)__unused scrollView -{ - [self adjustZoom]; -} - -- (void)scrollViewDidEndZooming:(UIScrollView *)__unused scrollView withView:(UIView *)__unused view atScale:(CGFloat)__unused scale -{ - [self adjustZoom]; - - if (_scrollView.zoomScale < _scrollView.normalZoomScale - FLT_EPSILON) - { - [TGHacks setAnimationDurationFactor:0.5f]; - [_scrollView setZoomScale:_scrollView.normalZoomScale animated:true]; - [TGHacks setAnimationDurationFactor:1.0f]; - } -} - -- (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView -{ - if (_imageView.superview == scrollView) - return _imageView; - - return nil; -} - -- (CGSize)contentSize -{ - return _imageSize; -} - -- (void)reset -{ - CGSize contentSize = [self contentSize]; - - _scrollView.minimumZoomScale = 1.0f; - _scrollView.maximumZoomScale = 1.0f; - _scrollView.normalZoomScale = 1.0f; - _scrollView.zoomScale = 1.0f; - _scrollView.contentSize = contentSize; - _imageView.frame = CGRectMake(0.0f, 0.0f, contentSize.width, contentSize.height); - - [self adjustZoom]; - _scrollView.zoomScale = _scrollView.normalZoomScale; -} - -- (void)adjustZoom -{ - CGSize contentSize = [self contentSize]; - CGSize boundsSize = _scrollView.frame.size; - if (contentSize.width < FLT_EPSILON || contentSize.height < FLT_EPSILON || boundsSize.width < FLT_EPSILON || boundsSize.height < FLT_EPSILON) - return; - - CGFloat scaleWidth = boundsSize.width / contentSize.width; - CGFloat scaleHeight = boundsSize.height / contentSize.height; - CGFloat minScale = MIN(scaleWidth, scaleHeight); - CGFloat maxScale = MAX(scaleWidth, scaleHeight); - maxScale = MAX(maxScale, minScale * 3.0f); - - if (ABS(maxScale - minScale) < 0.01f) - maxScale = minScale; - - if (_scrollView.minimumZoomScale != 0.05f) - _scrollView.minimumZoomScale = 0.05f; - if (_scrollView.normalZoomScale != minScale) - _scrollView.normalZoomScale = minScale; - if (_scrollView.maximumZoomScale != maxScale) - _scrollView.maximumZoomScale = maxScale; - - CGRect contentFrame = _imageView.frame; - - if (boundsSize.width > contentFrame.size.width) - contentFrame.origin.x = (boundsSize.width - contentFrame.size.width) / 2.0f; - else - contentFrame.origin.x = 0; - - if (boundsSize.height > contentFrame.size.height) - contentFrame.origin.y = (boundsSize.height - contentFrame.size.height) / 2.0f; - else - contentFrame.origin.y = 0; - - _imageView.frame = contentFrame; -} - -#pragma mark - - -- (void)updateEditorButtonsForEditorValues:(PGPhotoEditorValues *)editorValues -{ - TGPhotoEditorTab highlightedButtons = [TGPhotoEditorTabController highlightedButtonsForEditorValues:editorValues forAvatar:false]; - - TGPhotoEditorButton *timerButton = [_portraitToolbarView buttonForTab:TGPhotoEditorTimerTab]; - if (timerButton != nil) - { - NSInteger value = [[_editingContext timerForItem:_image] integerValue]; - - UIImage *defaultIcon = [TGPhotoEditorInterfaceAssets timerIconForValue:0]; - UIImage *icon = [TGPhotoEditorInterfaceAssets timerIconForValue:value]; - [timerButton setIconImage:defaultIcon activeIconImage:icon]; - - timerButton = [_landscapeToolbarView buttonForTab:TGPhotoEditorTimerTab]; - [timerButton setIconImage:defaultIcon activeIconImage:icon]; - - if (value > 0) - highlightedButtons |= TGPhotoEditorTimerTab; - } - - [_portraitToolbarView setEditButtonsHighlighted:highlightedButtons]; - [_landscapeToolbarView setEditButtonsHighlighted:highlightedButtons]; -} - -- (UIView *)transitionContentView -{ - if (_temporaryRepView != nil) - return _temporaryRepView; - - return _imageView; -} - -- (CGRect)transitionViewContentRect -{ - UIView *contentView = [self transitionContentView]; - return [self.view convertRect:contentView.frame fromView:contentView.superview]; -} - -- (void)presentPhotoEditorWithTab:(TGPhotoEditorTab)tab -{ - __weak TGCameraPhotoPreviewController *weakSelf = self; - - id editableMediaItem = _image; - - UIView *referenceView = [self transitionContentView]; - CGRect refFrame = [self transitionViewContentRect]; - UIImage *screenImage = nil; - if ([referenceView isKindOfClass:[UIImageView class]]) - screenImage = [(UIImageView *)referenceView image]; - - PGPhotoEditorValues *editorValues = (PGPhotoEditorValues *)[_editingContext adjustmentsForItem:_image]; - NSString *caption = [_editingContext captionForItem:_image]; - - TGPhotoEditorController *controller = [[TGPhotoEditorController alloc] initWithContext:_context item:editableMediaItem intent:TGPhotoEditorControllerFromCameraIntent adjustments:editorValues caption:caption screenImage:screenImage availableTabs:_portraitToolbarView.currentTabs selectedTab:tab]; - controller.editingContext = _editingContext; - self.editorController = controller; - controller.metadata = _metadata; - controller.suggestionContext = self.suggestionContext; - controller.didFinishRenderingFullSizeImage = ^(UIImage *image) - { - __strong TGCameraPhotoPreviewController *strongSelf = weakSelf; - if (strongSelf == nil) - return; - - [strongSelf->_editingContext setFullSizeImage:image forItem:strongSelf->_image]; - }; - controller.willFinishEditing = ^(PGPhotoEditorValues *editorValues, id temporaryRep, bool hasChanges) - { - __strong TGCameraPhotoPreviewController *strongSelf = weakSelf; - if (strongSelf == nil) - return; - - if (hasChanges) - { - [strongSelf->_editingContext setAdjustments:editorValues forItem:strongSelf->_image]; - [strongSelf->_editingContext setTemporaryRep:temporaryRep forItem:strongSelf->_image]; - } - }; - controller.didFinishEditing = ^(PGPhotoEditorValues *editorValues, UIImage *resultImage, __unused UIImage *thumbnailImage, bool hasChanges) - { -#ifdef DEBUG - if (editorValues != nil && hasChanges) - NSAssert(resultImage != nil, @"resultImage should not be nil"); -#endif - - __strong TGCameraPhotoPreviewController *strongSelf = weakSelf; - if (strongSelf == nil) - return; - - if (hasChanges) - [strongSelf->_editingContext setImage:resultImage thumbnailImage:nil forItem:strongSelf->_image synchronous:false]; - - PGPhotoEditorValues *values = !hasChanges ? (PGPhotoEditorValues *)[strongSelf->_editingContext adjustmentsForItem:strongSelf->_image] : editorValues; - [strongSelf updateEditorButtonsForEditorValues:values]; - - [strongSelf reset]; - }; - - controller.captionSet = ^(NSString *caption, NSArray *entities) - { - __strong TGCameraPhotoPreviewController *strongSelf = weakSelf; - if (strongSelf == nil) - return; - - [strongSelf reset]; - - [strongSelf->_editingContext setCaption:caption entities:entities forItem:strongSelf->_image]; - }; - - controller.requestToolbarsHidden = ^(bool hidden, bool animated) - { - __strong TGCameraPhotoPreviewController *strongSelf = weakSelf; - if (strongSelf == nil) - return; - - [strongSelf setToolbarsHidden:hidden animated:animated]; - }; - - controller.beginTransitionIn = ^UIView *(CGRect *referenceFrame, UIView **parentView) - { - __strong TGCameraPhotoPreviewController *strongSelf = weakSelf; - if (strongSelf == nil) - return nil; - - [strongSelf editorTransitionIn]; - - if (strongSelf.photoEditorShown != nil) - strongSelf.photoEditorShown(); - - strongSelf->_imageView.hidden = true; - strongSelf->_temporaryRepView.hidden = true; - - *parentView = strongSelf->_transitionParentView; - *referenceFrame = refFrame; - - [strongSelf reset]; - - if (iosMajorVersion() >= 7) - [strongSelf setNeedsStatusBarAppearanceUpdate]; - else { - [_context setStatusBarHidden:true withAnimation:UIStatusBarAnimationNone]; - } - - return referenceView; - }; - - controller.beginTransitionOut = ^UIView *(CGRect *referenceFrame, UIView **parentView) - { - __strong TGCameraPhotoPreviewController *strongSelf = weakSelf; - if (strongSelf == nil) - return nil; - - [strongSelf editorTransitionOut]; - - *parentView = strongSelf->_transitionParentView; - *referenceFrame = [strongSelf transitionViewContentRect]; - - return [strongSelf transitionContentView]; - }; - - controller.finishedTransitionOut = ^(__unused bool saved) - { - __strong TGCameraPhotoPreviewController *strongSelf = weakSelf; - if (strongSelf == nil) - return; - - if (strongSelf.photoEditorHidden != nil) - strongSelf.photoEditorHidden(); - - strongSelf->_imageView.hidden = false; - strongSelf->_temporaryRepView.hidden = false; - - if (iosMajorVersion() >= 7) - [strongSelf setNeedsStatusBarAppearanceUpdate]; - else { - [_context setStatusBarHidden:false withAnimation:UIStatusBarAnimationNone]; - } - }; - - controller.requestThumbnailImage = ^(id editableItem) - { - return [editableItem thumbnailImageSignal]; - }; - - controller.requestOriginalScreenSizeImage = ^(id editableItem, NSTimeInterval position) - { - return [editableItem screenImageSignal:position]; - }; - - controller.requestOriginalFullSizeImage = ^(id editableItem, NSTimeInterval position) - { - if (editableItem.isVideo) { - if ([editableItem isKindOfClass:[TGMediaAsset class]]) { - return [TGMediaAssetImageSignals avAssetForVideoAsset:(TGMediaAsset *)editableItem]; - } else if ([editableItem isKindOfClass:[TGCameraCapturedVideo class]]) { - return ((TGCameraCapturedVideo *)editableItem).avAsset; - } else { - return [editableItem originalImageSignal:position]; - } - } else { - return [editableItem originalImageSignal:position]; - } - }; - - [self addChildViewController:controller]; - [self.view addSubview:controller.view]; - controller.view.clipsToBounds = true; -} - -- (void)setToolbarsHidden:(bool)hidden animated:(bool)animated -{ - if (hidden) - { - [_portraitToolbarView transitionOutAnimated:animated transparent:true hideOnCompletion:false]; - [_landscapeToolbarView transitionOutAnimated:animated transparent:true hideOnCompletion:false]; - } - else - { - [_portraitToolbarView transitionInAnimated:animated transparent:true]; - [_landscapeToolbarView transitionInAnimated:animated transparent:true]; - } -} - -- (void)editorTransitionIn -{ - [UIView animateWithDuration:0.2 animations:^ - { - _arrowView.alpha = 0.0f; - _recipientLabel.alpha = 0.0f; - _captionMixin.inputPanel.alpha = 0.0f; - }]; -} - -- (void)editorTransitionOut -{ - [UIView animateWithDuration:0.3 animations:^ - { - _arrowView.alpha = 0.45f; - _recipientLabel.alpha = 1.0f; - _captionMixin.inputPanel.alpha = 1.0f; - }]; -} - -- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration -{ - [super willRotateToInterfaceOrientation:toInterfaceOrientation duration:duration]; - - [_imageView pop_removeAllAnimations]; -} - -- (void)viewWillLayoutSubviews -{ - [super viewWillLayoutSubviews]; - - [self updateLayout:[[LegacyComponentsGlobals provider] applicationStatusBarOrientation]]; -} - -- (void)setScrollViewVerticalOffset:(CGFloat)offset -{ - _scrollViewVerticalOffset = offset; - - CGRect scrollViewFrame = _scrollView.frame; - scrollViewFrame.origin.y = offset; - _scrollView.frame = scrollViewFrame; -} - -- (void)_layoutRecipientLabelForOrientation:(UIInterfaceOrientation)orientation screenEdges:(UIEdgeInsets)screenEdges -{ - CGFloat screenWidth = MIN(self.view.frame.size.width, self.view.frame.size.height); - CGFloat recipientWidth = MIN(_recipientLabel.frame.size.width, screenWidth - 100.0f); - - if (self.controllerSafeAreaInset.top > 20.0f + FLT_EPSILON) - screenEdges.top += self.controllerSafeAreaInset.top; - screenEdges.left += self.controllerSafeAreaInset.left; - screenEdges.right -= self.controllerSafeAreaInset.right; - - CGRect frame = CGRectZero; - switch (orientation) - { - case UIInterfaceOrientationLandscapeLeft: - frame = CGRectMake(screenEdges.right - recipientWidth - 28.0f, screenEdges.bottom - 24, _arrowView.frame.size.width, _arrowView.frame.size.height); - break; - - case UIInterfaceOrientationLandscapeRight: - frame = CGRectMake(screenEdges.left + 14, screenEdges.bottom - 24, _arrowView.frame.size.width, _arrowView.frame.size.height); - break; - - default: - frame = CGRectMake(screenEdges.left + 14, screenEdges.top + 16, _arrowView.frame.size.width, _arrowView.frame.size.height); - break; - } - - _arrowView.frame = frame; - _recipientLabel.frame = CGRectMake(CGRectGetMaxX(_arrowView.frame) + 6.0f, _arrowView.frame.origin.y - 2.0f, recipientWidth, _recipientLabel.frame.size.height); -} - -- (void)updateLayout:(UIInterfaceOrientation)orientation -{ - UIInterfaceOrientation originalOrientation = orientation; - if ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPad) - { - _landscapeToolbarView.hidden = true; - orientation = UIInterfaceOrientationPortrait; - } - - bool hasOnScreenNavigation = false; - if (iosMajorVersion() >= 11) - hasOnScreenNavigation = (self.viewLoaded && self.view.safeAreaInsets.bottom > FLT_EPSILON) || _context.safeAreaInset.bottom > FLT_EPSILON; - - CGSize referenceSize = [self referenceViewSizeForOrientation:originalOrientation]; - UIEdgeInsets safeAreaInset = [TGViewController safeAreaInsetForOrientation:orientation hasOnScreenNavigation:hasOnScreenNavigation]; - - [_captionMixin setContentAreaHeight:self.view.frame.size.height]; - - CGFloat screenSide = MAX(referenceSize.width, referenceSize.height); - _wrapperView.frame = CGRectMake((referenceSize.width - screenSide) / 2, (referenceSize.height - screenSide) / 2, screenSide, screenSide); - - UIEdgeInsets screenEdges = UIEdgeInsetsMake((screenSide - referenceSize.height) / 2, (screenSide - referenceSize.width) / 2, (screenSide + referenceSize.height) / 2, (screenSide + referenceSize.width) / 2); - - _landscapeToolbarView.interfaceOrientation = orientation; - - CGFloat portraitToolbarViewBottomEdge = screenSide; - if (TGIsPad()) - portraitToolbarViewBottomEdge = screenEdges.bottom; - _portraitToolbarView.frame = CGRectMake(screenEdges.left, portraitToolbarViewBottomEdge - TGPhotoEditorToolbarSize - safeAreaInset.bottom, referenceSize.width, TGPhotoEditorToolbarSize + safeAreaInset.bottom); - - UIEdgeInsets captionEdgeInsets = screenEdges; - captionEdgeInsets.bottom = _portraitToolbarView.frame.size.height; - [_captionMixin updateLayoutWithFrame:self.view.bounds edgeInsets:captionEdgeInsets]; - - switch (orientation) - { - case UIInterfaceOrientationLandscapeLeft: - { - [UIView performWithoutAnimation:^ - { - _landscapeToolbarView.frame = CGRectMake(screenEdges.left, screenEdges.top, TGPhotoEditorToolbarSize + safeAreaInset.left, referenceSize.height); - }]; - } - break; - - case UIInterfaceOrientationLandscapeRight: - { - [UIView performWithoutAnimation:^ - { - _landscapeToolbarView.frame = CGRectMake(screenEdges.right - TGPhotoEditorToolbarSize - safeAreaInset.right, screenEdges.top, TGPhotoEditorToolbarSize + safeAreaInset.right, referenceSize.height); - }]; - } - break; - - default: - { - _landscapeToolbarView.frame = CGRectMake(_landscapeToolbarView.frame.origin.x, screenEdges.top, TGPhotoEditorToolbarSize, referenceSize.height); - } - break; - } - - [self _layoutRecipientLabelForOrientation:orientation screenEdges:screenEdges]; - - if (_transitionInProgress) - return; - - if (!CGRectEqualToRect(_scrollView.frame, self.view.bounds)) - { - _scrollView.frame = CGRectMake(0.0f, _scrollViewVerticalOffset, self.view.bounds.size.width, self.view.bounds.size.height); - [self reset]; - } -} - -@end diff --git a/submodules/LegacyComponents/Sources/TGMediaAssetsController.m b/submodules/LegacyComponents/Sources/TGMediaAssetsController.m index dd2aa452d9..e77eaac4f0 100644 --- a/submodules/LegacyComponents/Sources/TGMediaAssetsController.m +++ b/submodules/LegacyComponents/Sources/TGMediaAssetsController.m @@ -1374,7 +1374,7 @@ @implementation TGMediaAssetsPallete -+ (instancetype)palleteWithDark:(bool)dark backgroundColor:(UIColor *)backgroundColor selectionColor:(UIColor *)selectionColor separatorColor:(UIColor *)separatorColor textColor:(UIColor *)textColor secondaryTextColor:(UIColor *)secondaryTextColor accentColor:(UIColor *)accentColor barBackgroundColor:(UIColor *)barBackgroundColor barSeparatorColor:(UIColor *)barSeparatorColor navigationTitleColor:(UIColor *)navigationTitleColor badge:(UIImage *)badge badgeTextColor:(UIColor *)badgeTextColor sendIconImage:(UIImage *)sendIconImage maybeAccentColor:(UIColor *)maybeAccentColor ++ (instancetype)palleteWithDark:(bool)dark backgroundColor:(UIColor *)backgroundColor selectionColor:(UIColor *)selectionColor separatorColor:(UIColor *)separatorColor textColor:(UIColor *)textColor secondaryTextColor:(UIColor *)secondaryTextColor accentColor:(UIColor *)accentColor barBackgroundColor:(UIColor *)barBackgroundColor barSeparatorColor:(UIColor *)barSeparatorColor navigationTitleColor:(UIColor *)navigationTitleColor badge:(UIImage *)badge badgeTextColor:(UIColor *)badgeTextColor sendIconImage:(UIImage *)sendIconImage doneIconImage:(UIImage *)doneIconImage maybeAccentColor:(UIColor *)maybeAccentColor { TGMediaAssetsPallete *pallete = [[TGMediaAssetsPallete alloc] init]; pallete->_isDark = dark; @@ -1390,6 +1390,7 @@ pallete->_badge = badge; pallete->_badgeTextColor = badgeTextColor; pallete->_sendIconImage = sendIconImage; + pallete->_doneIconImage = doneIconImage; pallete->_maybeAccentColor = maybeAccentColor; return pallete; } diff --git a/submodules/LegacyComponents/Sources/TGMediaAssetsPickerController.m b/submodules/LegacyComponents/Sources/TGMediaAssetsPickerController.m index b7c1f6832a..085737d580 100644 --- a/submodules/LegacyComponents/Sources/TGMediaAssetsPickerController.m +++ b/submodules/LegacyComponents/Sources/TGMediaAssetsPickerController.m @@ -441,7 +441,7 @@ { if (editableItem.isVideo) { if ([editableItem isKindOfClass:[TGMediaAsset class]]) { - return [TGMediaAssetImageSignals avAssetForVideoAsset:(TGMediaAsset *)editableItem]; + return [TGMediaAssetImageSignals avAssetForVideoAsset:(TGMediaAsset *)editableItem allowNetworkAccess:true]; } else if ([editableItem isKindOfClass:[TGCameraCapturedVideo class]]) { return ((TGCameraCapturedVideo *)editableItem).avAsset; } else { diff --git a/submodules/LegacyComponents/Sources/TGMediaAvatarEditorTransition.m b/submodules/LegacyComponents/Sources/TGMediaAvatarEditorTransition.m index 455b7570bd..e23598e047 100644 --- a/submodules/LegacyComponents/Sources/TGMediaAvatarEditorTransition.m +++ b/submodules/LegacyComponents/Sources/TGMediaAvatarEditorTransition.m @@ -94,6 +94,7 @@ { }]; + TGPhotoEditorController *controller = _controller; void (^imageReady)(void) = self.imageReady; _toTransitionView = [[TGImageView alloc] initWithFrame:fromTransitionFrame]; [_toTransitionView setSignal:[[[self.referenceScreenImageSignal() deliverOn:[SQueue mainQueue]] filter:^bool(id result) @@ -101,7 +102,7 @@ return [result isKindOfClass:[UIImage class]]; }] onNext:^(UIImage *next) { - [_controller _setScreenImage:next]; + [controller _setScreenImage:next]; if (imageReady != nil) imageReady(); }]]; diff --git a/submodules/LegacyComponents/Sources/TGMediaPickerGalleryModel.m b/submodules/LegacyComponents/Sources/TGMediaPickerGalleryModel.m index cb463180b6..f225854879 100644 --- a/submodules/LegacyComponents/Sources/TGMediaPickerGalleryModel.m +++ b/submodules/LegacyComponents/Sources/TGMediaPickerGalleryModel.m @@ -577,7 +577,7 @@ { if (editableItem.isVideo) { if ([editableItem isKindOfClass:[TGMediaAsset class]]) { - return [TGMediaAssetImageSignals avAssetForVideoAsset:(TGMediaAsset *)editableItem]; + return [TGMediaAssetImageSignals avAssetForVideoAsset:(TGMediaAsset *)editableItem allowNetworkAccess:true]; } else if ([editableItem isKindOfClass:[TGCameraCapturedVideo class]]) { return ((TGCameraCapturedVideo *)editableItem).avAsset; } else { diff --git a/submodules/LegacyComponents/Sources/TGMediaPickerGalleryVideoScrubber.h b/submodules/LegacyComponents/Sources/TGMediaPickerGalleryVideoScrubber.h index 75c30856d5..50ff18f206 100644 --- a/submodules/LegacyComponents/Sources/TGMediaPickerGalleryVideoScrubber.h +++ b/submodules/LegacyComponents/Sources/TGMediaPickerGalleryVideoScrubber.h @@ -15,11 +15,12 @@ @property (nonatomic, assign) NSTimeInterval trimStartValue; @property (nonatomic, assign) NSTimeInterval trimEndValue; -@property (nonatomic, assign) NSTimeInterval dotValue; +@property (nonatomic, assign) bool hasDotPicker; +- (void)setDotVideoView:(UIView *)dotVideoView; +- (void)setDotImage:(UIImage *)dotImage; @property (nonatomic, assign) NSTimeInterval maximumLength; - @property (nonatomic, assign) bool disableZoom; @property (nonatomic, assign) bool disableTimeDisplay; diff --git a/submodules/LegacyComponents/Sources/TGMediaPickerGalleryVideoScrubber.m b/submodules/LegacyComponents/Sources/TGMediaPickerGalleryVideoScrubber.m index e774062907..1a00467438 100644 --- a/submodules/LegacyComponents/Sources/TGMediaPickerGalleryVideoScrubber.m +++ b/submodules/LegacyComponents/Sources/TGMediaPickerGalleryVideoScrubber.m @@ -39,7 +39,10 @@ typedef enum UIView *_rightCurtainView; UIControl *_scrubberHandle; - UIImageView *_dotView; + UIControl *_dotHandle; + UIImageView *_dotImageView; + __weak UIView *_dotVideoView; + UIImageView *_dotFrameView; UIPanGestureRecognizer *_panGestureRecognizer; UILongPressGestureRecognizer *_pressGestureRecognizer; @@ -125,12 +128,7 @@ typedef enum _rightCurtainView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 0, 0)]; _rightCurtainView.backgroundColor = [[TGPhotoEditorInterfaceAssets toolbarBackgroundColor] colorWithAlphaComponent:0.8f]; [_wrapperView addSubview:_rightCurtainView]; - - _dotView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 8, 8)]; - _dotView.image = TGCircleImage(8.0, [TGPhotoEditorInterfaceAssets accentColor]); - _dotView.hidden = true; - [self addSubview:_dotView]; - + __weak TGMediaPickerGalleryVideoScrubber *weakSelf = self; _trimView = [[TGMediaPickerGalleryVideoTrimView alloc] initWithFrame:CGRectZero]; _trimView.exclusiveTouch = true; @@ -158,7 +156,11 @@ typedef enum [strongSelf->_trimView setTrimming:true animated:true]; - [strongSelf setScrubberHandleHidden:true animated:false]; + if (strongSelf->_hasDotPicker) { + [strongSelf setDotHandleHidden:true animated:false]; + } else { + [strongSelf setScrubberHandleHidden:true animated:false]; + } }; _trimView.didEndEditing = ^ { @@ -206,7 +208,11 @@ typedef enum [strongSelf->_trimView setTrimming:isTrimmed animated:true]; - [strongSelf setScrubberHandleHidden:false animated:true]; + if (strongSelf->_hasDotPicker) { + [strongSelf setDotHandleHidden:false animated:true]; + } else { + [strongSelf setScrubberHandleHidden:false animated:true]; + } [strongSelf cancelZoomIn]; if (strongSelf->_zoomedIn) @@ -260,8 +266,13 @@ typedef enum strongSelf->_trimStartValue = trimStartPosition; strongSelf->_trimEndValue = trimEndPosition; - [strongSelf setValue:strongSelf->_trimStartValue]; - + if (strongSelf->_hasDotPicker) { + if (strongSelf->_value < trimStartPosition) { + strongSelf->_value = trimStartPosition; + } + } else { + [strongSelf setValue:trimStartPosition]; + } UIView *handle = strongSelf->_scrubberHandle; handle.center = CGPointMake(trimView.frame.origin.x + 12 + handle.frame.size.width / 2, handle.center.y); @@ -323,7 +334,13 @@ typedef enum strongSelf->_trimStartValue = trimStartPosition; strongSelf->_trimEndValue = trimEndPosition; - [strongSelf setValue:strongSelf->_trimEndValue]; + if (strongSelf->_hasDotPicker) { + if (strongSelf->_value > trimEndPosition) { + strongSelf->_value = trimEndPosition; + } + } else { + [strongSelf setValue:trimEndPosition]; + } UIView *handle = strongSelf->_scrubberHandle; handle.center = CGPointMake(CGRectGetMaxX(trimView.frame) - 12 - handle.frame.size.width / 2, handle.center.y); @@ -341,13 +358,44 @@ typedef enum }; [_wrapperView addSubview:_trimView]; + _dotHandle = [[UIControl alloc] initWithFrame:CGRectMake(0, -4.0f, 26.0f, 44.0f)]; + _dotHandle.hitTestEdgeInsets = UIEdgeInsetsMake(-5, -12, -5, -12); + _dotHandle.hidden = true; + [_wrapperView addSubview:_dotHandle]; + + static UIImage *dotFrameImage = nil; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^ + { + UIGraphicsBeginImageContextWithOptions(CGSizeMake(_dotHandle.frame.size.width, _dotHandle.frame.size.height), false, 0.0f); + CGContextRef context = UIGraphicsGetCurrentContext(); + CGContextSetStrokeColorWithColor(context, [UIColor whiteColor].CGColor); + CGContextSetLineWidth(context, 3.0); + + UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(1.5f, 1.5f, _dotHandle.frame.size.width - 3.0, _dotHandle.frame.size.height - 3.0f) cornerRadius:4.0f]; + CGContextAddPath(context, path.CGPath); + CGContextStrokePath(context); + + dotFrameImage = UIGraphicsGetImageFromCurrentImageContext(); + UIGraphicsEndImageContext(); + }); + + _dotImageView = [[UIImageView alloc] initWithFrame:CGRectInset(_dotHandle.bounds, 2.0, 2.0)]; + _dotImageView.clipsToBounds = true; + _dotImageView.contentMode = UIViewContentModeScaleAspectFill; + [_dotHandle addSubview:_dotImageView]; + + _dotFrameView = [[UIImageView alloc] initWithFrame:_dotHandle.bounds]; + _dotFrameView.image = dotFrameImage; + [_dotHandle addSubview:_dotFrameView]; + _scrubberHandle = [[UIControl alloc] initWithFrame:CGRectMake(0, -4.0f, 5.0f, 44.0f)]; _scrubberHandle.hitTestEdgeInsets = UIEdgeInsetsMake(-5, -12, -5, -12); [_wrapperView addSubview:_scrubberHandle]; static UIImage *handleViewImage = nil; - static dispatch_once_t onceToken; - dispatch_once(&onceToken, ^ + static dispatch_once_t onceToken2; + dispatch_once(&onceToken2, ^ { UIGraphicsBeginImageContextWithOptions(CGSizeMake(_scrubberHandle.frame.size.width, _scrubberHandle.frame.size.height), false, 0.0f); CGContextRef context = UIGraphicsGetCurrentContext(); @@ -373,6 +421,7 @@ typedef enum _panGestureRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(handlePan:)]; _panGestureRecognizer.delegate = self; [_scrubberHandle addGestureRecognizer:_panGestureRecognizer]; + [_dotHandle addGestureRecognizer:_panGestureRecognizer]; _arrowView = [[UIImageView alloc] initWithImage:TGComponentsImageNamed(@"PhotoPickerArrow")]; _arrowView.alpha = 0.45f; @@ -400,6 +449,22 @@ typedef enum [self _layoutRecipientLabel]; } +- (void)setHasDotPicker:(bool)hasDotPicker { + _hasDotPicker = hasDotPicker; + _dotHandle.hidden = !hasDotPicker; + _scrubberHandle.hidden = true; +} + +- (void)setDotVideoView:(UIView *)dotVideoView { + _dotVideoView = dotVideoView; + _dotVideoView.frame = CGRectInset(_dotHandle.bounds, 2.0, 2.0); + [_dotHandle insertSubview:dotVideoView belowSubview:_dotFrameView]; +} + +- (void)setDotImage:(UIImage *)dotImage { + _dotImageView.image = dotImage; +} + - (bool)zoomAvailable { if (_disableZoom || _zoomedIn || _preparingToZoomIn || _summaryTimestamps.count == 0) @@ -886,13 +951,18 @@ typedef enum CGPoint point = [self _scrubberPositionForPosition:_value duration:_duration zoomedIn:zoomedIn]; CGRect frame = CGRectMake(CGFloor(point.x) - _scrubberHandle.frame.size.width / 2, _scrubberHandle.frame.origin.y, _scrubberHandle.frame.size.width, _scrubberHandle.frame.size.height); + CGPoint dotPoint = [self _dotPositionForPosition:_value duration:_duration]; + CGRect dotFrame = CGRectMake(CGFloor(dotPoint.x) - _dotHandle.frame.size.width / 2, _dotHandle.frame.origin.y, _dotHandle.frame.size.width, _dotHandle.frame.size.height); + if (_trimStartValue > DBL_EPSILON && fabs(_value - _trimStartValue) < 0.01) { frame = CGRectMake(_trimView.frame.origin.x + [self _scrubbingRectZoomedIn:zoomedIn].origin.x, _scrubberHandle.frame.origin.y, _scrubberHandle.frame.size.width, _scrubberHandle.frame.size.height); + dotFrame = CGRectMake(_trimView.frame.origin.x + [self _scrubbingRectZoomedIn:false].origin.x, _dotHandle.frame.origin.y, _dotHandle.frame.size.width, _dotHandle.frame.size.height); } else if (fabs(_value - _trimEndValue) < 0.01) { frame = CGRectMake(_trimView.frame.origin.x + _trimView.frame.size.width - [self _scrubbingRectZoomedIn:zoomedIn].origin.x - _scrubberHandle.frame.size.width, _scrubberHandle.frame.origin.y, _scrubberHandle.frame.size.width, _scrubberHandle.frame.size.height); + dotFrame = CGRectMake(_trimView.frame.origin.x + _trimView.frame.size.width - [self _scrubbingRectZoomedIn:false].origin.x - _dotHandle.frame.size.width, _dotHandle.frame.origin.y, _dotHandle.frame.size.width, _dotHandle.frame.size.height); } if (_isPlaying) @@ -921,6 +991,8 @@ typedef enum [self removeHandleAnimation]; _scrubberHandle.frame = frame; } + + _dotHandle.frame = dotFrame; } - (void)addHandleAnimationFromFrame:(CGRect)fromFrame toFrame:(CGRect)toFrame duration:(NSTimeInterval)duration @@ -1030,6 +1102,8 @@ typedef enum CGPoint translation = [gestureRecognizer translationInView:self]; [gestureRecognizer setTranslation:CGPointZero inView:self]; + UIView *handle = gestureRecognizer.view; + switch (gestureRecognizer.state) { case UIGestureRecognizerStateBegan: @@ -1059,23 +1133,23 @@ typedef enum CGRect scrubbingRect = [self _scrubbingRect]; CGRect normalScrubbingRect = [self _scrubbingRectZoomedIn:false]; - CGFloat minPosition = scrubbingRect.origin.x + _scrubberHandle.frame.size.width / 2; - CGFloat maxPosition = scrubbingRect.origin.x + scrubbingRect.size.width - _scrubberHandle.frame.size.width / 2; + CGFloat minPosition = scrubbingRect.origin.x + handle.frame.size.width / 2; + CGFloat maxPosition = scrubbingRect.origin.x + scrubbingRect.size.width - handle.frame.size.width / 2; if (self.allowsTrimming) { - minPosition = MAX(minPosition, _trimView.frame.origin.x + normalScrubbingRect.origin.x + _scrubberHandle.frame.size.width / 2); - maxPosition = MIN(maxPosition, CGRectGetMaxX(_trimView.frame) - normalScrubbingRect.origin.x - _scrubberHandle.frame.size.width / 2); + minPosition = MAX(minPosition, _trimView.frame.origin.x + normalScrubbingRect.origin.x + handle.frame.size.width / 2); + maxPosition = MIN(maxPosition, CGRectGetMaxX(_trimView.frame) - normalScrubbingRect.origin.x - handle.frame.size.width / 2); } - _scrubberHandle.center = CGPointMake(MIN(MAX(_scrubberHandle.center.x + translation.x, minPosition), maxPosition), _scrubberHandle.center.y); + handle.center = CGPointMake(MIN(MAX(handle.center.x + translation.x, minPosition), maxPosition), handle.center.y); - NSTimeInterval position = [self _positionForScrubberPosition:_scrubberHandle.center duration:_duration]; + NSTimeInterval position = [self _positionForScrubberPosition:handle.center duration:_duration]; if (self.allowsTrimming) { - if (ABS(_scrubberHandle.center.x - minPosition) < FLT_EPSILON) + if (ABS(handle.center.x - minPosition) < FLT_EPSILON) position = _trimStartValue; - else if (ABS(_scrubberHandle.center.x - maxPosition) < FLT_EPSILON) + else if (ABS(handle.center.x - maxPosition) < FLT_EPSILON) position = _trimEndValue; } @@ -1104,9 +1178,7 @@ typedef enum return; _scrubbing = false; - - [self setDotValue:_value]; - + id delegate = self.delegate; if ([delegate respondsToSelector:@selector(videoScrubberDidEndScrubbing:)]) [delegate videoScrubberDidEndScrubbing:self]; @@ -1145,6 +1217,27 @@ typedef enum } } +- (void)setDotHandleHidden:(bool)hidden animated:(bool)animated +{ + if (animated) + { + _dotHandle.hidden = false; + [UIView animateWithDuration:0.25f animations:^ + { + _dotHandle.alpha = hidden ? 0.0f : 1.0f; + } completion:^(BOOL finished) + { + if (finished) + _dotHandle.hidden = hidden; + }]; + } + else + { + _dotHandle.hidden = hidden; + _dotHandle.alpha = hidden ? 0.0f : 1.0f; + } +} + - (CGPoint)_scrubberPositionForPosition:(NSTimeInterval)position duration:(NSTimeInterval)duration { return [self _scrubberPositionForPosition:position duration:duration zoomedIn:_zoomedIn]; @@ -1202,25 +1295,17 @@ typedef enum #pragma mark - Dot -- (void)setDotValue:(NSTimeInterval)dotValue +- (CGPoint)_dotPositionForPosition:(NSTimeInterval)position duration:(NSTimeInterval)duration { - _dotValue = dotValue; + CGRect scrubbingRect = [self _scrubbingRectZoomedIn:false]; - if (dotValue > FLT_EPSILON) { - _dotView.hidden = false; - - CGPoint point = [self _scrubberPositionForPosition:dotValue duration:_duration zoomedIn:false]; - _dotView.frame = CGRectMake(_wrapperView.frame.origin.x + point.x - _dotView.frame.size.width / 2.0, 8.0f, _dotView.frame.size.width, _dotView.frame.size.height); - - _dotView.alpha = 0.0f; - _dotView.transform = CGAffineTransformMakeScale(0.25, 0.25); - [UIView animateWithDuration:0.2 animations:^{ - _dotView.alpha = 1.0; - _dotView.transform = CGAffineTransformIdentity; - }]; - } else { - _dotView.hidden = true; + if (duration < FLT_EPSILON) + { + position = 0.0; + duration = 1.0; } + + return CGPointMake(_dotHandle.frame.size.width / 2 + scrubbingRect.origin.x + (CGFloat)(position / duration) * (scrubbingRect.size.width - _dotHandle.frame.size.width), CGRectGetMidY([self _scrubbingRect])); } #pragma mark - Trimming diff --git a/submodules/LegacyComponents/Sources/TGMediaVideoConverter.m b/submodules/LegacyComponents/Sources/TGMediaVideoConverter.m index 6a90b98ce5..6dc2187e14 100644 --- a/submodules/LegacyComponents/Sources/TGMediaVideoConverter.m +++ b/submodules/LegacyComponents/Sources/TGMediaVideoConverter.m @@ -125,7 +125,7 @@ CGSize dimensions = [avAsset tracksWithMediaType:AVMediaTypeVideo].firstObject.naturalSize; TGMediaVideoConversionPreset preset = adjustments.sendAsGif ? TGMediaVideoConversionPresetAnimation : [self presetFromAdjustments:adjustments]; - if (!CGSizeEqualToSize(dimensions, CGSizeZero) && preset != TGMediaVideoConversionPresetAnimation && preset != TGMediaVideoConversionPresetVideoMessage) + if (!CGSizeEqualToSize(dimensions, CGSizeZero) && preset != TGMediaVideoConversionPresetAnimation && preset != TGMediaVideoConversionPresetVideoMessage && preset != TGMediaVideoConversionPresetProfile) { TGMediaVideoConversionPreset bestPreset = [self bestAvailablePresetForDimensions:dimensions]; if (preset > bestPreset) @@ -344,8 +344,6 @@ if (TGOrientationIsSideward(adjustments.cropOrientation, NULL)) outputDimensions = CGSizeMake(outputDimensions.height, outputDimensions.width); - CMTimeRange instructionTimeRange = CMTimeRangeMake(kCMTimeZero, timeRange.duration); - AVMutableCompositionTrack *compositionTrack = [composition addMutableTrackWithMediaType:AVMediaTypeVideo preferredTrackID:kCMPersistentTrackID_Invalid]; if (adjustments.videoStartValue > 0.0 && adjustments.videoStartValue > adjustments.trimStartValue) { NSTimeInterval trimEndValue = adjustments.trimEndValue > adjustments.trimStartValue ? adjustments.trimEndValue : CMTimeGetSeconds(videoTrack.timeRange.duration); @@ -1340,7 +1338,7 @@ static CGFloat progressOfSampleBufferInTimeRange(CMSampleBufferRef sampleBuffer, return 300; case TGMediaVideoConversionPresetProfile: - return 1000; + return 1800; default: return 900; diff --git a/submodules/LegacyComponents/Sources/TGOverlayControllerWindow.m b/submodules/LegacyComponents/Sources/TGOverlayControllerWindow.m index d171d429ac..eb45317107 100644 --- a/submodules/LegacyComponents/Sources/TGOverlayControllerWindow.m +++ b/submodules/LegacyComponents/Sources/TGOverlayControllerWindow.m @@ -172,8 +172,11 @@ if ([_manager managesWindow]) { _contentController = contentController; __weak TGOverlayControllerWindow *weakSelf = self; + __weak TGViewController *weakParentController = parentController; contentController.customDismissBlock = ^{ __strong TGOverlayControllerWindow *strongSelf = weakSelf; + __strong TGViewController *strongParentController = weakParentController; + [strongParentController.associatedWindowStack removeObject:strongSelf]; [manager setHidden:true window:strongSelf]; }; [_manager bindController:contentController]; diff --git a/submodules/LegacyComponents/Sources/TGPhotoAvatarCropController.m b/submodules/LegacyComponents/Sources/TGPhotoAvatarCropController.m index 11c9f1f356..337d8624a9 100644 --- a/submodules/LegacyComponents/Sources/TGPhotoAvatarCropController.m +++ b/submodules/LegacyComponents/Sources/TGPhotoAvatarCropController.m @@ -277,6 +277,8 @@ const CGFloat TGPhotoAvatarCropButtonsWrapperSize = 61.0f; { [_cropView hideImageForCustomTransition]; [_cropView animateTransitionOutSwitching:false]; + [_cropView invalidateVideoView]; + [UIView animateWithDuration:0.3f animations:^ { _buttonsWrapperView.alpha = 0.0f; @@ -321,9 +323,9 @@ const CGFloat TGPhotoAvatarCropButtonsWrapperSize = 61.0f; [photoEditor setImage:croppedImage forCropRect:_cropView.cropRect cropRotation:0.0f cropOrientation:_cropView.cropOrientation cropMirrored:_cropView.cropMirrored fullSize:false]; [photoEditor processAnimated:false completion:^ - { + { TGDispatchOnMainThread(^ - { + { [previewView setSnapshotImage:croppedImage]; if (!previewView.hidden) diff --git a/submodules/LegacyComponents/Sources/TGPhotoAvatarPreviewController.m b/submodules/LegacyComponents/Sources/TGPhotoAvatarPreviewController.m index 7614c368f0..070a006cce 100644 --- a/submodules/LegacyComponents/Sources/TGPhotoAvatarPreviewController.m +++ b/submodules/LegacyComponents/Sources/TGPhotoAvatarPreviewController.m @@ -14,6 +14,7 @@ #import "TGPhotoEditorSparseView.h" #import "TGMediaPickerGalleryVideoScrubber.h" +#import "TGModernGalleryVideoView.h" const CGFloat TGPhotoAvatarPreviewPanelSize = 96.0f; const CGFloat TGPhotoAvatarPreviewLandscapePanelSize = TGPhotoAvatarPreviewPanelSize + 40.0f; @@ -36,6 +37,7 @@ const CGFloat TGPhotoAvatarPreviewLandscapePanelSize = TGPhotoAvatarPreviewPanel CGFloat _currentDiameter; TGMediaPickerGalleryVideoScrubber *_scrubberView; + TGModernGalleryVideoView *_dotVideoView; UILabel *_coverLabel; bool _wasPlayingBeforeScrubbing; bool _requestingThumbnails; @@ -139,11 +141,13 @@ const CGFloat TGPhotoAvatarPreviewLandscapePanelSize = TGPhotoAvatarPreviewPanel { [super viewDidLoad]; - _scrubberView.allowsTrimming = true; + _scrubberView.allowsTrimming = self.item.originalDuration >= TGVideoEditMinimumTrimmableDuration; + _scrubberView.hasDotPicker = true; _scrubberView.disableZoom = true; _scrubberView.disableTimeDisplay = true; _scrubberView.trimStartValue = 0.0; - _scrubberView.trimEndValue = self.item.originalDuration; + _scrubberView.trimEndValue = MIN(10.0, self.item.originalDuration); + _scrubberView.maximumLength = 10.0; [_scrubberView reloadData]; [_scrubberView resetToStart]; } @@ -168,12 +172,8 @@ const CGFloat TGPhotoAvatarPreviewLandscapePanelSize = TGPhotoAvatarPreviewPanel _portraitToolsWrapperView.alpha = 1.0f; _landscapeToolsWrapperView.alpha = 1.0f; }]; - - UIInterfaceOrientation orientation = self.interfaceOrientation; - if ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPad) - orientation = UIInterfaceOrientationPortrait; - - switch (orientation) + + switch (self.effectiveOrientation) { case UIInterfaceOrientationLandscapeLeft: { @@ -216,12 +216,8 @@ const CGFloat TGPhotoAvatarPreviewLandscapePanelSize = TGPhotoAvatarPreviewPanel previewView.interactionEnded = nil; [_videoAreaView.superview bringSubviewToFront:_videoAreaView]; - - UIInterfaceOrientation orientation = self.interfaceOrientation; - if ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPad) - orientation = UIInterfaceOrientationPortrait; - switch (orientation) + switch (self.effectiveOrientation) { case UIInterfaceOrientationLandscapeLeft: { @@ -383,11 +379,7 @@ const CGFloat TGPhotoAvatarPreviewLandscapePanelSize = TGPhotoAvatarPreviewPanel - (CGRect)transitionOutSourceFrameForReferenceFrame:(CGRect)referenceFrame orientation:(UIInterfaceOrientation)orientation { - bool hasOnScreenNavigation = false; - if (iosMajorVersion() >= 11) - hasOnScreenNavigation = (self.viewLoaded && self.view.safeAreaInsets.bottom > FLT_EPSILON) || self.context.safeAreaInset.bottom > FLT_EPSILON; - - CGRect containerFrame = [TGPhotoAvatarPreviewController photoContainerFrameForParentViewFrame:self.view.frame toolbarLandscapeSize:self.toolbarLandscapeSize orientation:orientation panelSize:0 hasOnScreenNavigation:hasOnScreenNavigation]; + CGRect containerFrame = [TGPhotoAvatarPreviewController photoContainerFrameForParentViewFrame:self.view.frame toolbarLandscapeSize:self.toolbarLandscapeSize orientation:orientation panelSize:0 hasOnScreenNavigation:self.hasOnScreenNavigation]; CGSize fittedSize = TGScaleToSize(referenceFrame.size, containerFrame.size); CGRect sourceFrame = CGRectMake(containerFrame.origin.x + (containerFrame.size.width - fittedSize.width) / 2, containerFrame.origin.y + (containerFrame.size.height - fittedSize.height) / 2, fittedSize.width, fittedSize.height); @@ -397,16 +389,7 @@ const CGFloat TGPhotoAvatarPreviewLandscapePanelSize = TGPhotoAvatarPreviewPanel - (CGRect)_targetFrameForTransitionInFromFrame:(CGRect)fromFrame { CGSize referenceSize = [self referenceViewSize]; - UIInterfaceOrientation orientation = self.interfaceOrientation; - - if ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPad) - orientation = UIInterfaceOrientationPortrait; - - bool hasOnScreenNavigation = false; - if (iosMajorVersion() >= 11) - hasOnScreenNavigation = (self.viewLoaded && self.view.safeAreaInsets.bottom > FLT_EPSILON) || self.context.safeAreaInset.bottom > FLT_EPSILON; - - CGRect containerFrame = [TGPhotoAvatarPreviewController photoContainerFrameForParentViewFrame:CGRectMake(0, 0, referenceSize.width, referenceSize.height) toolbarLandscapeSize:self.toolbarLandscapeSize orientation:orientation panelSize:0 hasOnScreenNavigation:hasOnScreenNavigation]; + CGRect containerFrame = [TGPhotoAvatarPreviewController photoContainerFrameForParentViewFrame:CGRectMake(0, 0, referenceSize.width, referenceSize.height) toolbarLandscapeSize:self.toolbarLandscapeSize orientation:self.effectiveOrientation panelSize:0 hasOnScreenNavigation:self.hasOnScreenNavigation]; CGSize fittedSize = TGScaleToSize(fromFrame.size, containerFrame.size); CGRect toFrame = CGRectMake(containerFrame.origin.x + (containerFrame.size.width - fittedSize.width) / 2, containerFrame.origin.y + (containerFrame.size.height - fittedSize.height) / 2, fittedSize.width, fittedSize.height); @@ -440,12 +423,8 @@ const CGFloat TGPhotoAvatarPreviewLandscapePanelSize = TGPhotoAvatarPreviewPanel CGFloat panelToolbarPortraitSize = panelSize + TGPhotoEditorToolbarSize; CGFloat panelToolbarLandscapeSize = panelSize + TGPhotoEditorToolbarSize; - - bool hasOnScreenNavigation = false; - if (iosMajorVersion() >= 11) - hasOnScreenNavigation = (self.viewLoaded && self.view.safeAreaInsets.bottom > FLT_EPSILON) || self.context.safeAreaInset.bottom > FLT_EPSILON; - - UIEdgeInsets safeAreaInset = [TGViewController safeAreaInsetForOrientation:orientation hasOnScreenNavigation:hasOnScreenNavigation]; + + UIEdgeInsets safeAreaInset = [TGViewController safeAreaInsetForOrientation:orientation hasOnScreenNavigation:self.hasOnScreenNavigation]; UIEdgeInsets screenEdges = UIEdgeInsetsMake((screenSide - referenceSize.height) / 2, (screenSide - referenceSize.width) / 2, (screenSide + referenceSize.height) / 2, (screenSide + referenceSize.width) / 2); screenEdges.top += safeAreaInset.top; screenEdges.left += safeAreaInset.left; @@ -517,10 +496,6 @@ const CGFloat TGPhotoAvatarPreviewLandscapePanelSize = TGPhotoAvatarPreviewPanel - (void)updatePreviewView { - UIInterfaceOrientation orientation = self.interfaceOrientation; - if ([self inFormSheet] || TGIsPad()) - orientation = UIInterfaceOrientationPortrait; - CGSize referenceSize = [self referenceViewSize]; PGPhotoEditor *photoEditor = self.photoEditor; @@ -528,12 +503,8 @@ const CGFloat TGPhotoAvatarPreviewLandscapePanelSize = TGPhotoAvatarPreviewPanel if (_dismissing || previewView.superview != self.view) return; - - bool hasOnScreenNavigation = false; - if (iosMajorVersion() >= 11) - hasOnScreenNavigation = (self.viewLoaded && self.view.safeAreaInsets.bottom > FLT_EPSILON) || self.context.safeAreaInset.bottom > FLT_EPSILON; - - CGRect containerFrame = [TGPhotoAvatarPreviewController photoContainerFrameForParentViewFrame:CGRectMake(0, 0, referenceSize.width, referenceSize.height) toolbarLandscapeSize:self.toolbarLandscapeSize orientation:orientation panelSize:0 hasOnScreenNavigation:hasOnScreenNavigation]; + + CGRect containerFrame = [TGPhotoAvatarPreviewController photoContainerFrameForParentViewFrame:CGRectMake(0, 0, referenceSize.width, referenceSize.height) toolbarLandscapeSize:self.toolbarLandscapeSize orientation:self.effectiveOrientation panelSize:0 hasOnScreenNavigation:self.hasOnScreenNavigation]; CGSize fittedSize = TGScaleToSize(photoEditor.rotatedCropSize, containerFrame.size); previewView.frame = CGRectMake(containerFrame.origin.x + (containerFrame.size.width - fittedSize.width) / 2, containerFrame.origin.y + (containerFrame.size.height - fittedSize.height) / 2, fittedSize.width, fittedSize.height); @@ -578,7 +549,7 @@ const CGFloat TGPhotoAvatarPreviewLandscapePanelSize = TGPhotoAvatarPreviewPanel if (!_dismissing) [self updateToolViews]; - + dispatch_async(dispatch_get_main_queue(), ^{ [_scrubberView reloadThumbnails]; }); @@ -588,12 +559,12 @@ const CGFloat TGPhotoAvatarPreviewLandscapePanelSize = TGPhotoAvatarPreviewPanel - (TGPhotoEditorTab)availableTabs { - return TGPhotoEditorPaintTab | TGPhotoEditorToolsTab; + return TGPhotoEditorCropTab | TGPhotoEditorPaintTab | TGPhotoEditorToolsTab; } - (TGPhotoEditorTab)activeTab { - return TGPhotoEditorCropTab; + return TGPhotoEditorNoneTab; } - (TGPhotoEditorTab)highlightedTabs @@ -680,8 +651,8 @@ const CGFloat TGPhotoAvatarPreviewLandscapePanelSize = TGPhotoAvatarPreviewPanel { _wasPlayingBeforeScrubbing = true; self.controlVideoPlayback(false); - - _scrubberView.dotValue = 0.0; + + _dotVideoView.hidden = false; _coverLabel.alpha = 1.0f; @@ -694,6 +665,19 @@ const CGFloat TGPhotoAvatarPreviewLandscapePanelSize = TGPhotoAvatarPreviewPanel - (void)videoScrubberDidEndScrubbing:(TGMediaPickerGalleryVideoScrubber *)__unused videoScrubber { + AVPlayer *player = ((TGPhotoEditorController *)self.parentViewController).player; + AVAssetImageGenerator *generator = [[AVAssetImageGenerator alloc] initWithAsset:player.currentItem.asset]; + generator.appliesPreferredTrackTransform = true; + generator.maximumSize = CGSizeMake(128.0f, 128.0f); + generator.requestedTimeToleranceAfter = kCMTimeZero; + generator.requestedTimeToleranceBefore = kCMTimeZero; + CGImageRef imageRef = [generator copyCGImageAtTime:player.currentItem.currentTime actualTime:NULL error:NULL]; + UIImage *thumbnailImage = [[UIImage alloc] initWithCGImage:imageRef]; + CGImageRelease(imageRef); + + [_scrubberView setDotImage:thumbnailImage]; + _dotVideoView.hidden = true; + [UIView animateWithDuration:0.12 animations:^{ _flashView.alpha = 1.0f; } completion:^(BOOL finished) { @@ -791,18 +775,30 @@ const CGFloat TGPhotoAvatarPreviewLandscapePanelSize = TGPhotoAvatarPreviewPanel id adjustments = [self.photoEditor exportAdjustments]; + NSArray *cachedThumbnails = ((TGPhotoEditorController *)self.parentViewController).cachedVideoThumbnails; + SSignal *thumbnailsSignal = nil; - if ([self.item isKindOfClass:[TGMediaAsset class]]) + if (cachedThumbnails.count > 0) { + thumbnailsSignal = [SSignal single:cachedThumbnails]; + } else if ([self.item isKindOfClass:[TGMediaAsset class]]) { thumbnailsSignal = [TGMediaAssetImageSignals videoThumbnailsForAsset:(TGMediaAsset *)self.item size:size timestamps:timestamps]; - else if ([self.item isKindOfClass:[TGCameraCapturedVideo class]]) + } else if ([self.item isKindOfClass:[TGCameraCapturedVideo class]]) { thumbnailsSignal = [((TGCameraCapturedVideo *)self.item).avAsset mapToSignal:^SSignal *(AVAsset *avAsset) { return [TGMediaAssetImageSignals videoThumbnailsForAVAsset:avAsset size:size timestamps:timestamps]; }]; - + } + _requestingThumbnails = true; __weak TGPhotoAvatarPreviewController *weakSelf = self; - [_thumbnailsDisposable setDisposable:[[[thumbnailsSignal map:^NSArray *(NSArray *images) { + [_thumbnailsDisposable setDisposable:[[[[thumbnailsSignal onNext:^(NSArray *images) { + __strong TGPhotoAvatarPreviewController *strongSelf = weakSelf; + if (strongSelf == nil) + return; + TGDispatchOnMainThread(^{ + ((TGPhotoEditorController *)strongSelf.parentViewController).cachedVideoThumbnails = images; + }); + }] map:^NSArray *(NSArray *images) { if (adjustments.toolsApplied) { NSMutableArray *editedImages = [[NSMutableArray alloc] init]; PGPhotoEditor *editor = [[PGPhotoEditor alloc] initWithOriginalSize:adjustments.originalSize adjustments:adjustments forVideo:false enableStickers:true]; @@ -868,16 +864,22 @@ const CGFloat TGPhotoAvatarPreviewLandscapePanelSize = TGPhotoAvatarPreviewPanel - (void)setScrubberPosition:(NSTimeInterval)position reset:(bool)reset { - [_scrubberView setValue:_scrubberView.trimStartValue resetPosition:reset]; + } - (void)setScrubberPlaying:(bool)value { - [_scrubberView setIsPlaying:value]; + if (_dotVideoView == nil) { + AVPlayer *player = ((TGPhotoEditorController *)self.parentViewController).player; + _dotVideoView = [[TGModernGalleryVideoView alloc] initWithFrame:CGRectMake(0.0, 0.0, 27.0, 44.0) player:player]; + _dotVideoView.playerLayer.videoGravity = AVLayerVideoGravityResizeAspectFill; + _dotVideoView.hidden = true; + [_scrubberView setDotVideoView:_dotVideoView]; + } } - (NSTimeInterval)coverPosition { - return _scrubberView.dotValue; + return _scrubberView.value; } @end diff --git a/submodules/LegacyComponents/Sources/TGPhotoCropController.m b/submodules/LegacyComponents/Sources/TGPhotoCropController.m index 376a95bac8..9e497a5ed4 100644 --- a/submodules/LegacyComponents/Sources/TGPhotoCropController.m +++ b/submodules/LegacyComponents/Sources/TGPhotoCropController.m @@ -37,9 +37,6 @@ NSString * const TGPhotoCropOriginalAspectRatio = @"original"; CGFloat _autoRotationAngle; UIView *_buttonsWrapperView; - TGModernButton *_rotateButton; - TGModernButton *_mirrorButton; - TGModernButton *_aspectRatioButton; TGModernButton *_resetButton; TGPhotoCropView *_cropView; @@ -107,7 +104,7 @@ NSString * const TGPhotoCropOriginalAspectRatio = @"original"; [self.view addSubview:_wrapperView]; PGPhotoEditor *photoEditor = self.photoEditor; - _cropView = [[TGPhotoCropView alloc] initWithOriginalSize:photoEditor.originalSize hasArbitraryRotation:true]; + _cropView = [[TGPhotoCropView alloc] initWithOriginalSize:photoEditor.originalSize hasArbitraryRotation:!_forVideo]; [_cropView setCropRect:photoEditor.cropRect]; [_cropView setCropOrientation:photoEditor.cropOrientation]; [_cropView setRotation:photoEditor.cropRotation]; @@ -153,31 +150,6 @@ NSString * const TGPhotoCropOriginalAspectRatio = @"original"; _buttonsWrapperView = [[UIView alloc] initWithFrame:CGRectZero]; [_wrapperView addSubview:_buttonsWrapperView]; - _rotateButton = [[TGModernButton alloc] initWithFrame:CGRectMake(0, 0, 36, 36)]; - _rotateButton.exclusiveTouch = true; - _rotateButton.hitTestEdgeInsets = UIEdgeInsetsMake(-10, -10, -10, -10); - [_rotateButton addTarget:self action:@selector(rotate) forControlEvents:UIControlEventTouchUpInside]; - [_rotateButton setImage:TGComponentsImageNamed(@"PhotoEditorRotateIcon") forState:UIControlStateNormal]; - //[_buttonsWrapperView addSubview:_rotateButton]; - - _mirrorButton = [[TGModernButton alloc] initWithFrame:CGRectMake(0, 0, 36, 36)]; - _mirrorButton.exclusiveTouch = true; - _mirrorButton.imageEdgeInsets = UIEdgeInsetsMake(4.0f, 0.0f, 0.0f, 0.0f); - _mirrorButton.hitTestEdgeInsets = UIEdgeInsetsMake(-10, -10, -10, -10); - [_mirrorButton addTarget:self action:@selector(mirror) forControlEvents:UIControlEventTouchUpInside]; - [_mirrorButton setImage:TGComponentsImageNamed(@"PhotoEditorMirrorIcon") forState:UIControlStateNormal]; - //[_buttonsWrapperView addSubview:_mirrorButton]; - - _aspectRatioButton = [[TGModernButton alloc] initWithFrame:CGRectMake(0, 0, 36, 36)]; - _aspectRatioButton.exclusiveTouch = true; - _aspectRatioButton.hitTestEdgeInsets = UIEdgeInsetsMake(-10, -10, -10, -10); - [_aspectRatioButton addTarget:self action:@selector(aspectRatioButtonPressed) forControlEvents:UIControlEventTouchUpInside]; - UIImage *aspectRatioHighlightedImage = TGTintedImage(TGComponentsImageNamed(@"PhotoEditorAspectRatioIcon"), [TGPhotoEditorInterfaceAssets accentColor]); - [_aspectRatioButton setImage:TGComponentsImageNamed(@"PhotoEditorAspectRatioIcon") forState:UIControlStateNormal]; - [_aspectRatioButton setImage:aspectRatioHighlightedImage forState:UIControlStateSelected]; - [_aspectRatioButton setImage:aspectRatioHighlightedImage forState:UIControlStateSelected | UIControlStateHighlighted]; - //[_buttonsWrapperView addSubview:_aspectRatioButton]; - NSString *resetButtonTitle = TGLocalized(@"PhotoEditor.CropReset"); _resetButton = [[TGModernButton alloc] init]; _resetButton.exclusiveTouch = true; @@ -190,14 +162,10 @@ NSString * const TGPhotoCropOriginalAspectRatio = @"original"; _resetButton.frame = CGRectMake(0, 0, _resetButton.frame.size.width, 24); [_buttonsWrapperView addSubview:_resetButton]; - if ([resetButtonTitle respondsToSelector:@selector(sizeWithAttributes:)]) - _resetButtonWidth = CGCeil([resetButtonTitle sizeWithAttributes:@{ NSFontAttributeName:TGSystemFontOfSize(13) }].width); - else - _resetButtonWidth = CGCeil([resetButtonTitle sizeWithFont:TGSystemFontOfSize(13)].width); + _resetButtonWidth = CGCeil([resetButtonTitle sizeWithAttributes:@{ NSFontAttributeName:TGSystemFontOfSize(13) }].width); if (photoEditor.cropLockedAspectRatio > FLT_EPSILON) { - _aspectRatioButton.selected = true; [_cropView setLockedAspectRatio:photoEditor.cropLockedAspectRatio performResize:false animated:false]; } else if ([photoEditor hasDefaultCropping] && ABS(_autoRotationAngle) > FLT_EPSILON) @@ -376,16 +344,11 @@ NSString * const TGPhotoCropOriginalAspectRatio = @"original"; - (CGRect)_targetFrameForTransitionInFromFrame:(CGRect)fromFrame { CGSize referenceSize = [self referenceViewSize]; - - UIInterfaceOrientation orientation = self.interfaceOrientation; - if ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPad) - orientation = UIInterfaceOrientationPortrait; - bool hasOnScreenNavigation = false; if (iosMajorVersion() >= 11) hasOnScreenNavigation = (self.viewLoaded && self.view.safeAreaInsets.bottom > FLT_EPSILON) || _context.safeAreaInset.bottom > FLT_EPSILON; - CGRect containerFrame = [TGPhotoCropController photoContainerFrameForParentViewFrame:CGRectMake(0, 0, referenceSize.width, referenceSize.height) toolbarLandscapeSize:self.toolbarLandscapeSize orientation:orientation hasArbitraryRotation:_cropView.hasArbitraryRotation hasOnScreenNavigation:hasOnScreenNavigation]; + CGRect containerFrame = [TGPhotoCropController photoContainerFrameForParentViewFrame:CGRectMake(0, 0, referenceSize.width, referenceSize.height) toolbarLandscapeSize:self.toolbarLandscapeSize orientation:self.effectiveOrientation hasArbitraryRotation:_cropView.hasArbitraryRotation hasOnScreenNavigation:hasOnScreenNavigation]; containerFrame = CGRectInset(containerFrame, TGPhotoCropAreaInsetSize.width, TGPhotoCropAreaInsetSize.height); CGSize fittedSize = TGScaleToSize(fromFrame.size, containerFrame.size); @@ -576,7 +539,6 @@ NSString * const TGPhotoCropOriginalAspectRatio = @"original"; if (_cropView.isAspectRatioLocked) { [_cropView unlockAspectRatio]; - _aspectRatioButton.selected = false; } else { @@ -677,14 +639,14 @@ NSString * const TGPhotoCropOriginalAspectRatio = @"original"; }]]; [controller setItemViews:items]; - controller.sourceRect = ^CGRect - { - __strong TGPhotoCropController *strongSelf = weakSelf; - if (strongSelf != nil) - return [strongSelf.view convertRect:strongSelf->_aspectRatioButton.frame fromView:strongSelf->_aspectRatioButton.superview]; - - return CGRectZero; - }; +// controller.sourceRect = ^CGRect +// { +// __strong TGPhotoCropController *strongSelf = weakSelf; +// if (strongSelf != nil) +// return [strongSelf.view convertRect:strongSelf->_aspectRatioButton.frame fromView:strongSelf->_aspectRatioButton.superview]; +// +// return CGRectZero; +// }; [controller presentInViewController:self.parentViewController sourceView:self.view animated:true]; } @@ -706,8 +668,6 @@ NSString * const TGPhotoCropOriginalAspectRatio = @"original"; } else { - _aspectRatioButton.selected = false; - [_cropView resetAnimated:true]; if (hasAutorotationAngle) @@ -796,8 +756,7 @@ NSString * const TGPhotoCropOriginalAspectRatio = @"original"; - (void)updateLayout:(UIInterfaceOrientation)orientation { - if ([self inFormSheet] || [UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPad) - orientation = UIInterfaceOrientationPortrait; + orientation = [self effectiveOrientation:orientation]; CGSize referenceSize = [self referenceViewSize]; @@ -823,19 +782,8 @@ NSString * const TGPhotoCropOriginalAspectRatio = @"original"; { case UIInterfaceOrientationLandscapeLeft: { - _buttonsWrapperView.frame = CGRectMake(screenEdges.left + self.toolbarLandscapeSize, - screenEdges.top, - TGPhotoCropButtonsWrapperSize, - referenceSize.height); - - _rotateButton.frame = CGRectMake(25, 10, _rotateButton.frame.size.width, _rotateButton.frame.size.height); - _mirrorButton.frame = CGRectMake(25, 60, _mirrorButton.frame.size.width, _mirrorButton.frame.size.height); - - _aspectRatioButton.frame = CGRectMake(25, - _buttonsWrapperView.frame.size.height - _aspectRatioButton.frame.size.height - 10, - _aspectRatioButton.frame.size.width, - _aspectRatioButton.frame.size.height); - + _buttonsWrapperView.frame = CGRectMake(screenEdges.left + self.toolbarLandscapeSize, screenEdges.top, TGPhotoCropButtonsWrapperSize, referenceSize.height); + _resetButton.transform = CGAffineTransformIdentity; _resetButton.frame = CGRectMake(0, 0, _resetButtonWidth, 24); @@ -852,19 +800,8 @@ NSString * const TGPhotoCropOriginalAspectRatio = @"original"; case UIInterfaceOrientationLandscapeRight: { - _buttonsWrapperView.frame = CGRectMake(screenEdges.right - self.toolbarLandscapeSize - TGPhotoCropButtonsWrapperSize, - screenEdges.top, - TGPhotoCropButtonsWrapperSize, - referenceSize.height); - - _rotateButton.frame = CGRectMake(_buttonsWrapperView.frame.size.width - _rotateButton.frame.size.width - 25, 10, _rotateButton.frame.size.width, _rotateButton.frame.size.height); - _mirrorButton.frame = CGRectMake(_buttonsWrapperView.frame.size.width - _mirrorButton.frame.size.width - 25, 60, _mirrorButton.frame.size.width, _mirrorButton.frame.size.height); - - _aspectRatioButton.frame = CGRectMake(_buttonsWrapperView.frame.size.width - _aspectRatioButton.frame.size.width - 25, - _buttonsWrapperView.frame.size.height - _aspectRatioButton.frame.size.height - 10, - _aspectRatioButton.frame.size.width, - _aspectRatioButton.frame.size.height); - + _buttonsWrapperView.frame = CGRectMake(screenEdges.right - self.toolbarLandscapeSize - TGPhotoCropButtonsWrapperSize, screenEdges.top, TGPhotoCropButtonsWrapperSize, referenceSize.height); + _resetButton.transform = CGAffineTransformIdentity; _resetButton.frame = CGRectMake(0, 0, _resetButtonWidth, 24); @@ -881,18 +818,7 @@ NSString * const TGPhotoCropOriginalAspectRatio = @"original"; default: { - _buttonsWrapperView.frame = CGRectMake(screenEdges.left, - screenEdges.bottom - TGPhotoEditorToolbarSize - TGPhotoCropButtonsWrapperSize, - referenceSize.width, - TGPhotoCropButtonsWrapperSize); - - _rotateButton.frame = CGRectMake(10, _buttonsWrapperView.frame.size.height - _rotateButton.frame.size.height - 25, _rotateButton.frame.size.width, _rotateButton.frame.size.height); - _mirrorButton.frame = CGRectMake(60, _buttonsWrapperView.frame.size.height - _mirrorButton.frame.size.height - 25, _mirrorButton.frame.size.width, _mirrorButton.frame.size.height); - - _aspectRatioButton.frame = CGRectMake(_buttonsWrapperView.frame.size.width - _aspectRatioButton.frame.size.width - 10, - _buttonsWrapperView.frame.size.height - _aspectRatioButton.frame.size.height - 25, - _aspectRatioButton.frame.size.width, - _aspectRatioButton.frame.size.height); + _buttonsWrapperView.frame = CGRectMake(screenEdges.left, screenEdges.bottom - TGPhotoEditorToolbarSize - TGPhotoCropButtonsWrapperSize, referenceSize.width, TGPhotoCropButtonsWrapperSize); _resetButton.transform = CGAffineTransformIdentity; _resetButton.frame = CGRectMake((_buttonsWrapperView.frame.size.width - _resetButton.frame.size.width) / 2, 20, _resetButtonWidth, 24); diff --git a/submodules/LegacyComponents/Sources/TGPhotoEditorController.m b/submodules/LegacyComponents/Sources/TGPhotoEditorController.m index ec88dc6b3b..aa421d2cd3 100644 --- a/submodules/LegacyComponents/Sources/TGPhotoEditorController.m +++ b/submodules/LegacyComponents/Sources/TGPhotoEditorController.m @@ -72,12 +72,12 @@ UIImage *_thumbnailImage; AVPlayerItem *_playerItem; - AVPlayer *_player; SMetaDisposable *_playerItemDisposable; id _playerStartedObserver; id _playerReachedEndObserver; bool _registeredKeypathObserver; NSTimer *_positionTimer; + bool _scheduledVideoPlayback; id _initialAdjustments; NSString *_caption; @@ -260,14 +260,6 @@ TGPhotoEditorBackButton backButton = TGPhotoEditorBackButtonCancel; - if ([self presentedForAvatarCreation]) - { - if ([self presentedFromCamera]) - backButton = TGPhotoEditorBackButtonCancel; - else - backButton = TGPhotoEditorBackButtonCancel; - } - TGPhotoEditorDoneButton doneButton = TGPhotoEditorDoneButtonCheck; _portraitToolbarView = [[TGPhotoToolbarView alloc] initWithBackButton:backButton doneButton:doneButton solidBackground:true]; [_portraitToolbarView setToolbarTabs:_availableTabs animated:false]; @@ -403,8 +395,11 @@ [signal startWithNext:^(id next) { + CGFloat progress = 0.0; + bool progressVisible = false; if ([next isKindOfClass:[UIImage class]]) { [_photoEditor setImage:(UIImage *)next forCropRect:_photoEditor.cropRect cropRotation:_photoEditor.cropRotation cropOrientation:_photoEditor.cropOrientation cropMirrored:_photoEditor.cropMirrored fullSize:false]; + progress = 1.0f; } else if ([next isKindOfClass:[AVAsset class]]) { _playerItem = [AVPlayerItem playerItemWithAsset:(AVAsset *)next]; _player = [AVPlayer playerWithPlayerItem:_playerItem]; @@ -421,9 +416,22 @@ [_previewView performTransitionInWithCompletion:^ { }]; + + if (_scheduledVideoPlayback) { + _scheduledVideoPlayback = false; + [self startVideoPlayback:true]; + } }); + progress = 1.0f; + } else if ([next isKindOfClass:[NSNumber class]]) { + progress = [next floatValue]; + progressVisible = true; } + TGDispatchOnMainThread(^{ + [self setProgressVisible:progressVisible value:progress animated:true]; + }); + if (_ignoreDefaultPreviewViewTransitionIn) { TGDispatchOnMainThread(^ @@ -496,6 +504,11 @@ } - (void)startVideoPlayback:(bool)reset { + if (reset && _player == nil) { + _scheduledVideoPlayback = true; + return; + } + if (reset) { NSTimeInterval startPosition = 0.0f; if (_photoEditor.trimStartValue > DBL_EPSILON) @@ -973,6 +986,9 @@ _switchingTab = true; + TGPhotoEditorBackButton backButtonType = TGPhotoEditorBackButtonCancel; + TGPhotoEditorDoneButton doneButtonType = TGPhotoEditorDoneButtonCheck; + __weak TGPhotoEditorController *weakSelf = self; TGPhotoEditorTabController *controller = nil; switch (tab) @@ -1016,7 +1032,7 @@ strongSelf->_switchingTab = false; [strongSelf startVideoPlayback:true]; }; - + controller = paintController; } break; @@ -1358,6 +1374,8 @@ [strongSelf setVideoEndTime:endTime]; }; controller = previewController; + + doneButtonType = TGPhotoEditorDoneButtonDone; } break; @@ -1365,6 +1383,10 @@ break; } + if (_intent == TGPhotoEditorControllerAvatarIntent && !isInitialAppearance && tab != TGPhotoEditorPreviewTab) { + backButtonType = TGPhotoEditorBackButtonBack; + } + _currentTabController = controller; _currentTabController.item = _item; _currentTabController.intent = _intent; @@ -1381,12 +1403,12 @@ if (currentController != nil) [_currentTabController viewWillAppear:true]; + + _currentTabController.view.frame = _containerView.bounds; if (currentController != nil) [_currentTabController viewDidAppear:true]; - _currentTabController.view.frame = _containerView.bounds; - _currentTabController.valuesChanged = ^ { __strong TGPhotoEditorController *strongSelf = weakSelf; @@ -1405,6 +1427,12 @@ [_portraitToolbarView setToolbarTabs:[_currentTabController availableTabs] animated:true]; [_landscapeToolbarView setToolbarTabs:[_currentTabController availableTabs] animated:true]; + [_portraitToolbarView setBackButtonType:backButtonType]; + [_landscapeToolbarView setBackButtonType:backButtonType]; + + [_portraitToolbarView setDoneButtonType:doneButtonType]; + [_landscapeToolbarView setDoneButtonType:doneButtonType]; + [self updateEditorButtons]; if ([self respondsToSelector:@selector(setNeedsUpdateOfScreenEdgesDeferringSystemGestures)]) @@ -1474,10 +1502,7 @@ - (void)dismissEditor { - if ([_currentTabController isKindOfClass:[TGPhotoAvatarPreviewController class]]) { - [self presentEditorTab:TGPhotoEditorCropTab]; - return; - } else if (![_currentTabController isKindOfClass:[TGPhotoAvatarCropController class]] && _intent == TGPhotoEditorControllerAvatarIntent) { + if ((![_currentTabController isKindOfClass:[TGPhotoAvatarPreviewController class]] && ![_currentTabController isKindOfClass:[TGPhotoAvatarCropController class]]) && _intent == TGPhotoEditorControllerAvatarIntent) { [self presentEditorTab:TGPhotoEditorPreviewTab]; return; } @@ -1570,9 +1595,7 @@ - (void)doneButtonPressed { - if ([_currentTabController isKindOfClass:[TGPhotoAvatarCropController class]]) { - [self presentEditorTab:TGPhotoEditorPreviewTab]; - } else if (_intent == TGPhotoEditorControllerAvatarIntent && ![_currentTabController isKindOfClass:[TGPhotoAvatarPreviewController class]]) { + if (_intent == TGPhotoEditorControllerAvatarIntent && ![_currentTabController isKindOfClass:[TGPhotoAvatarPreviewController class]]) { [self presentEditorTab:TGPhotoEditorPreviewTab]; } else { [self applyEditor]; @@ -1895,7 +1918,7 @@ - (void)dismiss { - if (self.overlayWindow != nil) + if (self.overlayWindow != nil || self.customDismissBlock != nil) { [super dismiss]; } diff --git a/submodules/LegacyComponents/Sources/TGPhotoEditorTabController.m b/submodules/LegacyComponents/Sources/TGPhotoEditorTabController.m index 3cf549c575..53652e29eb 100644 --- a/submodules/LegacyComponents/Sources/TGPhotoEditorTabController.m +++ b/submodules/LegacyComponents/Sources/TGPhotoEditorTabController.m @@ -82,6 +82,13 @@ const CGFloat TGPhotoEditorToolbarSize = 49.0f; } } +- (bool)hasOnScreenNavigation { + bool hasOnScreenNavigation = false; + if (iosMajorVersion() >= 11) + hasOnScreenNavigation = (self.viewLoaded && self.view.safeAreaInsets.bottom > FLT_EPSILON) || self.context.safeAreaInset.bottom > FLT_EPSILON; + return hasOnScreenNavigation; +} + - (UIInterfaceOrientation)effectiveOrientation { return [self effectiveOrientation:self.interfaceOrientation]; } diff --git a/submodules/LegacyComponents/Sources/TGPhotoPaintController.m b/submodules/LegacyComponents/Sources/TGPhotoPaintController.m index c6ae14b4ad..3c5e2d2709 100644 --- a/submodules/LegacyComponents/Sources/TGPhotoPaintController.m +++ b/submodules/LegacyComponents/Sources/TGPhotoPaintController.m @@ -1830,16 +1830,7 @@ const CGFloat TGPhotoPaintStickerKeyboardSize = 260.0f; - (CGRect)_targetFrameForTransitionInFromFrame:(CGRect)fromFrame { CGSize referenceSize = [self referenceViewSize]; - UIInterfaceOrientation orientation = self.interfaceOrientation; - - if ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPad) - orientation = UIInterfaceOrientationPortrait; - - bool hasOnScreenNavigation = false; - if (iosMajorVersion() >= 11) - hasOnScreenNavigation = (self.viewLoaded && self.view.safeAreaInsets.bottom > FLT_EPSILON) || _context.safeAreaInset.bottom > FLT_EPSILON; - - CGRect containerFrame = [TGPhotoPaintController photoContainerFrameForParentViewFrame:CGRectMake(0, 0, referenceSize.width, referenceSize.height) toolbarLandscapeSize:self.toolbarLandscapeSize orientation:orientation panelSize:TGPhotoPaintTopPanelSize + TGPhotoPaintBottomPanelSize hasOnScreenNavigation:hasOnScreenNavigation]; + CGRect containerFrame = [TGPhotoPaintController photoContainerFrameForParentViewFrame:CGRectMake(0, 0, referenceSize.width, referenceSize.height) toolbarLandscapeSize:self.toolbarLandscapeSize orientation:self.effectiveOrientation panelSize:TGPhotoPaintTopPanelSize + TGPhotoPaintBottomPanelSize hasOnScreenNavigation:self.hasOnScreenNavigation]; CGSize fittedSize = TGScaleToSize(fromFrame.size, containerFrame.size); CGRect toFrame = CGRectMake(containerFrame.origin.x + (containerFrame.size.width - fittedSize.width) / 2, containerFrame.origin.y + (containerFrame.size.height - fittedSize.height) / 2, fittedSize.width, fittedSize.height); @@ -1920,11 +1911,7 @@ const CGFloat TGPhotoPaintStickerKeyboardSize = 260.0f; - (CGRect)transitionOutSourceFrameForReferenceFrame:(CGRect)referenceFrame orientation:(UIInterfaceOrientation)orientation { - bool hasOnScreenNavigation = false; - if (iosMajorVersion() >= 11) - hasOnScreenNavigation = (self.viewLoaded && self.view.safeAreaInsets.bottom > FLT_EPSILON) || _context.safeAreaInset.bottom > FLT_EPSILON; - - CGRect containerFrame = [TGPhotoPaintController photoContainerFrameForParentViewFrame:self.view.frame toolbarLandscapeSize:self.toolbarLandscapeSize orientation:orientation panelSize:TGPhotoPaintTopPanelSize + TGPhotoPaintBottomPanelSize hasOnScreenNavigation:hasOnScreenNavigation]; + CGRect containerFrame = [TGPhotoPaintController photoContainerFrameForParentViewFrame:self.view.frame toolbarLandscapeSize:self.toolbarLandscapeSize orientation:orientation panelSize:TGPhotoPaintTopPanelSize + TGPhotoPaintBottomPanelSize hasOnScreenNavigation:self.hasOnScreenNavigation]; CGSize fittedSize = TGScaleToSize(referenceFrame.size, containerFrame.size); return CGRectMake(containerFrame.origin.x + (containerFrame.size.width - fittedSize.width) / 2, containerFrame.origin.y + (containerFrame.size.height - fittedSize.height) / 2, fittedSize.width, fittedSize.height); @@ -1942,15 +1929,8 @@ const CGFloat TGPhotoPaintStickerKeyboardSize = 260.0f; TGPhotoEditorPreviewView *previewView = self.previewView; [previewView prepareForTransitionOut]; - bool hasOnScreenNavigation = false; - if (iosMajorVersion() >= 11) - hasOnScreenNavigation = (self.viewLoaded && self.view.safeAreaInsets.bottom > FLT_EPSILON) || _context.safeAreaInset.bottom > FLT_EPSILON; - - UIInterfaceOrientation orientation = self.interfaceOrientation; - if ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPad) - orientation = UIInterfaceOrientationPortrait; - - CGRect containerFrame = [TGPhotoPaintController photoContainerFrameForParentViewFrame:self.view.frame toolbarLandscapeSize:self.toolbarLandscapeSize orientation:orientation panelSize:TGPhotoPaintTopPanelSize + TGPhotoPaintBottomPanelSize hasOnScreenNavigation:hasOnScreenNavigation]; + UIInterfaceOrientation orientation = self.effectiveOrientation; + CGRect containerFrame = [TGPhotoPaintController photoContainerFrameForParentViewFrame:self.view.frame toolbarLandscapeSize:self.toolbarLandscapeSize orientation:orientation panelSize:TGPhotoPaintTopPanelSize + TGPhotoPaintBottomPanelSize hasOnScreenNavigation:self.hasOnScreenNavigation]; CGRect referenceFrame = CGRectMake(0, 0, self.photoEditor.rotatedCropSize.width, self.photoEditor.rotatedCropSize.height); CGRect rect = CGRectOffset([self transitionOutSourceFrameForReferenceFrame:referenceFrame orientation:orientation], -containerFrame.origin.x, -containerFrame.origin.y); previewView.frame = rect; @@ -2182,19 +2162,15 @@ const CGFloat TGPhotoPaintStickerKeyboardSize = 260.0f; CGFloat panelToolbarPortraitSize = TGPhotoPaintBottomPanelSize + TGPhotoEditorToolbarSize; CGFloat panelToolbarLandscapeSize = TGPhotoPaintBottomPanelSize + self.toolbarLandscapeSize; - - bool hasOnScreenNavigation = false; - if (iosMajorVersion() >= 11) - hasOnScreenNavigation = (self.viewLoaded && self.view.safeAreaInsets.bottom > FLT_EPSILON) || _context.safeAreaInset.bottom > FLT_EPSILON; - - UIEdgeInsets safeAreaInset = [TGViewController safeAreaInsetForOrientation:orientation hasOnScreenNavigation:hasOnScreenNavigation]; + + UIEdgeInsets safeAreaInset = [TGViewController safeAreaInsetForOrientation:orientation hasOnScreenNavigation:self.hasOnScreenNavigation]; UIEdgeInsets screenEdges = UIEdgeInsetsMake((screenSide - referenceSize.height) / 2, (screenSide - referenceSize.width) / 2, (screenSide + referenceSize.height) / 2, (screenSide + referenceSize.width) / 2); screenEdges.top += safeAreaInset.top; screenEdges.left += safeAreaInset.left; screenEdges.bottom -= safeAreaInset.bottom; screenEdges.right -= safeAreaInset.right; - CGRect containerFrame = [TGPhotoPaintController photoContainerFrameForParentViewFrame:CGRectMake(0, 0, referenceSize.width, referenceSize.height) toolbarLandscapeSize:self.toolbarLandscapeSize orientation:orientation panelSize:TGPhotoPaintTopPanelSize + TGPhotoPaintBottomPanelSize hasOnScreenNavigation:hasOnScreenNavigation]; + CGRect containerFrame = [TGPhotoPaintController photoContainerFrameForParentViewFrame:CGRectMake(0, 0, referenceSize.width, referenceSize.height) toolbarLandscapeSize:self.toolbarLandscapeSize orientation:orientation panelSize:TGPhotoPaintTopPanelSize + TGPhotoPaintBottomPanelSize hasOnScreenNavigation:self.hasOnScreenNavigation]; _settingsViewWrapper.frame = self.parentViewController.view.bounds; @@ -2361,18 +2337,10 @@ const CGFloat TGPhotoPaintStickerKeyboardSize = 260.0f; - (void)keyboardHeightChangedTo:(CGFloat)height duration:(NSTimeInterval)duration curve:(NSInteger)curve { - UIInterfaceOrientation orientation = self.interfaceOrientation; - if ([self inFormSheet] || [UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPad) - orientation = UIInterfaceOrientationPortrait; - - bool hasOnScreenNavigation = false; - if (iosMajorVersion() >= 11) - hasOnScreenNavigation = (self.viewLoaded && self.view.safeAreaInsets.bottom > FLT_EPSILON) || _context.safeAreaInset.bottom > FLT_EPSILON; - CGSize referenceSize = [self referenceViewSize]; CGFloat screenSide = MAX(referenceSize.width, referenceSize.height) + 2 * TGPhotoPaintBottomPanelSize; - CGRect containerFrame = [TGPhotoPaintController photoContainerFrameForParentViewFrame:CGRectMake(0, 0, referenceSize.width, referenceSize.height) toolbarLandscapeSize:self.toolbarLandscapeSize orientation:orientation panelSize:TGPhotoPaintTopPanelSize + TGPhotoPaintBottomPanelSize hasOnScreenNavigation:hasOnScreenNavigation]; + CGRect containerFrame = [TGPhotoPaintController photoContainerFrameForParentViewFrame:CGRectMake(0, 0, referenceSize.width, referenceSize.height) toolbarLandscapeSize:self.toolbarLandscapeSize orientation:self.effectiveOrientation panelSize:TGPhotoPaintTopPanelSize + TGPhotoPaintBottomPanelSize hasOnScreenNavigation:self.hasOnScreenNavigation]; CGFloat visibleArea = self.view.frame.size.height - height; CGFloat yCenter = visibleArea / 2.0f; diff --git a/submodules/LegacyComponents/Sources/TGPhotoToolbarView.m b/submodules/LegacyComponents/Sources/TGPhotoToolbarView.m index adcd430859..1a3b0d64b2 100644 --- a/submodules/LegacyComponents/Sources/TGPhotoToolbarView.m +++ b/submodules/LegacyComponents/Sources/TGPhotoToolbarView.m @@ -42,49 +42,19 @@ _buttonsWrapperView = [[UIView alloc] initWithFrame:_backgroundView.bounds]; [_backgroundView addSubview:_buttonsWrapperView]; - _cancelButton = [[TGModernButton alloc] initWithFrame:CGRectMake(0, 0, 49, 49)]; + CGSize buttonSize = CGSizeMake(49.0f, 49.0f); + _cancelButton = [[TGModernButton alloc] initWithFrame:CGRectMake(0, 0, buttonSize.width, buttonSize.height)]; _cancelButton.exclusiveTouch = true; _cancelButton.adjustsImageWhenHighlighted = false; - - UIImage *cancelImage = nil; - switch (backButton) - { - case TGPhotoEditorBackButtonCancel: - cancelImage = TGTintedImage([UIImage imageNamed:@"Editor/Cancel"], [UIColor whiteColor]); - break; - - default: - cancelImage = TGComponentsImageNamed(@"PhotoPickerBackIcon"); - break; - } - [_cancelButton setImage:cancelImage forState:UIControlStateNormal]; + [self setBackButtonType:backButton]; [_cancelButton addTarget:self action:@selector(cancelButtonPressed) forControlEvents:UIControlEventTouchUpInside]; [_backgroundView addSubview:_cancelButton]; - UIImage *doneImage = nil; - CGSize buttonSize = CGSizeMake(49.0f, 49.0f); - switch (doneButton) - { - case TGPhotoEditorDoneButtonCheck: - doneImage = TGTintedImage([UIImage imageNamed:@"Editor/Commit"], [UIColor whiteColor]); - break; - - default: - { - TGMediaAssetsPallete *pallete = nil; - if ([[LegacyComponentsGlobals provider] respondsToSelector:@selector(mediaAssetsPallete)]) - pallete = [[LegacyComponentsGlobals provider] mediaAssetsPallete]; - - doneImage = pallete != nil ? pallete.sendIconImage : TGComponentsImageNamed(@"PhotoPickerSendIcon"); - } - break; - } _doneButton = [[TGModernButton alloc] initWithFrame:CGRectMake(0, 0, buttonSize.width, buttonSize.height)]; _doneButton.exclusiveTouch = true; _doneButton.adjustsImageWhenHighlighted = false; - - [_doneButton setImage:doneImage forState:UIControlStateNormal]; + [self setDoneButtonType:doneButton]; [_doneButton addTarget:self action:@selector(doneButtonPressed) forControlEvents:UIControlEventTouchUpInside]; [_backgroundView addSubview:_doneButton]; @@ -95,6 +65,55 @@ return self; } +- (void)setBackButtonType:(TGPhotoEditorBackButton)backButtonType { + _backButtonType = backButtonType; + + UIImage *cancelImage = nil; + switch (backButtonType) + { + case TGPhotoEditorBackButtonCancel: + cancelImage = TGTintedImage([UIImage imageNamed:@"Editor/Cancel"], [UIColor whiteColor]); + break; + + default: + cancelImage = TGComponentsImageNamed(@"PhotoPickerBackIcon"); + break; + } + [_cancelButton setImage:cancelImage forState:UIControlStateNormal]; +} + +- (void)setDoneButtonType:(TGPhotoEditorDoneButton)doneButtonType { + _doneButtonType = doneButtonType; + + UIImage *doneImage; + switch (doneButtonType) + { + case TGPhotoEditorDoneButtonCheck: + doneImage = TGTintedImage([UIImage imageNamed:@"Editor/Commit"], [UIColor whiteColor]); + break; + + case TGPhotoEditorDoneButtonDone: + { + TGMediaAssetsPallete *pallete = nil; + if ([[LegacyComponentsGlobals provider] respondsToSelector:@selector(mediaAssetsPallete)]) + pallete = [[LegacyComponentsGlobals provider] mediaAssetsPallete]; + + doneImage = pallete != nil ? pallete.doneIconImage : TGTintedImage([UIImage imageNamed:@"Editor/Commit"], [UIColor whiteColor]); + break; + } + default: + { + TGMediaAssetsPallete *pallete = nil; + if ([[LegacyComponentsGlobals provider] respondsToSelector:@selector(mediaAssetsPallete)]) + pallete = [[LegacyComponentsGlobals provider] mediaAssetsPallete]; + + doneImage = pallete != nil ? pallete.sendIconImage : TGComponentsImageNamed(@"PhotoPickerSendIcon"); + } + break; + } + [_doneButton setImage:doneImage forState:UIControlStateNormal]; +} + - (UIButton *)doneButton { return _doneButton; diff --git a/submodules/LegacyComponents/Sources/TGPhotoToolsController.m b/submodules/LegacyComponents/Sources/TGPhotoToolsController.m index 7994165e0e..13c715e120 100644 --- a/submodules/LegacyComponents/Sources/TGPhotoToolsController.m +++ b/submodules/LegacyComponents/Sources/TGPhotoToolsController.m @@ -30,6 +30,7 @@ const CGFloat TGPhotoEditorToolsLandscapePanelSize = TGPhotoEditorToolsPanelSize { NSValue *_contentOffsetAfterRotation; bool _appeared; + bool _scheduledTransitionIn; CGFloat _cellWidth; NSArray *_allTools; @@ -306,17 +307,17 @@ const CGFloat TGPhotoEditorToolsLandscapePanelSize = TGPhotoEditorToolsPanelSize - (void)transitionIn { + if (_portraitToolsWrapperView.frame.size.height < FLT_EPSILON) { + _scheduledTransitionIn = true; + return; + } [UIView animateWithDuration:0.3f animations:^ { _portraitToolsWrapperView.alpha = 1.0f; _landscapeToolsWrapperView.alpha = 1.0f; }]; - UIInterfaceOrientation orientation = self.interfaceOrientation; - if ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPad) - orientation = UIInterfaceOrientationPortrait; - - switch (orientation) + switch (self.effectiveOrientation) { case UIInterfaceOrientationLandscapeLeft: { @@ -350,8 +351,12 @@ const CGFloat TGPhotoEditorToolsLandscapePanelSize = TGPhotoEditorToolsPanelSize } } -- (void)transitionOutSwitching:(bool)__unused switching completion:(void (^)(void))completion +- (void)transitionOutSwitching:(bool)switching completion:(void (^)(void))completion { + if (switching) { + _dismissing = true; + } + TGPhotoEditorPreviewView *previewView = self.previewView; previewView.touchedUp = nil; previewView.touchedDown = nil; @@ -360,11 +365,7 @@ const CGFloat TGPhotoEditorToolsLandscapePanelSize = TGPhotoEditorToolsPanelSize [_toolAreaView.superview bringSubviewToFront:_toolAreaView]; - UIInterfaceOrientation orientation = self.interfaceOrientation; - if ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPad) - orientation = UIInterfaceOrientationPortrait; - - switch (orientation) + switch (self.effectiveOrientation) { case UIInterfaceOrientationLandscapeLeft: { @@ -769,17 +770,18 @@ const CGFloat TGPhotoEditorToolsLandscapePanelSize = TGPhotoEditorToolsPanelSize [self updateLayout:[[LegacyComponentsGlobals provider] applicationStatusBarOrientation]]; + if (_scheduledTransitionIn) { + _scheduledTransitionIn = false; + [self transitionIn]; + } + if (![self inFormSheet]) [self _applyPreparedContentOffset]; } - (CGRect)transitionOutSourceFrameForReferenceFrame:(CGRect)referenceFrame orientation:(UIInterfaceOrientation)orientation { - bool hasOnScreenNavigation = false; - if (iosMajorVersion() >= 11) - hasOnScreenNavigation = (self.viewLoaded && self.view.safeAreaInsets.bottom > FLT_EPSILON) || self.context.safeAreaInset.bottom > FLT_EPSILON; - - CGRect containerFrame = [TGPhotoToolsController photoContainerFrameForParentViewFrame:self.view.frame toolbarLandscapeSize:self.toolbarLandscapeSize orientation:orientation panelSize:TGPhotoEditorPanelSize hasOnScreenNavigation:hasOnScreenNavigation]; + CGRect containerFrame = [TGPhotoToolsController photoContainerFrameForParentViewFrame:self.view.frame toolbarLandscapeSize:self.toolbarLandscapeSize orientation:orientation panelSize:TGPhotoEditorPanelSize hasOnScreenNavigation:self.hasOnScreenNavigation]; CGSize fittedSize = TGScaleToSize(referenceFrame.size, containerFrame.size); CGRect sourceFrame = CGRectMake(containerFrame.origin.x + (containerFrame.size.width - fittedSize.width) / 2, containerFrame.origin.y + (containerFrame.size.height - fittedSize.height) / 2, fittedSize.width, fittedSize.height); @@ -789,16 +791,7 @@ const CGFloat TGPhotoEditorToolsLandscapePanelSize = TGPhotoEditorToolsPanelSize - (CGRect)_targetFrameForTransitionInFromFrame:(CGRect)fromFrame { CGSize referenceSize = [self referenceViewSize]; - UIInterfaceOrientation orientation = self.interfaceOrientation; - - if ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPad) - orientation = UIInterfaceOrientationPortrait; - - bool hasOnScreenNavigation = false; - if (iosMajorVersion() >= 11) - hasOnScreenNavigation = (self.viewLoaded && self.view.safeAreaInsets.bottom > FLT_EPSILON) || self.context.safeAreaInset.bottom > FLT_EPSILON; - - CGRect containerFrame = [TGPhotoToolsController photoContainerFrameForParentViewFrame:CGRectMake(0, 0, referenceSize.width, referenceSize.height) toolbarLandscapeSize:self.toolbarLandscapeSize orientation:orientation panelSize:TGPhotoEditorPanelSize hasOnScreenNavigation:hasOnScreenNavigation]; + CGRect containerFrame = [TGPhotoToolsController photoContainerFrameForParentViewFrame:CGRectMake(0, 0, referenceSize.width, referenceSize.height) toolbarLandscapeSize:self.toolbarLandscapeSize orientation:self.effectiveOrientation panelSize:TGPhotoEditorPanelSize hasOnScreenNavigation:self.hasOnScreenNavigation]; CGSize fittedSize = TGScaleToSize(fromFrame.size, containerFrame.size); CGRect toFrame = CGRectMake(containerFrame.origin.x + (containerFrame.size.width - fittedSize.width) / 2, containerFrame.origin.y + (containerFrame.size.height - fittedSize.height) / 2, fittedSize.width, fittedSize.height); @@ -831,11 +824,7 @@ const CGFloat TGPhotoEditorToolsLandscapePanelSize = TGPhotoEditorToolsPanelSize CGFloat panelToolbarPortraitSize = panelSize + TGPhotoEditorToolbarSize; CGFloat panelToolbarLandscapeSize = panelSize + TGPhotoEditorToolbarSize; - bool hasOnScreenNavigation = false; - if (iosMajorVersion() >= 11) - hasOnScreenNavigation = (self.viewLoaded && self.view.safeAreaInsets.bottom > FLT_EPSILON) || self.context.safeAreaInset.bottom > FLT_EPSILON; - - UIEdgeInsets safeAreaInset = [TGViewController safeAreaInsetForOrientation:orientation hasOnScreenNavigation:hasOnScreenNavigation]; + UIEdgeInsets safeAreaInset = [TGViewController safeAreaInsetForOrientation:orientation hasOnScreenNavigation:self.hasOnScreenNavigation]; UIEdgeInsets screenEdges = UIEdgeInsetsMake((screenSide - referenceSize.height) / 2, (screenSide - referenceSize.width) / 2, (screenSide + referenceSize.height) / 2, (screenSide + referenceSize.width) / 2); screenEdges.top += safeAreaInset.top; screenEdges.left += safeAreaInset.left; @@ -946,23 +935,14 @@ const CGFloat TGPhotoEditorToolsLandscapePanelSize = TGPhotoEditorToolsPanelSize - (void)updatePreviewView { - UIInterfaceOrientation orientation = self.interfaceOrientation; - if ([self inFormSheet] || TGIsPad()) - orientation = UIInterfaceOrientationPortrait; - - CGSize referenceSize = [self referenceViewSize]; - PGPhotoEditor *photoEditor = self.photoEditor; TGPhotoEditorPreviewView *previewView = self.previewView; if (_dismissing || previewView.superview != self.view) return; - bool hasOnScreenNavigation = false; - if (iosMajorVersion() >= 11) - hasOnScreenNavigation = (self.viewLoaded && self.view.safeAreaInsets.bottom > FLT_EPSILON) || self.context.safeAreaInset.bottom > FLT_EPSILON; - - CGRect containerFrame = _preview ? CGRectMake(0.0f, 0.0f, referenceSize.width, referenceSize.height) : [TGPhotoToolsController photoContainerFrameForParentViewFrame:CGRectMake(0, 0, referenceSize.width, referenceSize.height) toolbarLandscapeSize:self.toolbarLandscapeSize orientation:orientation panelSize:TGPhotoEditorPanelSize hasOnScreenNavigation:hasOnScreenNavigation]; + CGSize referenceSize = [self referenceViewSize]; + CGRect containerFrame = _preview ? CGRectMake(0.0f, 0.0f, referenceSize.width, referenceSize.height) : [TGPhotoToolsController photoContainerFrameForParentViewFrame:CGRectMake(0, 0, referenceSize.width, referenceSize.height) toolbarLandscapeSize:self.toolbarLandscapeSize orientation:self.effectiveOrientation panelSize:TGPhotoEditorPanelSize hasOnScreenNavigation:self.hasOnScreenNavigation]; CGSize fittedSize = TGScaleToSize(photoEditor.rotatedCropSize, containerFrame.size); previewView.frame = CGRectMake(containerFrame.origin.x + (containerFrame.size.width - fittedSize.width) / 2, containerFrame.origin.y + (containerFrame.size.height - fittedSize.height) / 2, fittedSize.width, fittedSize.height); diff --git a/submodules/LegacyComponents/Sources/TGVideoEditAdjustments.m b/submodules/LegacyComponents/Sources/TGVideoEditAdjustments.m index 2e886041ff..41dffa5608 100644 --- a/submodules/LegacyComponents/Sources/TGVideoEditAdjustments.m +++ b/submodules/LegacyComponents/Sources/TGVideoEditAdjustments.m @@ -9,7 +9,7 @@ #import "TGPhotoPaintStickerEntity.h" #import "TGPhotoPaintTextEntity.h" -const NSTimeInterval TGVideoEditMinimumTrimmableDuration = 1.0; +const NSTimeInterval TGVideoEditMinimumTrimmableDuration = 1.5; const NSTimeInterval TGVideoEditMaximumGifDuration = 30.5; @implementation TGVideoEditAdjustments diff --git a/submodules/LegacyMediaPickerUI/Sources/LegacyAvatarPicker.swift b/submodules/LegacyMediaPickerUI/Sources/LegacyAvatarPicker.swift index bed85c5bbf..5e364d0cba 100644 --- a/submodules/LegacyMediaPickerUI/Sources/LegacyAvatarPicker.swift +++ b/submodules/LegacyMediaPickerUI/Sources/LegacyAvatarPicker.swift @@ -14,7 +14,7 @@ public func presentLegacyAvatarPicker(holder: Atomic, signup: Bool, t let navigationController = makeLegacyNavigationController(rootController: emptyController) navigationController.setNavigationBarHidden(true, animated: false) navigationController.navigationBar.transform = CGAffineTransform(translationX: -1000.0, y: 0.0) - + legacyController.bind(controller: navigationController) present(legacyController, nil) @@ -27,7 +27,7 @@ public func presentLegacyAvatarPicker(holder: Atomic, signup: Bool, t } completion(image) } - mixin.didFinishWithView = { [weak legacyController] in + mixin.didFinishWithView = { openCurrent?() } mixin.didDismiss = { [weak legacyController] in diff --git a/submodules/LegacyUI/Sources/TelegramInitializeLegacyComponents.swift b/submodules/LegacyUI/Sources/TelegramInitializeLegacyComponents.swift index c037184a90..1c5d394d0a 100644 --- a/submodules/LegacyUI/Sources/TelegramInitializeLegacyComponents.swift +++ b/submodules/LegacyUI/Sources/TelegramInitializeLegacyComponents.swift @@ -346,7 +346,7 @@ private final class LegacyComponentsGlobalsProviderImpl: NSObject, LegacyCompone let navigationBar = presentationTheme.rootController.navigationBar let tabBar = presentationTheme.rootController.tabBar - return TGMediaAssetsPallete(dark: presentationTheme.overallDarkAppearance, backgroundColor: theme.plainBackgroundColor, selectionColor: theme.itemHighlightedBackgroundColor, separatorColor: theme.itemPlainSeparatorColor, textColor: theme.itemPrimaryTextColor, secondaryTextColor: theme.controlSecondaryColor, accentColor: theme.itemAccentColor, barBackgroundColor: tabBar.backgroundColor, barSeparatorColor: tabBar.separatorColor, navigationTitleColor: navigationBar.primaryTextColor, badge: generateStretchableFilledCircleImage(diameter: 22.0, color: navigationBar.accentTextColor), badgeTextColor: navigationBar.backgroundColor, sendIconImage: PresentationResourcesChat.chatInputPanelSendButtonImage(presentationTheme), maybeAccentColor: navigationBar.accentTextColor) + return TGMediaAssetsPallete(dark: presentationTheme.overallDarkAppearance, backgroundColor: theme.plainBackgroundColor, selectionColor: theme.itemHighlightedBackgroundColor, separatorColor: theme.itemPlainSeparatorColor, textColor: theme.itemPrimaryTextColor, secondaryTextColor: theme.controlSecondaryColor, accentColor: theme.itemAccentColor, barBackgroundColor: tabBar.backgroundColor, barSeparatorColor: tabBar.separatorColor, navigationTitleColor: navigationBar.primaryTextColor, badge: generateStretchableFilledCircleImage(diameter: 22.0, color: navigationBar.accentTextColor), badgeTextColor: navigationBar.backgroundColor, sendIconImage: PresentationResourcesChat.chatInputPanelSendButtonImage(presentationTheme), doneIconImage: PresentationResourcesChat.chatInputPanelApplyButtonImage(presentationTheme), maybeAccentColor: navigationBar.accentTextColor) } func checkButtonPallete() -> TGCheckButtonPallete! { diff --git a/submodules/PassportUI/Sources/SecureIdDocumentGalleryController.swift b/submodules/PassportUI/Sources/SecureIdDocumentGalleryController.swift index 2b6c8e9966..37c901c718 100644 --- a/submodules/PassportUI/Sources/SecureIdDocumentGalleryController.swift +++ b/submodules/PassportUI/Sources/SecureIdDocumentGalleryController.swift @@ -105,7 +105,7 @@ class SecureIdDocumentGalleryController: ViewController, StandalonePresentableCo $0.item(context: context, theme: strongSelf.presentationData.theme, strings: strongSelf.presentationData.strings, secureIdContext: strongSelf.secureIdContext, delete: { resource in self?.deleteItem(resource) }) - }), centralItemIndex: centralIndex, keepFirst: false) + }), centralItemIndex: centralIndex) let ready = (strongSelf.galleryNode.pager.ready() |> timeout(2.0, queue: Queue.mainQueue(), alternate: .single(Void()))) |> afterNext { [weak strongSelf] _ in strongSelf?.didSetReady = true diff --git a/submodules/PassportUI/Sources/SecureIdDocumentImageGalleryItem.swift b/submodules/PassportUI/Sources/SecureIdDocumentImageGalleryItem.swift index 52c88a88ec..3d3b692a44 100644 --- a/submodules/PassportUI/Sources/SecureIdDocumentImageGalleryItem.swift +++ b/submodules/PassportUI/Sources/SecureIdDocumentImageGalleryItem.swift @@ -39,7 +39,7 @@ class SecureIdDocumentGalleryItem: GalleryItem { self.delete = delete } - func node() -> GalleryItemNode { + func node(synchronous: Bool) -> GalleryItemNode { let node = SecureIdDocumentGalleryItemNode(context: self.context, theme: self.theme, strings: self.strings) node.setResource(secureIdContext: self.secureIdContext, resource: self.resource) @@ -52,7 +52,7 @@ class SecureIdDocumentGalleryItem: GalleryItem { return node } - func updateNode(node: GalleryItemNode) { + func updateNode(node: GalleryItemNode, synchronous: Bool) { if let node = node as? SecureIdDocumentGalleryItemNode { node._title.set(.single(self.strings.Items_NOfM("\(self.location.position + 1)", "\(self.location.totalCount)").0)) diff --git a/submodules/PeerAvatarGalleryUI/Sources/AvatarGalleryController.swift b/submodules/PeerAvatarGalleryUI/Sources/AvatarGalleryController.swift index 65deb7d9a2..e2032b666f 100644 --- a/submodules/PeerAvatarGalleryUI/Sources/AvatarGalleryController.swift +++ b/submodules/PeerAvatarGalleryUI/Sources/AvatarGalleryController.swift @@ -223,6 +223,7 @@ public class AvatarGalleryController: ViewController, StandalonePresentableContr self.disposable.set(combineLatest(entriesSignal, self.animatedIn.get()).start(next: { [weak self] entries, animatedIn in let f: () -> Void = { if let strongSelf = self, animatedIn { + let isFirstTime = strongSelf.entries.isEmpty strongSelf.entries = entries if strongSelf.centralEntryIndex == nil { strongSelf.centralEntryIndex = 0 @@ -248,7 +249,7 @@ public class AvatarGalleryController: ViewController, StandalonePresentableContr } : nil, setMain: { [weak self] in self?.setMainEntry(entry) }) - }), centralItemIndex: 0, keepFirst: false) + }), centralItemIndex: 0, synchronous: !isFirstTime) let ready = strongSelf.galleryNode.pager.ready() |> timeout(2.0, queue: Queue.mainQueue(), alternate: .single(Void())) |> afterNext { [weak strongSelf] _ in strongSelf?.didSetReady = true @@ -597,7 +598,7 @@ public class AvatarGalleryController: ViewController, StandalonePresentableContr } else { if let index = self.entries.firstIndex(of: entry) { self.entries.remove(at: index) - self.galleryNode.pager.transaction(GalleryPagerTransaction(deleteItems: [index], insertItems: [], updateItems: [], focusOnItem: index - 1)) + self.galleryNode.pager.transaction(GalleryPagerTransaction(deleteItems: [index], insertItems: [], updateItems: [], focusOnItem: index - 1, synchronous: false)) } } } @@ -611,7 +612,7 @@ public class AvatarGalleryController: ViewController, StandalonePresentableContr } else { if let index = self.entries.firstIndex(of: entry) { self.entries.remove(at: index) - self.galleryNode.pager.transaction(GalleryPagerTransaction(deleteItems: [index], insertItems: [], updateItems: [], focusOnItem: index - 1)) + self.galleryNode.pager.transaction(GalleryPagerTransaction(deleteItems: [index], insertItems: [], updateItems: [], focusOnItem: index - 1, synchronous: false)) } } } else { @@ -625,7 +626,7 @@ public class AvatarGalleryController: ViewController, StandalonePresentableContr } else { if let index = self.entries.firstIndex(of: entry) { self.entries.remove(at: index) - self.galleryNode.pager.transaction(GalleryPagerTransaction(deleteItems: [index], insertItems: [], updateItems: [], focusOnItem: index - 1)) + self.galleryNode.pager.transaction(GalleryPagerTransaction(deleteItems: [index], insertItems: [], updateItems: [], focusOnItem: index - 1, synchronous: false)) } } } diff --git a/submodules/PeerAvatarGalleryUI/Sources/PeerAvatarImageGalleryItem.swift b/submodules/PeerAvatarGalleryUI/Sources/PeerAvatarImageGalleryItem.swift index da24338d4c..a85bd3a5d2 100644 --- a/submodules/PeerAvatarGalleryUI/Sources/PeerAvatarImageGalleryItem.swift +++ b/submodules/PeerAvatarGalleryUI/Sources/PeerAvatarImageGalleryItem.swift @@ -64,28 +64,28 @@ class PeerAvatarImageGalleryItem: GalleryItem { self.delete = delete self.setMain = setMain } - - func node() -> GalleryItemNode { + + func node(synchronous: Bool) -> GalleryItemNode { let node = PeerAvatarImageGalleryItemNode(context: self.context, presentationData: self.presentationData, peer: self.peer, sourceHasRoundCorners: self.sourceHasRoundCorners) if let indexData = self.entry.indexData { node._title.set(.single(self.presentationData.strings.Items_NOfM("\(indexData.position + 1)", "\(indexData.totalCount)").0)) } - node.setEntry(self.entry) + node.setEntry(self.entry, synchronous: synchronous) node.footerContentNode.delete = self.delete node.footerContentNode.setMain = self.setMain return node } - func updateNode(node: GalleryItemNode) { + func updateNode(node: GalleryItemNode, synchronous: Bool) { if let node = node as? PeerAvatarImageGalleryItemNode { if let indexData = self.entry.indexData { node._title.set(.single(self.presentationData.strings.Items_NOfM("\(indexData.position + 1)", "\(indexData.totalCount)").0)) } - node.setEntry(self.entry) + node.setEntry(self.entry, synchronous: synchronous) node.footerContentNode.delete = self.delete node.footerContentNode.setMain = self.setMain } @@ -201,7 +201,7 @@ final class PeerAvatarImageGalleryItemNode: ZoomableContentGalleryItemNode { transition.updateFrame(node: self.statusNode, frame: CGRect(origin: CGPoint(), size: statusSize)) } - fileprivate func setEntry(_ entry: AvatarGalleryEntry) { + fileprivate func setEntry(_ entry: AvatarGalleryEntry, synchronous: Bool) { if self.entry != entry { self.entry = entry @@ -224,7 +224,7 @@ final class PeerAvatarImageGalleryItemNode: ZoomableContentGalleryItemNode { case let .image(_, _, imageRepresentations, _, _, _, _, _): representations = imageRepresentations } - self.imageNode.setSignal(chatAvatarGalleryPhoto(account: self.context.account, representations: representations), dispatchOnDisplayLink: false) + self.imageNode.setSignal(chatAvatarGalleryPhoto(account: self.context.account, representations: representations, attemptSynchronously: synchronous), attemptSynchronously: synchronous, dispatchOnDisplayLink: false) self.zoomableContent = (largestSize.dimensions.cgSize, self.contentNode) if let largestIndex = representations.firstIndex(where: { $0.representation == largestSize }) { @@ -278,8 +278,8 @@ final class PeerAvatarImageGalleryItemNode: ZoomableContentGalleryItemNode { } if let video = entry.videoRepresentations.last, let id = id { let mediaManager = self.context.sharedContext.mediaManager - let videoFileReference = FileMediaReference.standalone(media: TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: 0), partialReference: nil, resource: video.resource, previewRepresentations: representations.map { $0.representation }, videoThumbnails: [], immediateThumbnailData: nil, mimeType: "video/mp4", size: nil, attributes: [.Animated, .Video(duration: 0, size: video.dimensions, flags: [])])) - let videoContent = NativeVideoContent(id: .profileVideo(id), fileReference: videoFileReference, streamVideo: .none, loopVideo: true, enableSound: false, fetchAutomatically: true, onlyFullSizeThumbnail: false, continuePlayingWithoutSoundOnLostAudioSession: false, placeholderColor: .black) + let videoFileReference = FileMediaReference.standalone(media: TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: 0), partialReference: nil, resource: video.resource, previewRepresentations: [], videoThumbnails: [], immediateThumbnailData: nil, mimeType: "video/mp4", size: nil, attributes: [.Animated, .Video(duration: 0, size: video.dimensions, flags: [])])) + let videoContent = NativeVideoContent(id: .profileVideo(id), fileReference: videoFileReference, streamVideo: isMediaStreamable(resource: video.resource) ? .conservative : .none, loopVideo: true, enableSound: false, fetchAutomatically: true, onlyFullSizeThumbnail: false, continuePlayingWithoutSoundOnLostAudioSession: false, placeholderColor: .clear) let videoNode = UniversalVideoNode(postbox: self.context.account.postbox, audioSession: mediaManager.audioSession, manager: mediaManager.universalVideoManager, decoration: GalleryVideoDecoration(), content: videoContent, priority: .embedded) videoNode.isUserInteractionEnabled = false videoNode.ownsContentNodeUpdated = { [weak self] owns in diff --git a/submodules/PeerInfoUI/Sources/ChannelInfoController.swift b/submodules/PeerInfoUI/Sources/ChannelInfoController.swift index effb06b3ef..b1c2ba31f4 100644 --- a/submodules/PeerInfoUI/Sources/ChannelInfoController.swift +++ b/submodules/PeerInfoUI/Sources/ChannelInfoController.swift @@ -774,7 +774,7 @@ public func channelInfoController(context: AccountContext, peerId: PeerId) -> Vi if let profileImage = peer?.smallProfileImage { return $0.withUpdatedUpdatingAvatar(.image(profileImage, false)) } else { - return $0.withUpdatedUpdatingAvatar(.none) + return $0.withUpdatedUpdatingAvatar(ItemListAvatarAndNameInfoItemUpdatingAvatar.none) } } updateAvatarDisposable.set((updatePeerPhoto(postbox: context.account.postbox, network: context.account.network, stateManager: context.account.stateManager, accountPeerId: context.account.peerId, peerId: peerId, photo: nil, mapResourceToAvatarSizes: { resource, representations in diff --git a/submodules/SettingsUI/Sources/EditSettingsController.swift b/submodules/SettingsUI/Sources/EditSettingsController.swift index ddadf0937f..ea8a33afa0 100644 --- a/submodules/SettingsUI/Sources/EditSettingsController.swift +++ b/submodules/SettingsUI/Sources/EditSettingsController.swift @@ -628,7 +628,7 @@ func editSettingsController(context: AccountContext, currentName: ItemListAvatar if let profileImage = peer?.smallProfileImage { return $0.withUpdatedUpdatingAvatar(.image(profileImage, false)) } else { - return $0.withUpdatedUpdatingAvatar(.none) + return $0.withUpdatedUpdatingAvatar(ItemListAvatarAndNameInfoItemUpdatingAvatar.none) } } updateAvatarDisposable.set((updateAccountPhoto(account: context.account, resource: nil, videoResource: nil, mapResourceToAvatarSizes: { resource, representations in diff --git a/submodules/SettingsUI/Sources/Themes/WallpaperGalleryController.swift b/submodules/SettingsUI/Sources/Themes/WallpaperGalleryController.swift index 0157a7c7ba..c0fe85ad60 100644 --- a/submodules/SettingsUI/Sources/Themes/WallpaperGalleryController.swift +++ b/submodules/SettingsUI/Sources/Themes/WallpaperGalleryController.swift @@ -301,7 +301,7 @@ public class WallpaperGalleryController: ViewController { updateItems.append(item) i += 1 } - return GalleryPagerTransaction(deleteItems: [], insertItems: [], updateItems: updateItems, focusOnItem: self.galleryNode.pager.centralItemNode()?.index) + return GalleryPagerTransaction(deleteItems: [], insertItems: [], updateItems: updateItems, focusOnItem: self.galleryNode.pager.centralItemNode()?.index, synchronous: false) } override public func loadDisplayNode() { diff --git a/submodules/SettingsUI/Sources/Themes/WallpaperGalleryItem.swift b/submodules/SettingsUI/Sources/Themes/WallpaperGalleryItem.swift index 5b85f67397..f885ba6877 100644 --- a/submodules/SettingsUI/Sources/Themes/WallpaperGalleryItem.swift +++ b/submodules/SettingsUI/Sources/Themes/WallpaperGalleryItem.swift @@ -51,13 +51,13 @@ class WallpaperGalleryItem: GalleryItem { self.source = source } - func node() -> GalleryItemNode { + func node(synchronous: Bool) -> GalleryItemNode { let node = WallpaperGalleryItemNode(context: self.context) node.setEntry(self.entry, arguments: self.arguments, source: self.source) return node } - func updateNode(node: GalleryItemNode) { + func updateNode(node: GalleryItemNode, synchronous: Bool) { if let node = node as? WallpaperGalleryItemNode { node.setEntry(self.entry, arguments: self.arguments, source: self.source) } diff --git a/submodules/SyncCore/Sources/CloudFileMediaResource.swift b/submodules/SyncCore/Sources/CloudFileMediaResource.swift index 36859b3f38..c1826c179b 100644 --- a/submodules/SyncCore/Sources/CloudFileMediaResource.swift +++ b/submodules/SyncCore/Sources/CloudFileMediaResource.swift @@ -122,19 +122,21 @@ public final class CloudPhotoSizeMediaResource: TelegramMediaResource { public let sizeSpec: String public let volumeId: Int64 public let localId: Int32 + public let size: Int? public let fileReference: Data? public var id: MediaResourceId { return CloudPhotoSizeMediaResourceId(datacenterId: Int32(self.datacenterId), photoId: self.photoId, sizeSpec: self.sizeSpec) } - public init(datacenterId: Int32, photoId: Int64, accessHash: Int64, sizeSpec: String, volumeId: Int64, localId: Int32, fileReference: Data?) { + public init(datacenterId: Int32, photoId: Int64, accessHash: Int64, sizeSpec: String, volumeId: Int64, localId: Int32, size: Int?, fileReference: Data?) { self.datacenterId = Int(datacenterId) self.photoId = photoId self.accessHash = accessHash self.sizeSpec = sizeSpec self.volumeId = volumeId self.localId = localId + self.size = size self.fileReference = fileReference } @@ -145,6 +147,11 @@ public final class CloudPhotoSizeMediaResource: TelegramMediaResource { self.sizeSpec = decoder.decodeStringForKey("s", orElse: "") self.volumeId = decoder.decodeInt64ForKey("v", orElse: 0) self.localId = decoder.decodeInt32ForKey("l", orElse: 0) + if let size = decoder.decodeOptionalInt32ForKey("n") { + self.size = Int(size) + } else { + self.size = nil + } self.fileReference = decoder.decodeBytesForKey("fr")?.makeData() } @@ -155,6 +162,11 @@ public final class CloudPhotoSizeMediaResource: TelegramMediaResource { encoder.encodeString(self.sizeSpec, forKey: "s") encoder.encodeInt64(self.volumeId, forKey: "v") encoder.encodeInt32(self.localId, forKey: "l") + if let size = self.size { + encoder.encodeInt32(Int32(size), forKey: "n") + } else { + encoder.encodeNil(forKey: "n") + } if let fileReference = self.fileReference { encoder.encodeBytes(MemoryBuffer(data: fileReference), forKey: "fr") } else { @@ -164,7 +176,7 @@ public final class CloudPhotoSizeMediaResource: TelegramMediaResource { public func isEqual(to: MediaResource) -> Bool { if let to = to as? CloudPhotoSizeMediaResource { - return self.datacenterId == to.datacenterId && self.photoId == to.photoId && self.accessHash == to.accessHash && self.sizeSpec == to.sizeSpec && self.volumeId == to.volumeId && self.localId == to.localId && self.fileReference == to.fileReference + return self.datacenterId == to.datacenterId && self.photoId == to.photoId && self.accessHash == to.accessHash && self.sizeSpec == to.sizeSpec && self.volumeId == to.volumeId && self.localId == to.localId && self.size == to.size && self.fileReference == to.fileReference } else { return false } diff --git a/submodules/TelegramCore/Sources/PeerPhotoUpdater.swift b/submodules/TelegramCore/Sources/PeerPhotoUpdater.swift index 30b61bfaf3..95b4528cd4 100644 --- a/submodules/TelegramCore/Sources/PeerPhotoUpdater.swift +++ b/submodules/TelegramCore/Sources/PeerPhotoUpdater.swift @@ -125,12 +125,13 @@ public func updatePeerPhotoInternal(postbox: Postbox, network: Network, stateMan |> mapError { _ in return UploadPeerPhotoError.generic } |> mapToSignal { photo -> Signal<(UpdatePeerPhotoStatus, MediaResource?), UploadPeerPhotoError> in var representations: [TelegramMediaImageRepresentation] = [] + var videoRepresentations: [TelegramMediaImage.VideoRepresentation] = [] switch photo { case let .photo(photo: apiPhoto, users: _): switch apiPhoto { case .photoEmpty: break - case let .photo(_, _, _, _, _, sizes, videoSizes, dcId): + case let .photo(_, id, accessHash, fileReference, _, sizes, videoSizes, dcId): var sizes = sizes if sizes.count == 3 { sizes.remove(at: 1) @@ -147,14 +148,32 @@ public func updatePeerPhotoInternal(postbox: Postbox, network: Network, stateMan } } - if let resource = photoResult.resource as? LocalFileReferenceMediaResource { - if let data = try? Data(contentsOf: URL(fileURLWithPath: resource.localFilePath)) { - for representation in representations { - postbox.mediaBox.storeResourceData(representation.resource.id, data: data) + if let videoSizes = videoSizes { + for size in videoSizes { + switch size { + case let .videoSize(type, location, w, h, size): + let resource: TelegramMediaResource + switch location { + case let .fileLocationToBeDeprecated(volumeId, localId): + resource = CloudPhotoSizeMediaResource(datacenterId: dcId, photoId: id, accessHash: accessHash, sizeSpec: type, volumeId: volumeId, localId: localId, size: Int(size), fileReference: fileReference.makeData()) + } + + videoRepresentations.append(TelegramMediaImage.VideoRepresentation( + dimensions: PixelDimensions(width: w, height: h), + resource: resource)) } } } + + for representation in representations { + postbox.mediaBox.copyResourceData(from: photoResult.resource.id, to: representation.resource.id) + } + if let resource = videoResult?.resource { + for representation in videoRepresentations { + postbox.mediaBox.copyResourceData(from: resource.id, to: representation.resource.id) + } + } } } return postbox.transaction { transaction -> (UpdatePeerPhotoStatus, MediaResource?) in @@ -221,7 +240,7 @@ public func updatePeerPhotoInternal(postbox: Postbox, network: Network, stateMan switch result { case let .complete(representations): if let resource = resource as? LocalFileReferenceMediaResource { - if let data = try? Data(contentsOf: URL(fileURLWithPath: resource.localFilePath)) { + if let data = try? Data(contentsOf: URL(fileURLWithPath: resource.localFilePath), options: [.mappedRead] ) { for representation in representations { postbox.mediaBox.storeResourceData(representation.resource.id, data: data) } @@ -285,13 +304,7 @@ public func updatePeerPhotoExisting(network: Network, reference: TelegramMediaIm return .complete() } |> mapToSignal { _ -> Signal in - return network.request(Api.functions.photos.deletePhotos(id: [.inputPhoto(id: imageId, accessHash: accessHash, fileReference: Buffer(data: fileReference))])) - |> `catch` { _ -> Signal<[Int64], NoError> in - return .single([]) - } - |> mapToSignal { _ -> Signal in - return .complete() - } + return .complete() } } } diff --git a/submodules/TelegramCore/Sources/TelegramMediaImage.swift b/submodules/TelegramCore/Sources/TelegramMediaImage.swift index c1aba36673..dc0d83a115 100644 --- a/submodules/TelegramCore/Sources/TelegramMediaImage.swift +++ b/submodules/TelegramCore/Sources/TelegramMediaImage.swift @@ -12,13 +12,13 @@ func telegramMediaImageRepresentationsFromApiSizes(datacenterId: Int32, photoId: case let .photoCachedSize(type, location, w, h, _): switch location { case let .fileLocationToBeDeprecated(volumeId, localId): - let resource = CloudPhotoSizeMediaResource(datacenterId: datacenterId, photoId: photoId, accessHash: accessHash, sizeSpec: type, volumeId: volumeId, localId: localId, fileReference: fileReference) + let resource = CloudPhotoSizeMediaResource(datacenterId: datacenterId, photoId: photoId, accessHash: accessHash, sizeSpec: type, volumeId: volumeId, localId: localId, size: nil, fileReference: fileReference) representations.append(TelegramMediaImageRepresentation(dimensions: PixelDimensions(width: w, height: h), resource: resource)) } - case let .photoSize(type, location, w, h, _): + case let .photoSize(type, location, w, h, size): switch location { case let .fileLocationToBeDeprecated(volumeId, localId): - let resource = CloudPhotoSizeMediaResource(datacenterId: datacenterId, photoId: photoId, accessHash: accessHash, sizeSpec: type, volumeId: volumeId, localId: localId, fileReference: fileReference) + let resource = CloudPhotoSizeMediaResource(datacenterId: datacenterId, photoId: photoId, accessHash: accessHash, sizeSpec: type, volumeId: volumeId, localId: localId, size: Int(size), fileReference: fileReference) representations.append(TelegramMediaImageRepresentation(dimensions: PixelDimensions(width: w, height: h), resource: resource)) } case let .photoStrippedSize(_, data): @@ -44,11 +44,11 @@ func telegramMediaImageFromApiPhoto(_ photo: Api.Photo) -> TelegramMediaImage? { if let videoSizes = videoSizes { for size in videoSizes { switch size { - case let .videoSize(type, location, w, h, _): + case let .videoSize(type, location, w, h, size): let resource: TelegramMediaResource switch location { case let .fileLocationToBeDeprecated(volumeId, localId): - resource = CloudPhotoSizeMediaResource(datacenterId: dcId, photoId: id, accessHash: accessHash, sizeSpec: type, volumeId: volumeId, localId: localId, fileReference: fileReference.makeData()) + resource = CloudPhotoSizeMediaResource(datacenterId: dcId, photoId: id, accessHash: accessHash, sizeSpec: type, volumeId: volumeId, localId: localId, size: Int(size), fileReference: fileReference.makeData()) } videoRepresentations.append(TelegramMediaImage.VideoRepresentation( diff --git a/submodules/TelegramCore/Sources/UpdatesApiUtils.swift b/submodules/TelegramCore/Sources/UpdatesApiUtils.swift index 9f47ed87ae..483f1e69e1 100644 --- a/submodules/TelegramCore/Sources/UpdatesApiUtils.swift +++ b/submodules/TelegramCore/Sources/UpdatesApiUtils.swift @@ -12,7 +12,7 @@ private func collectPreCachedResources(for photo: Api.Photo) -> [(MediaResource, case let .photoCachedSize(type, location, _, _, bytes): switch location { case let .fileLocationToBeDeprecated(volumeId, localId): - let resource = CloudPhotoSizeMediaResource(datacenterId: dcId, photoId: id, accessHash: accessHash, sizeSpec: type, volumeId: volumeId, localId: localId, fileReference: fileReference.makeData()) + let resource = CloudPhotoSizeMediaResource(datacenterId: dcId, photoId: id, accessHash: accessHash, sizeSpec: type, volumeId: volumeId, localId: localId, size: nil, fileReference: fileReference.makeData()) let data = bytes.makeData() return [(resource, data)] } diff --git a/submodules/TelegramPresentationData/Sources/PresentationThemeCodable.swift b/submodules/TelegramPresentationData/Sources/PresentationThemeCodable.swift index a1f72c12b0..cf790fd448 100644 --- a/submodules/TelegramPresentationData/Sources/PresentationThemeCodable.swift +++ b/submodules/TelegramPresentationData/Sources/PresentationThemeCodable.swift @@ -7,7 +7,10 @@ import TelegramUIPreferences private func decodeColor(_ values: KeyedDecodingContainer, _ key: Key, decoder: Decoder? = nil, fallbackKey: String? = nil) throws -> UIColor { if let decoder = decoder as? PresentationThemeDecoding, let fallbackKey = fallbackKey { - let key = (decoder.codingPath.map { $0.stringValue } + [key.stringValue]).joined(separator: ".") + var codingPath = decoder.codingPath.map { $0.stringValue } + codingPath.append(key.stringValue) + + let key = codingPath.joined(separator: ".") decoder.fallbackKeys[key] = fallbackKey } @@ -1021,9 +1024,17 @@ extension PresentationThemeBubbleColorComponents: Codable { public convenience init(from decoder: Decoder) throws { let values = try decoder.container(keyedBy: CodingKeys.self) let codingPath = decoder.codingPath.map { $0.stringValue }.joined(separator: ".") + + var fillColor = try decodeColor(values, .bg) + var gradientColor = try decodeColor(values, .gradientBg, decoder: decoder, fallbackKey: "\(codingPath).bg") + if gradientColor.rgb != fillColor.rgb { + fillColor = fillColor.withAlphaComponent(1.0) + gradientColor = gradientColor.withAlphaComponent(1.0) + } + self.init( - fill: try decodeColor(values, .bg), - gradientFill: try decodeColor(values, .gradientBg, decoder: decoder, fallbackKey: codingPath + ".bg"), + fill: fillColor, + gradientFill: gradientColor, highlightedFill: try decodeColor(values, .highlightedBg), stroke: try decodeColor(values, .stroke), shadow: try? values.decode(PresentationThemeBubbleShadow.self, forKey: .shadow) @@ -1162,7 +1173,7 @@ extension PresentationThemePartedColors: Codable { accentControlDisabledColor: (try? decodeColor(values, .accentControlDisabled)) ?? accentControlColor.withAlphaComponent(0.5), mediaActiveControlColor: try decodeColor(values, .mediaActiveControl), mediaInactiveControlColor: try decodeColor(values, .mediaInactiveControl), - mediaControlInnerBackgroundColor: try decodeColor(values, .mediaControlInnerBg, decoder: decoder, fallbackKey: codingPath + ".bubble.withWp.bg"), + mediaControlInnerBackgroundColor: try decodeColor(values, .mediaControlInnerBg, decoder: decoder, fallbackKey: "\(codingPath).bubble.withWp.bg"), pendingActivityColor: try decodeColor(values, .pendingActivity), fileTitleColor: try decodeColor(values, .fileTitle), fileDescriptionColor: try decodeColor(values, .fileDescription), @@ -1389,7 +1400,7 @@ extension PresentationThemeChatInputPanel: Codable { let values = try decoder.container(keyedBy: CodingKeys.self) let codingPath = decoder.codingPath.map { $0.stringValue }.joined(separator: ".") self.init(panelBackgroundColor: try decodeColor(values, .panelBg), - panelBackgroundColorNoWallpaper: try decodeColor(values, .panelBg, decoder: decoder, fallbackKey: codingPath + ".panelBgNoWallpaper"), + panelBackgroundColorNoWallpaper: try decodeColor(values, .panelBg, decoder: decoder, fallbackKey: "\(codingPath).panelBgNoWallpaper"), panelSeparatorColor: try decodeColor(values, .panelSeparator), panelControlAccentColor: try decodeColor(values, .panelControlAccent), panelControlColor: try decodeColor(values, .panelControl), diff --git a/submodules/TelegramUI/Sources/PeerInfo/PeerInfoHeaderNode.swift b/submodules/TelegramUI/Sources/PeerInfo/PeerInfoHeaderNode.swift index acef699485..b0fb0fda15 100644 --- a/submodules/TelegramUI/Sources/PeerInfo/PeerInfoHeaderNode.swift +++ b/submodules/TelegramUI/Sources/PeerInfo/PeerInfoHeaderNode.swift @@ -182,12 +182,20 @@ final class PeerInfoAvatarListItemNode: ASDisplayNode { } } + var isExpanded: Bool = false { + didSet { + self.videoNode?.canAttachContent = self.isExpanded + } + } + init(context: AccountContext) { self.context = context self.imageNode = TransformImageNode() super.init() + self.clipsToBounds = true + self.imageNode.contentAnimations = [.firstUpdate, .subsequentUpdates] self.addSubnode(self.imageNode) @@ -222,8 +230,8 @@ final class PeerInfoAvatarListItemNode: ASDisplayNode { if let video = videoRepresentations.last, let id = id { let mediaManager = self.context.sharedContext.mediaManager - let videoFileReference = FileMediaReference.standalone(media: TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: 0), partialReference: nil, resource: video.resource, previewRepresentations: representations.map { $0.representation }, videoThumbnails: [], immediateThumbnailData: nil, mimeType: "video/mp4", size: nil, attributes: [.Animated, .Video(duration: 0, size: video.dimensions, flags: [])])) - let videoContent = NativeVideoContent(id: .profileVideo(id), fileReference: videoFileReference, streamVideo: .none, loopVideo: true, enableSound: false, fetchAutomatically: true, onlyFullSizeThumbnail: false, continuePlayingWithoutSoundOnLostAudioSession: false, placeholderColor: .black) + let videoFileReference = FileMediaReference.standalone(media: TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: 0), partialReference: nil, resource: video.resource, previewRepresentations: [], videoThumbnails: [], immediateThumbnailData: nil, mimeType: "video/mp4", size: nil, attributes: [.Animated, .Video(duration: 0, size: video.dimensions, flags: [])])) + let videoContent = NativeVideoContent(id: .profileVideo(id), fileReference: videoFileReference, streamVideo: isMediaStreamable(resource: video.resource) ? .conservative : .none, loopVideo: true, enableSound: false, fetchAutomatically: true, onlyFullSizeThumbnail: false, continuePlayingWithoutSoundOnLostAudioSession: false, placeholderColor: .clear) let videoNode = UniversalVideoNode(postbox: self.context.account.postbox, audioSession: mediaManager.audioSession, manager: mediaManager.universalVideoManager, decoration: GalleryVideoDecoration(), content: videoContent, priority: .embedded) videoNode.isUserInteractionEnabled = false videoNode.ownsContentNodeUpdated = { [weak self] owns in @@ -259,7 +267,7 @@ final class PeerInfoAvatarListItemNode: ASDisplayNode { videoNode.updateLayout(size: imageSize, transition: .immediate) videoNode.frame = imageFrame - videoNode.canAttachContent = true + videoNode.canAttachContent = self.isExpanded if videoNode.hasAttachedContext { videoNode.play() } @@ -285,17 +293,17 @@ final class PeerInfoAvatarListContainerNode: ASDisplayNode { private var items: [PeerInfoAvatarListItem] = [] private var itemNodes: [WrappedMediaResourceId: PeerInfoAvatarListItemNode] = [:] private var stripNodes: [ASImageNode] = [] - private var stripWidth: CGFloat = 0.0 private let activeStripImage: UIImage private var appliedStripNodeCurrentIndex: Int? private var currentIndex: Int = 0 private var transitionFraction: CGFloat = 0.0 private var validLayout: CGSize? + private var isExpanded = false private let disposable = MetaDisposable() - private let positionDisposable = MetaDisposable() private var initializedList = false + var itemsUpdated: (([PeerInfoAvatarListItem]) -> Void)? let isReady = Promise() private var didSetReady = false @@ -316,55 +324,6 @@ final class PeerInfoAvatarListContainerNode: ASDisplayNode { } } - private var playerUpdateTimer: SwiftSignalKit.Timer? - private var playerStatus: MediaPlayerStatus? { - didSet { - if self.playerStatus != oldValue { - if let playerStatus = playerStatus, case .playing = playerStatus.status { - self.ensureHasTimer() - } else { - self.stopTimer() - } - self.updateStatus() - } - } - } - - private func ensureHasTimer() { - if self.playerUpdateTimer == nil { - let timer = SwiftSignalKit.Timer(timeout: 0.016, repeat: true, completion: { [weak self] in - self?.updateStatus() - }, queue: Queue.mainQueue()) - self.playerUpdateTimer = timer - timer.start() - } - } - - private func updateStatus() { - var position: CGFloat = 1.0 - if let playerStatus = self.playerStatus { - var playerPosition: Double - if !playerStatus.generationTimestamp.isZero, case .playing = playerStatus.status { - playerPosition = playerStatus.timestamp + (CACurrentMediaTime() - playerStatus.generationTimestamp) - } else { - playerPosition = playerStatus.timestamp - } - - position = CGFloat(playerPosition / playerStatus.duration) - } - - if let appliedStripNodeCurrentIndex = self.appliedStripNodeCurrentIndex { - var frame = self.stripNodes[appliedStripNodeCurrentIndex].frame - frame.size.width = self.stripWidth * position - self.stripNodes[appliedStripNodeCurrentIndex].frame = frame - } - } - - private func stopTimer() { - self.playerUpdateTimer?.invalidate() - self.playerUpdateTimer = nil - } - init(context: AccountContext) { self.context = context @@ -537,7 +496,6 @@ final class PeerInfoAvatarListContainerNode: ASDisplayNode { deinit { self.disposable.dispose() - self.positionDisposable.dispose() } override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? { @@ -628,8 +586,9 @@ final class PeerInfoAvatarListContainerNode: ASDisplayNode { } } - func update(size: CGSize, peer: Peer?, transition: ContainedViewLayoutTransition) { + func update(size: CGSize, peer: Peer?, isExpanded: Bool, transition: ContainedViewLayoutTransition) { self.validLayout = size + self.isExpanded = isExpanded self.leftHighlightNode.frame = CGRect(origin: CGPoint(), size: CGSize(width: floor(size.width * 1.0 / 5.0), height: size.height)) self.rightHighlightNode.frame = CGRect(origin: CGPoint(x: size.width - floor(size.width * 1.0 / 5.0), y: 0.0), size: CGSize(width: floor(size.width * 1.0 / 5.0), height: size.height)) @@ -652,6 +611,7 @@ final class PeerInfoAvatarListContainerNode: ASDisplayNode { } strongSelf.galleryEntries = entries strongSelf.items = items + strongSelf.itemsUpdated?(items) if let size = strongSelf.validLayout { strongSelf.updateItems(size: size, transition: .immediate, stripTransition: .immediate) } @@ -677,9 +637,11 @@ final class PeerInfoAvatarListContainerNode: ASDisplayNode { var wasAdded = false if let current = self.itemNodes[self.items[i].id] { itemNode = current + itemNode.isExpanded = self.isExpanded } else { wasAdded = true itemNode = PeerInfoAvatarListItemNode(context: self.context) + itemNode.isExpanded = self.isExpanded itemNode.setup(item: self.items[i], synchronous: synchronous && i == self.currentIndex) self.itemNodes[self.items[i].id] = itemNode self.contentNode.addSubnode(itemNode) @@ -687,6 +649,7 @@ final class PeerInfoAvatarListContainerNode: ASDisplayNode { let indexOffset = CGFloat(i - self.currentIndex) let itemFrame = CGRect(origin: CGPoint(x: indexOffset * size.width + self.transitionFraction * size.width - size.width / 2.0, y: -size.height / 2.0), size: size) + if wasAdded { addedItemNodesForAdditiveTransition.append(itemNode) itemNode.frame = itemFrame @@ -748,17 +711,6 @@ final class PeerInfoAvatarListContainerNode: ASDisplayNode { if self.currentIndex >= 0 && self.currentIndex < self.stripNodes.count { self.stripNodes[self.currentIndex].alpha = 1.0 } - - if let currentItemNode = self.currentItemNode { - self.positionDisposable.set((currentItemNode.mediaStatus - |> deliverOnMainQueue).start(next: { [weak self] status in - if let strongSelf = self { - strongSelf.playerStatus = status - } - })) - } else { - self.positionDisposable.set(nil) - } } if hadOneStripNode && self.stripNodes.count > 1 { self.stripContainerNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.25) @@ -766,7 +718,6 @@ final class PeerInfoAvatarListContainerNode: ASDisplayNode { let stripInset: CGFloat = 8.0 let stripSpacing: CGFloat = 4.0 let stripWidth: CGFloat = max(5.0, floor((size.width - stripInset * 2.0 - stripSpacing * CGFloat(self.stripNodes.count - 1)) / CGFloat(self.stripNodes.count))) - self.stripWidth = stripWidth let currentStripMinX = stripInset + CGFloat(self.currentIndex) * (stripWidth + stripSpacing) let currentStripMidX = floor(currentStripMinX + stripWidth / 2.0) let lastStripMaxX = stripInset + CGFloat(self.stripNodes.count - 1) * (stripWidth + stripSpacing) + stripWidth @@ -795,6 +746,9 @@ final class PeerInfoAvatarTransformContainerNode: ASDisplayNode { let context: AccountContext let avatarNode: AvatarNode + private var videoNode: UniversalVideoNode? + private var videoContent: NativeVideoContent? + var tapped: (() -> Void)? private var isFirstAvatarLoading = true @@ -818,7 +772,7 @@ final class PeerInfoAvatarTransformContainerNode: ASDisplayNode { } } - func update(peer: Peer?, theme: PresentationTheme, avatarSize: CGFloat) { + func update(peer: Peer?, item: PeerInfoAvatarListItem?, theme: PresentationTheme, avatarSize: CGFloat, isExpanded: Bool) { if let peer = peer { var overrideImage: AvatarNodeImageOverride? if peer.isDeleted { @@ -829,6 +783,46 @@ final class PeerInfoAvatarTransformContainerNode: ASDisplayNode { self.avatarNode.frame = CGRect(origin: CGPoint(x: -avatarSize / 2.0, y: -avatarSize / 2.0), size: CGSize(width: avatarSize, height: avatarSize)) self.avatarNode.font = avatarPlaceholderFont(size: floor(avatarSize * 16.0 / 37.0)) + + if let item = item, case let .image(reference, _, videoRepresentations) = item, let video = videoRepresentations.last, case let .cloud(imageId, _, _) = reference { + let id = imageId + let videoFileReference = FileMediaReference.standalone(media: TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: 0), partialReference: nil, resource: video.resource, previewRepresentations: [], videoThumbnails: [], immediateThumbnailData: nil, mimeType: "video/mp4", size: nil, attributes: [.Animated, .Video(duration: 0, size: video.dimensions, flags: [])])) + let videoContent = NativeVideoContent(id: .profileVideo(id), fileReference: videoFileReference, streamVideo: isMediaStreamable(resource: video.resource) ? .conservative : .none, loopVideo: true, enableSound: false, fetchAutomatically: true, onlyFullSizeThumbnail: false, continuePlayingWithoutSoundOnLostAudioSession: false, placeholderColor: .clear) + if videoContent.id != self.videoContent?.id { + let mediaManager = self.context.sharedContext.mediaManager + let videoNode = UniversalVideoNode(postbox: self.context.account.postbox, audioSession: mediaManager.audioSession, manager: mediaManager.universalVideoManager, decoration: GalleryVideoDecoration(), content: videoContent, priority: .embedded) + videoNode.isUserInteractionEnabled = false + videoNode.ownsContentNodeUpdated = { [weak self] owns in + if let strongSelf = self { + strongSelf.videoNode?.isHidden = !owns + } + } + self.videoContent = videoContent + self.videoNode = videoNode + + let maskPath = UIBezierPath(ovalIn: CGRect(origin: CGPoint(), size: self.avatarNode.frame.size)) + let shape = CAShapeLayer() + shape.path = maskPath.cgPath + videoNode.layer.mask = shape + + self.addSubnode(videoNode) + } + } else if let videoNode = self.videoNode { + self.videoContent = nil + self.videoNode = nil + + videoNode.removeFromSupernode() + } + + if let videoNode = self.videoNode { + videoNode.updateLayout(size: self.avatarNode.frame.size, transition: .immediate) + videoNode.frame = self.avatarNode.frame + + videoNode.canAttachContent = !isExpanded + if videoNode.hasAttachedContext { + videoNode.play() + } + } } } } @@ -921,6 +915,9 @@ final class PeerInfoAvatarListNode: ASDisplayNode { let listContainerNode: PeerInfoAvatarListContainerNode let isReady = Promise() + + var arguments: (Peer?, PresentationTheme, CGFloat, Bool)? + var item: PeerInfoAvatarListItem? init(context: AccountContext, readyWhenGalleryLoads: Bool) { self.avatarContainerNode = PeerInfoAvatarTransformContainerNode(context: context) @@ -965,10 +962,20 @@ final class PeerInfoAvatarListNode: ASDisplayNode { return value } |> take(1)) + + self.listContainerNode.itemsUpdated = { [weak self] items in + if let strongSelf = self { + strongSelf.item = items.first + if let (peer, theme, avatarSize, isExpanded) = strongSelf.arguments { + strongSelf.avatarContainerNode.update(peer: peer, item: strongSelf.item, theme: theme, avatarSize: avatarSize, isExpanded: isExpanded) + } + } + } } func update(size: CGSize, avatarSize: CGFloat, isExpanded: Bool, peer: Peer?, theme: PresentationTheme, transition: ContainedViewLayoutTransition) { - self.avatarContainerNode.update(peer: peer, theme: theme, avatarSize: avatarSize) + self.arguments = (peer, theme, avatarSize, isExpanded) + self.avatarContainerNode.update(peer: peer, item: self.item, theme: theme, avatarSize: avatarSize, isExpanded: isExpanded) } override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? { @@ -2073,7 +2080,7 @@ final class PeerInfoHeaderNode: ASDisplayNode { transition.updateSublayerTransformScale(node: self.avatarListNode.listContainerTransformNode, scale: avatarListContainerScale) } - self.avatarListNode.listContainerNode.update(size: expandedAvatarListSize, peer: peer, transition: transition) + self.avatarListNode.listContainerNode.update(size: expandedAvatarListSize, peer: peer, isExpanded: self.isAvatarExpanded, transition: transition) let panelWithAvatarHeight: CGFloat = 112.0 + avatarSize let buttonsCollapseStart = titleCollapseOffset diff --git a/submodules/WallpaperResources/Sources/WallpaperResources.swift b/submodules/WallpaperResources/Sources/WallpaperResources.swift index b8b6d8efcd..a2e7973982 100644 --- a/submodules/WallpaperResources/Sources/WallpaperResources.swift +++ b/submodules/WallpaperResources/Sources/WallpaperResources.swift @@ -829,7 +829,9 @@ public func drawThemeImage(context c: CGContext, theme: PresentationTheme, wallp let _ = try? drawSvgPath(c, path: "M98.0061174,0 C106.734138,0 113.82927,6.99200411 113.996965,15.6850616 L114,16 C114,24.836556 106.830179,32 98.0061174,32 L21.9938826,32 C18.2292665,32 14.7684355,30.699197 12.0362474,28.5221601 C8.56516444,32.1765452 -1.77635684e-15,31.9985981 -1.77635684e-15,31.9985981 C5.69252399,28.6991366 5.98604874,24.4421608 5.99940747,24.1573436 L6,24.1422468 L6,16 C6,7.163444 13.1698213,0 21.9938826,0 L98.0061174,0 ") if incoming.fill.rgb != incoming.gradientFill.rgb { - let gradientColors = [incoming.fill, incoming.gradientFill].map { $0.cgColor } as CFArray + c.clip() + + let gradientColors = [incoming.fill.withAlphaComponent(1.0), incoming.gradientFill.withAlphaComponent(1.0)].map { $0.cgColor } as CFArray var locations: [CGFloat] = [0.0, 1.0] let colorSpace = CGColorSpaceCreateDeviceRGB() let gradient = CGGradient(colorsSpace: colorSpace, colors: gradientColors, locations: &locations)! @@ -855,7 +857,7 @@ public func drawThemeImage(context c: CGContext, theme: PresentationTheme, wallp if outgoing.fill.rgb != outgoing.gradientFill.rgb { c.clip() - let gradientColors = [outgoing.fill, outgoing.gradientFill].map { $0.cgColor } as CFArray + let gradientColors = [outgoing.fill.withAlphaComponent(1.0), outgoing.gradientFill.withAlphaComponent(1.0)].map { $0.cgColor } as CFArray var locations: [CGFloat] = [0.0, 1.0] let colorSpace = CGColorSpaceCreateDeviceRGB() let gradient = CGGradient(colorsSpace: colorSpace, colors: gradientColors, locations: &locations)! diff --git a/submodules/WebSearchUI/Sources/WebSearchGalleryController.swift b/submodules/WebSearchUI/Sources/WebSearchGalleryController.swift index 9eef9e9b80..bd5196a332 100644 --- a/submodules/WebSearchUI/Sources/WebSearchGalleryController.swift +++ b/submodules/WebSearchUI/Sources/WebSearchGalleryController.swift @@ -139,7 +139,7 @@ class WebSearchGalleryController: ViewController { if strongSelf.isViewLoaded { strongSelf.galleryNode.pager.replaceItems(strongSelf.entries.map({ $0.item(context: context, presentationData: strongSelf.presentationData, controllerInteraction: strongSelf.controllerInteraction) - }), centralItemIndex: centralIndex, keepFirst: false) + }), centralItemIndex: centralIndex) let ready = strongSelf.galleryNode.pager.ready() |> timeout(2.0, queue: Queue.mainQueue(), alternate: .single(Void())) |> afterNext { [weak strongSelf] _ in strongSelf?.didSetReady = true diff --git a/submodules/WebSearchUI/Sources/WebSearchVideoGalleryItem.swift b/submodules/WebSearchUI/Sources/WebSearchVideoGalleryItem.swift index f41c5c450e..201845745c 100644 --- a/submodules/WebSearchUI/Sources/WebSearchVideoGalleryItem.swift +++ b/submodules/WebSearchUI/Sources/WebSearchVideoGalleryItem.swift @@ -35,13 +35,13 @@ class WebSearchVideoGalleryItem: GalleryItem { self.controllerInteraction = controllerInteraction } - func node() -> GalleryItemNode { + func node(synchronous: Bool) -> GalleryItemNode { let node = WebSearchVideoGalleryItemNode(context: self.context, presentationData: self.presentationData, controllerInteraction: self.controllerInteraction) node.setupItem(self) return node } - func updateNode(node: GalleryItemNode) { + func updateNode(node: GalleryItemNode, synchronous: Bool) { if let node = node as? WebSearchVideoGalleryItemNode { node.setupItem(self) }