Merge commit '5e36741afa9f5f28e55af8fd13e718a300c98870'

This commit is contained in:
Ali 2020-07-02 22:09:21 +04:00
commit 9cd7e3a8f1
23 changed files with 386 additions and 93 deletions

View File

@ -312,6 +312,7 @@ public final class GalleryPagerNode: ASDisplayNode, UIScrollViewDelegate, UIGest
} }
} }
public var updateOnReplacement = false
public func replaceItems(_ items: [GalleryItem], centralItemIndex: Int?, synchronous: Bool = false) { public func replaceItems(_ items: [GalleryItem], centralItemIndex: Int?, synchronous: Bool = false) {
var updateItems: [GalleryPagerUpdateItem] = [] var updateItems: [GalleryPagerUpdateItem] = []
var deleteItems: [Int] = [] var deleteItems: [Int] = []
@ -326,14 +327,20 @@ public final class GalleryPagerNode: ASDisplayNode, UIScrollViewDelegate, UIGest
} }
} }
for i in 0 ..< items.count { if self.updateOnReplacement {
if i == previousIndexById[items[i].id] { for i in 0 ..< items.count {
updateItems.append(GalleryPagerUpdateItem(index: i, previousIndex: i, item: items[i])) if (previousIndexById[items[i].id] == nil) {
} else { insertItems.append(GalleryPagerInsertItem(index: i, item: items[i], previousIndex: previousIndexById[items[i].id]))
} else {
updateItems.append(GalleryPagerUpdateItem(index: i, previousIndex: i, item: items[i]))
}
}
} else {
for i in 0 ..< items.count {
insertItems.append(GalleryPagerInsertItem(index: i, item: items[i], previousIndex: previousIndexById[items[i].id])) insertItems.append(GalleryPagerInsertItem(index: i, item: items[i], previousIndex: previousIndexById[items[i].id]))
} }
} }
self.transaction(GalleryPagerTransaction(deleteItems: deleteItems, insertItems: insertItems, updateItems: updateItems, focusOnItem: centralItemIndex, synchronous: synchronous)) self.transaction(GalleryPagerTransaction(deleteItems: deleteItems, insertItems: insertItems, updateItems: updateItems, focusOnItem: centralItemIndex, synchronous: synchronous))
} }
@ -404,7 +411,7 @@ public final class GalleryPagerNode: ASDisplayNode, UIScrollViewDelegate, UIGest
self.ignoreCentralItemIndexUpdate = true self.ignoreCentralItemIndexUpdate = true
self.centralItemIndex = focusOnItem self.centralItemIndex = focusOnItem
self.ignoreCentralItemIndexUpdate = false self.ignoreCentralItemIndexUpdate = false
self.updateItemNodes(transition: .immediate, forceOffsetReset: true) self.updateItemNodes(transition: .immediate, forceOffsetReset: true, synchronous: transaction.synchronous)
} }
} }

View File

@ -39,6 +39,8 @@
- (void)closeCurtains; - (void)closeCurtains;
- (void)openCurtains; - (void)openCurtains;
- (void)flash:(void (^)(void))completion;
- (void)invalidateCropRect; - (void)invalidateCropRect;
- (UIImage *)currentImage; - (UIImage *)currentImage;

View File

@ -44,6 +44,7 @@
- (void)prepareTransitionOutSaving:(bool)saving; - (void)prepareTransitionOutSaving:(bool)saving;
- (void)prepareForCustomTransitionOut; - (void)prepareForCustomTransitionOut;
- (void)finishCustomTransitionOut;
- (void)animateTransitionIn; - (void)animateTransitionIn;
- (CGRect)_targetFrameForTransitionInFromFrame:(CGRect)fromFrame; - (CGRect)_targetFrameForTransitionInFromFrame:(CGRect)fromFrame;

View File

@ -2,6 +2,8 @@
@interface TGPhotoVideoEditor : NSObject @interface TGPhotoVideoEditor : NSObject
+ (void)presentWithContext:(id<LegacyComponentsContext>)context parentController:(TGViewController *)parentController image:(UIImage *)image video:(NSURL *)video didFinishWithImage:(void (^)(UIImage *image))didFinishWithImage didFinishWithVideo:(void (^)(UIImage *image, NSURL *url, TGVideoEditAdjustments *adjustments))didFinishWithVideo;
+ (void)presentWithContext:(id<LegacyComponentsContext>)context controller:(TGViewController *)controller caption:(NSString *)caption entities:(NSArray *)entities withItem:(id<TGMediaEditableItem, TGMediaSelectableItem>)item recipientName:(NSString *)recipientName stickersContext:(id<TGPhotoPaintStickersContext>)stickersContext completion:(void (^)(id<TGMediaEditableItem>, TGMediaEditingContext *))completion dismissed:(void (^)())dismissed; + (void)presentWithContext:(id<LegacyComponentsContext>)context controller:(TGViewController *)controller caption:(NSString *)caption entities:(NSArray *)entities withItem:(id<TGMediaEditableItem, TGMediaSelectableItem>)item recipientName:(NSString *)recipientName stickersContext:(id<TGPhotoPaintStickersContext>)stickersContext completion:(void (^)(id<TGMediaEditableItem>, TGMediaEditingContext *))completion dismissed:(void (^)())dismissed;
@end @end

View File

@ -484,7 +484,6 @@
[_finalFilter addTarget:self.previewOutput.imageView]; [_finalFilter addTarget:self.previewOutput.imageView];
} }
} }
if (_histogramGenerator != nil && !self.standalone) { if (_histogramGenerator != nil && !self.standalone) {
[_finalFilter addTarget:_histogramGenerator]; [_finalFilter addTarget:_histogramGenerator];

View File

@ -23,8 +23,12 @@
#import <LegacyComponents/TGMenuView.h> #import <LegacyComponents/TGMenuView.h>
#import "TGPaintFaceDetector.h"
@interface TGMediaPickerGalleryPhotoItemView () @interface TGMediaPickerGalleryPhotoItemView ()
{ {
SMetaDisposable *_facesDisposable;
UILabel *_fileInfoLabel; UILabel *_fileInfoLabel;
TGMessageImageViewOverlayView *_progressView; TGMessageImageViewOverlayView *_progressView;
@ -57,6 +61,8 @@
self = [super initWithFrame:frame]; self = [super initWithFrame:frame];
if (self != nil) if (self != nil)
{ {
_facesDisposable = [[SMetaDisposable alloc] init];
__weak TGMediaPickerGalleryPhotoItemView *weakSelf = self; __weak TGMediaPickerGalleryPhotoItemView *weakSelf = self;
_imageView = [[TGModernGalleryImageItemImageView alloc] init]; _imageView = [[TGModernGalleryImageItemImageView alloc] init];
_imageView.clipsToBounds = true; _imageView.clipsToBounds = true;
@ -101,6 +107,7 @@
{ {
[_adjustmentsDisposable dispose]; [_adjustmentsDisposable dispose];
[_attributesDisposable dispose]; [_attributesDisposable dispose];
[_facesDisposable dispose];
} }
- (void)setHiddenAsBeingEdited:(bool)hidden - (void)setHiddenAsBeingEdited:(bool)hidden
@ -226,8 +233,11 @@
}]]; }]];
if (!item.asFile) if (!item.asFile) {
[_facesDisposable setDisposable:[[TGPaintFaceDetector detectFacesInItem:item.editableMediaItem editingContext:item.editingContext] startWithNext:nil]];
return; return;
}
_fileInfoLabel.text = nil; _fileInfoLabel.text = nil;

View File

@ -41,6 +41,7 @@
#import <LegacyComponents/TGMenuView.h> #import <LegacyComponents/TGMenuView.h>
#import "PGPhotoEditor.h" #import "PGPhotoEditor.h"
#import "TGPaintFaceDetector.h"
@interface TGMediaPickerGalleryVideoItemView() <TGMediaPickerGalleryVideoScrubberDataSource, TGMediaPickerGalleryVideoScrubberDelegate> @interface TGMediaPickerGalleryVideoItemView() <TGMediaPickerGalleryVideoScrubberDataSource, TGMediaPickerGalleryVideoScrubberDelegate>
{ {
@ -96,6 +97,7 @@
SMetaDisposable *_adjustmentsDisposable; SMetaDisposable *_adjustmentsDisposable;
SMetaDisposable *_attributesDisposable; SMetaDisposable *_attributesDisposable;
SMetaDisposable *_downloadDisposable; SMetaDisposable *_downloadDisposable;
SMetaDisposable *_facesDisposable;
SMetaDisposable *_currentAudioSession; SMetaDisposable *_currentAudioSession;
SVariable *_editableItemVariable; SVariable *_editableItemVariable;
@ -131,6 +133,7 @@
_currentAudioSession = [[SMetaDisposable alloc] init]; _currentAudioSession = [[SMetaDisposable alloc] init];
_playerItemDisposable = [[SMetaDisposable alloc] init]; _playerItemDisposable = [[SMetaDisposable alloc] init];
_facesDisposable = [[SMetaDisposable alloc] init];
_videoDurationVar = [[SVariable alloc] init]; _videoDurationVar = [[SVariable alloc] init];
_videoDurationDisposable = [[SMetaDisposable alloc] init]; _videoDurationDisposable = [[SMetaDisposable alloc] init];
@ -251,6 +254,7 @@
[_thumbnailsDisposable dispose]; [_thumbnailsDisposable dispose];
[_attributesDisposable dispose]; [_attributesDisposable dispose];
[_downloadDisposable dispose]; [_downloadDisposable dispose];
[_facesDisposable dispose];
[self stopPlayer]; [self stopPlayer];
[self releaseVolumeOverlay]; [self releaseVolumeOverlay];
@ -385,8 +389,13 @@
[super setItem:item synchronously:synchronously]; [super setItem:item synchronously:synchronously];
if (itemChanged) if (itemChanged) {
[self _playerCleanup]; [self _playerCleanup];
if (!item.asFile) {
[_facesDisposable setDisposable:[[TGPaintFaceDetector detectFacesInItem:item.editableMediaItem editingContext:item.editingContext] startWithNext:nil]];
}
}
_scrubberView.allowsTrimming = false; _scrubberView.allowsTrimming = false;
_videoDimensions = item.dimensions; _videoDimensions = item.dimensions;

View File

@ -1,6 +1,9 @@
#import <UIKit/UIKit.h> #import <UIKit/UIKit.h>
#import <SSignalKit/SSignalKit.h> #import <SSignalKit/SSignalKit.h>
@protocol TGMediaEditableItem;
@class TGMediaEditingContext;
@interface TGPaintFaceFeature : NSObject @interface TGPaintFaceFeature : NSObject
{ {
CGPoint _position; CGPoint _position;
@ -49,6 +52,8 @@
+ (SSignal *)detectFacesInImage:(UIImage *)image originalSize:(CGSize)originalSize; + (SSignal *)detectFacesInImage:(UIImage *)image originalSize:(CGSize)originalSize;
+ (SSignal *)detectFacesInItem:(id<TGMediaEditableItem>)item editingContext:(TGMediaEditingContext *)editingContext;
@end @end

View File

@ -3,6 +3,9 @@
#import <LegacyComponents/TGPaintUtils.h> #import <LegacyComponents/TGPaintUtils.h>
#import <ImageIO/ImageIO.h> #import <ImageIO/ImageIO.h>
#import "TGMediaEditingContext.h"
#import "UIImage+TG.h"
@interface TGPaintFace () @interface TGPaintFace ()
+ (instancetype)faceWithBounds:(CGRect)bounds angle:(CGFloat)angle leftEye:(TGPaintFaceEye *)leftEye rightEye:(TGPaintFaceEye *)rightEye mouth:(TGPaintFaceMouth *)mouth; + (instancetype)faceWithBounds:(CGRect)bounds angle:(CGFloat)angle leftEye:(TGPaintFaceEye *)leftEye rightEye:(TGPaintFaceEye *)rightEye mouth:(TGPaintFaceMouth *)mouth;
@ -26,6 +29,41 @@
@implementation TGPaintFaceDetector @implementation TGPaintFaceDetector
+ (SSignal *)detectFacesInItem:(id<TGMediaEditableItem>)item editingContext:(TGMediaEditingContext *)editingContext
{
CGSize originalSize = item.originalSize;
SSignal *cachedFaces = [editingContext facesForItem:item];
SSignal *cachedSignal = [cachedFaces mapToSignal:^SSignal *(id result)
{
if (result == nil)
return [SSignal fail:nil];
return [SSignal single:result];
}];
SSignal *imageSignal = [item screenImageSignal:0];
SSignal *detectSignal = [[[imageSignal filter:^bool(UIImage *image)
{
if (![image isKindOfClass:[UIImage class]])
return false;
if (image.degraded)
return false;
return true;
}] take:1] mapToSignal:^SSignal *(UIImage *image) {
return [[TGPaintFaceDetector detectFacesInImage:image originalSize:originalSize] startOn:[SQueue concurrentDefaultQueue]];
}];
return [[[cachedSignal catch:^SSignal *(__unused id error)
{
return detectSignal;
}] deliverOn:[SQueue mainQueue]] onNext:^(NSArray *next)
{
[editingContext setFaces:next forItem:item];
}];
}
+ (SSignal *)detectFacesInImage:(UIImage *)image originalSize:(CGSize)originalSize + (SSignal *)detectFacesInImage:(UIImage *)image originalSize:(CGSize)originalSize
{ {
return [[SSignal alloc] initWithGenerator:^id<SDisposable>(SSubscriber *subscriber) return [[SSignal alloc] initWithGenerator:^id<SDisposable>(SSubscriber *subscriber)

View File

@ -26,6 +26,8 @@ const CGFloat TGPhotoAvatarCropViewCurtainMargin = 200;
UIView *_snapshotView; UIView *_snapshotView;
CGSize _snapshotSize; CGSize _snapshotSize;
UIView *_flashView;
UIView *_topOverlayView; UIView *_topOverlayView;
UIView *_leftOverlayView; UIView *_leftOverlayView;
UIView *_rightOverlayView; UIView *_rightOverlayView;
@ -39,7 +41,7 @@ const CGFloat TGPhotoAvatarCropViewCurtainMargin = 200;
CGFloat _currentDiameter; CGFloat _currentDiameter;
PGPhotoEditorView *_fullPreviewView; __weak PGPhotoEditorView *_fullPreviewView;
} }
@end @end
@ -82,6 +84,12 @@ const CGFloat TGPhotoAvatarCropViewCurtainMargin = 200;
_fullPreviewView.userInteractionEnabled = false; _fullPreviewView.userInteractionEnabled = false;
[_wrapperView addSubview:_fullPreviewView]; [_wrapperView addSubview:_fullPreviewView];
_flashView = [[UIView alloc] init];
_flashView.alpha = 0.0;
_flashView.backgroundColor = [UIColor whiteColor];
_flashView.userInteractionEnabled = false;
[self addSubview:_flashView];
_topCurtainView = [[UIView alloc] initWithFrame:CGRectZero]; _topCurtainView = [[UIView alloc] initWithFrame:CGRectZero];
_topCurtainView.backgroundColor = [UIColor blackColor]; _topCurtainView.backgroundColor = [UIColor blackColor];
[self addSubview:_topCurtainView]; [self addSubview:_topCurtainView];
@ -122,6 +130,11 @@ const CGFloat TGPhotoAvatarCropViewCurtainMargin = 200;
return self; return self;
} }
- (void)dealloc
{
_scrollView.delegate = nil;
}
- (void)handleTap:(UITapGestureRecognizer *)gestureRecognizer { - (void)handleTap:(UITapGestureRecognizer *)gestureRecognizer {
if (self.tapped != nil) if (self.tapped != nil)
self.tapped(); self.tapped();
@ -611,6 +624,8 @@ const CGFloat TGPhotoAvatarCropViewCurtainMargin = 200;
{ {
[self _layoutOverlayViews]; [self _layoutOverlayViews];
_flashView.frame = self.bounds;
if (_scrollView.superview == nil) if (_scrollView.superview == nil)
{ {
_scrollView.frame = self.bounds; _scrollView.frame = self.bounds;
@ -647,4 +662,16 @@ const CGFloat TGPhotoAvatarCropViewCurtainMargin = 200;
return CGSizeMake(20, 20); return CGSizeMake(20, 20);
} }
- (void)flash:(void (^)(void))completion {
[UIView animateWithDuration:0.12 animations:^{
_flashView.alpha = 1.0f;
} completion:^(BOOL finished) {
[UIView animateWithDuration:0.2 animations:^{
_flashView.alpha = 0.0f;
} completion:^(BOOL finished) {
completion();
}];
}];
}
@end @end

View File

@ -15,7 +15,12 @@
@property (nonatomic, copy) void (^croppingChanged)(void); @property (nonatomic, copy) void (^croppingChanged)(void);
@property (nonatomic, copy) void (^togglePlayback)(void); @property (nonatomic, copy) void (^togglePlayback)(void);
- (instancetype)initWithContext:(id<LegacyComponentsContext>)context photoEditor:(PGPhotoEditor *)photoEditor previewView:(TGPhotoEditorPreviewView *)previewView scrubberView:(TGMediaPickerGalleryVideoScrubber *)scrubberView dotImageView:(UIView *)dotImageView fullPreviewView:(PGPhotoEditorView *)fullPreviewView; @property (nonatomic, weak) UIView *dotImageView;
@property (nonatomic, weak) UIView *dotMarkerView;
@property (nonatomic, weak) PGPhotoEditorView *fullPreviewView;
@property (nonatomic, weak) TGMediaPickerGalleryVideoScrubber *scrubberView;
- (instancetype)initWithContext:(id<LegacyComponentsContext>)context photoEditor:(PGPhotoEditor *)photoEditor previewView:(TGPhotoEditorPreviewView *)previewView;
- (void)setImage:(UIImage *)image; - (void)setImage:(UIImage *)image;
- (void)setSnapshotImage:(UIImage *)snapshotImage; - (void)setSnapshotImage:(UIImage *)snapshotImage;

View File

@ -32,18 +32,13 @@ const CGFloat TGPhotoAvatarPreviewLandscapePanelSize = TGPhotoAvatarPreviewPanel
UIView *_wrapperView; UIView *_wrapperView;
TGPhotoAvatarCropView *_cropView; __weak TGPhotoAvatarCropView *_cropView;
PGPhotoEditorView *_fullPreviewView;
UIView *_portraitToolsWrapperView; UIView *_portraitToolsWrapperView;
UIView *_landscapeToolsWrapperView; UIView *_landscapeToolsWrapperView;
UIView *_portraitWrapperBackgroundView; UIView *_portraitWrapperBackgroundView;
UIView *_landscapeWrapperBackgroundView; UIView *_landscapeWrapperBackgroundView;
TGMediaPickerGalleryVideoScrubber *_scrubberView;
UIView *_dotImageView;
UIView *_videoAreaView;
UIView *_flashView;
UIView *_portraitToolControlView; UIView *_portraitToolControlView;
UIView *_landscapeToolControlView; UIView *_landscapeToolControlView;
UILabel *_coverLabel; UILabel *_coverLabel;
@ -58,21 +53,20 @@ const CGFloat TGPhotoAvatarPreviewLandscapePanelSize = TGPhotoAvatarPreviewPanel
@implementation TGPhotoAvatarPreviewController @implementation TGPhotoAvatarPreviewController
- (instancetype)initWithContext:(id<LegacyComponentsContext>)context photoEditor:(PGPhotoEditor *)photoEditor previewView:(TGPhotoEditorPreviewView *)previewView scrubberView:(TGMediaPickerGalleryVideoScrubber *)scrubberView dotImageView:(UIView *)dotImageView fullPreviewView:(PGPhotoEditorView *)fullPreviewView - (instancetype)initWithContext:(id<LegacyComponentsContext>)context photoEditor:(PGPhotoEditor *)photoEditor previewView:(TGPhotoEditorPreviewView *)previewView {
{
self = [super initWithContext:context]; self = [super initWithContext:context];
if (self != nil) if (self != nil)
{ {
self.photoEditor = photoEditor; self.photoEditor = photoEditor;
self.previewView = previewView; self.previewView = previewView;
_fullPreviewView = fullPreviewView;
_scrubberView = scrubberView;
_dotImageView = dotImageView;
} }
return self; return self;
} }
- (void)dealloc {
NSLog(@"");
}
- (void)loadView - (void)loadView
{ {
[super loadView]; [super loadView];
@ -90,7 +84,7 @@ const CGFloat TGPhotoAvatarPreviewLandscapePanelSize = TGPhotoAvatarPreviewPanel
if (strongSelf == nil) if (strongSelf == nil)
return; return;
self.controlVideoPlayback(false); strongSelf.controlVideoPlayback(false);
}; };
void(^interactionEnded)(void) = ^ void(^interactionEnded)(void) = ^
{ {
@ -101,11 +95,12 @@ const CGFloat TGPhotoAvatarPreviewLandscapePanelSize = TGPhotoAvatarPreviewPanel
if ([strongSelf shouldAutorotate]) if ([strongSelf shouldAutorotate])
[TGViewController attemptAutorotation]; [TGViewController attemptAutorotation];
self.controlVideoPlayback(true); strongSelf.controlVideoPlayback(true);
}; };
PGPhotoEditor *photoEditor = self.photoEditor; PGPhotoEditor *photoEditor = self.photoEditor;
_cropView = [[TGPhotoAvatarCropView alloc] initWithOriginalSize:photoEditor.originalSize screenSize:[self referenceViewSize] fullPreviewView:_fullPreviewView]; TGPhotoAvatarCropView *cropView = [[TGPhotoAvatarCropView alloc] initWithOriginalSize:photoEditor.originalSize screenSize:[self referenceViewSize] fullPreviewView:_fullPreviewView];
_cropView = cropView;
[_cropView setCropRect:photoEditor.cropRect]; [_cropView setCropRect:photoEditor.cropRect];
[_cropView setCropOrientation:photoEditor.cropOrientation]; [_cropView setCropOrientation:photoEditor.cropOrientation];
[_cropView setCropMirrored:photoEditor.cropMirrored]; [_cropView setCropMirrored:photoEditor.cropMirrored];
@ -142,7 +137,7 @@ const CGFloat TGPhotoAvatarPreviewLandscapePanelSize = TGPhotoAvatarPreviewPanel
} }
_cropView.interactionBegan = interactionBegan; _cropView.interactionBegan = interactionBegan;
_cropView.interactionEnded = interactionEnded; _cropView.interactionEnded = interactionEnded;
[_wrapperView addSubview:_cropView]; [_wrapperView addSubview:cropView];
_portraitToolsWrapperView = [[UIView alloc] initWithFrame:CGRectZero]; _portraitToolsWrapperView = [[UIView alloc] initWithFrame:CGRectZero];
[_wrapperView addSubview:_portraitToolsWrapperView]; [_wrapperView addSubview:_portraitToolsWrapperView];
@ -162,18 +157,9 @@ const CGFloat TGPhotoAvatarPreviewLandscapePanelSize = TGPhotoAvatarPreviewPanel
_landscapeWrapperBackgroundView.backgroundColor = [TGPhotoEditorInterfaceAssets toolbarTransparentBackgroundColor]; _landscapeWrapperBackgroundView.backgroundColor = [TGPhotoEditorInterfaceAssets toolbarTransparentBackgroundColor];
_landscapeWrapperBackgroundView.userInteractionEnabled = false; _landscapeWrapperBackgroundView.userInteractionEnabled = false;
[_landscapeToolsWrapperView addSubview:_landscapeWrapperBackgroundView]; [_landscapeToolsWrapperView addSubview:_landscapeWrapperBackgroundView];
_videoAreaView = [[UIView alloc] init];
[self.view insertSubview:_videoAreaView belowSubview:_wrapperView];
[_portraitToolsWrapperView addSubview:_scrubberView]; [_portraitToolsWrapperView addSubview:_scrubberView];
_flashView = [[UIView alloc] init];
_flashView.alpha = 0.0;
_flashView.backgroundColor = [UIColor whiteColor];
_flashView.userInteractionEnabled = false;
[_videoAreaView addSubview:_flashView];
_coverLabel = [[UILabel alloc] init]; _coverLabel = [[UILabel alloc] init];
_coverLabel.alpha = 0.7f; _coverLabel.alpha = 0.7f;
_coverLabel.backgroundColor = [UIColor clearColor]; _coverLabel.backgroundColor = [UIColor clearColor];
@ -183,7 +169,6 @@ const CGFloat TGPhotoAvatarPreviewLandscapePanelSize = TGPhotoAvatarPreviewPanel
[_coverLabel sizeToFit]; [_coverLabel sizeToFit];
[_portraitToolsWrapperView addSubview:_coverLabel]; [_portraitToolsWrapperView addSubview:_coverLabel];
_dotImageView.alpha = 1.0f;
[_wrapperView addSubview:_dotImageView]; [_wrapperView addSubview:_dotImageView];
} }
} }
@ -263,7 +248,9 @@ const CGFloat TGPhotoAvatarPreviewLandscapePanelSize = TGPhotoAvatarPreviewPanel
- (void)prepareTransitionInWithReferenceView:(UIView *)referenceView referenceFrame:(CGRect)referenceFrame parentView:(UIView *)parentView noTransitionView:(bool)noTransitionView - (void)prepareTransitionInWithReferenceView:(UIView *)referenceView referenceFrame:(CGRect)referenceFrame parentView:(UIView *)parentView noTransitionView:(bool)noTransitionView
{ {
[super prepareTransitionInWithReferenceView:referenceView referenceFrame:referenceFrame parentView:parentView noTransitionView:noTransitionView]; [super prepareTransitionInWithReferenceView:referenceView referenceFrame:referenceFrame parentView:parentView noTransitionView:noTransitionView];
[self.view insertSubview:_transitionView belowSubview:_wrapperView];
if (self.initialAppearance && self.fromCamera)
[self.view insertSubview:_transitionView belowSubview:_wrapperView];
} }
- (void)transitionIn - (void)transitionIn
@ -282,6 +269,8 @@ const CGFloat TGPhotoAvatarPreviewLandscapePanelSize = TGPhotoAvatarPreviewPanel
{ {
_portraitToolsWrapperView.alpha = 1.0f; _portraitToolsWrapperView.alpha = 1.0f;
_landscapeToolsWrapperView.alpha = 1.0f; _landscapeToolsWrapperView.alpha = 1.0f;
_dotImageView.alpha = 1.0f;
_dotMarkerView.alpha = 1.0f;
} completion:^(BOOL finished) { } completion:^(BOOL finished) {
_scrubberView.layer.shouldRasterize = false; _scrubberView.layer.shouldRasterize = false;
}]; }];
@ -387,6 +376,7 @@ const CGFloat TGPhotoAvatarPreviewLandscapePanelSize = TGPhotoAvatarPreviewPanel
_cropView.transform = CGAffineTransformMakeScale(targetCropViewScale, targetCropViewScale); _cropView.transform = CGAffineTransformMakeScale(targetCropViewScale, targetCropViewScale);
} completion:^(__unused BOOL finished) } completion:^(__unused BOOL finished)
{ {
[_cropView removeFromSuperview];
_previewView.alpha = 1.0; _previewView.alpha = 1.0;
if (self.finishedTransitionOut != nil) if (self.finishedTransitionOut != nil)
self.finishedTransitionOut(); self.finishedTransitionOut();
@ -394,8 +384,9 @@ const CGFloat TGPhotoAvatarPreviewLandscapePanelSize = TGPhotoAvatarPreviewPanel
if (completion != nil) if (completion != nil)
completion(); completion();
}]; }];
} else if (self.fromCamera) { } else {
_previewView.alpha = 0.0f; if (self.fromCamera)
_previewView.alpha = 0.0f;
} }
switch (self.effectiveOrientation) switch (self.effectiveOrientation)
@ -438,9 +429,11 @@ const CGFloat TGPhotoAvatarPreviewLandscapePanelSize = TGPhotoAvatarPreviewPanel
_portraitToolsWrapperView.alpha = 0.0f; _portraitToolsWrapperView.alpha = 0.0f;
_landscapeToolsWrapperView.alpha = 0.0f; _landscapeToolsWrapperView.alpha = 0.0f;
_dotImageView.alpha = 0.0f; _dotImageView.alpha = 0.0f;
_dotMarkerView.alpha = 0.0f;
} completion:^(__unused BOOL finished) } completion:^(__unused BOOL finished)
{ {
if (!switching) { if (!switching) {
[_cropView removeFromSuperview];
if (completion != nil) if (completion != nil)
completion(); completion();
} }
@ -544,17 +537,21 @@ const CGFloat TGPhotoAvatarPreviewLandscapePanelSize = TGPhotoAvatarPreviewPanel
{ {
_portraitToolsWrapperView.alpha = 0.0f; _portraitToolsWrapperView.alpha = 0.0f;
_landscapeToolsWrapperView.alpha = 0.0f; _landscapeToolsWrapperView.alpha = 0.0f;
_videoAreaView.alpha = 0.0f;
_dotImageView.alpha = 0.0f; _dotImageView.alpha = 0.0f;
} completion:nil]; } completion:nil];
} }
- (void)finishCustomTransitionOut
{
[_cropView removeFromSuperview];
}
- (CGRect)transitionOutReferenceFrame - (CGRect)transitionOutReferenceFrame
{ {
if (_dismissingToCamera) { if (_dismissingToCamera) {
return _fullPreviewView.frame; return [_fullPreviewView.superview convertRect:_fullPreviewView.frame toView:self.view];
} else { } else {
return _previewView.frame; return [_wrapperView convertRect:_cropView.frame toView:self.view];
} }
} }
@ -735,12 +732,6 @@ const CGFloat TGPhotoAvatarPreviewLandscapePanelSize = TGPhotoAvatarPreviewPanel
CGRect containerFrame = [TGPhotoAvatarPreviewController photoContainerFrameForParentViewFrame:CGRectMake(0, 0, referenceSize.width, referenceSize.height) toolbarLandscapeSize:self.toolbarLandscapeSize orientation:self.effectiveOrientation panelSize:0 hasOnScreenNavigation:self.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); 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); 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);
[UIView performWithoutAnimation:^
{
_videoAreaView.frame = _previewView.frame;
_flashView.frame = _videoAreaView.bounds;
}];
} }
- (void)updateLayout:(UIInterfaceOrientation)orientation - (void)updateLayout:(UIInterfaceOrientation)orientation
@ -826,29 +817,24 @@ const CGFloat TGPhotoAvatarPreviewLandscapePanelSize = TGPhotoAvatarPreviewPanel
- (void)beginScrubbing:(bool)flash - (void)beginScrubbing:(bool)flash
{ {
if (flash) if (flash) {
_coverLabel.alpha = 1.0f; _coverLabel.alpha = 1.0f;
}
} }
- (void)endScrubbing:(bool)flash completion:(bool (^)(void))completion - (void)endScrubbing:(bool)flash completion:(bool (^)(void))completion
{ {
if (flash) { if (flash) {
[UIView animateWithDuration:0.12 animations:^{ [_cropView flash:^{
_flashView.alpha = 1.0f; TGDispatchAfter(1.0, dispatch_get_main_queue(), ^{
} completion:^(BOOL finished) { if (completion()) {
[UIView animateWithDuration:0.2 animations:^{ [UIView animateWithDuration:0.2 animations:^{
_flashView.alpha = 0.0f; _coverLabel.alpha = 0.7f;
} completion:^(BOOL finished) { }];
TGDispatchAfter(1.0, dispatch_get_main_queue(), ^{
if (completion()) { self.controlVideoPlayback(true);
[UIView animateWithDuration:0.2 animations:^{ }
_coverLabel.alpha = 0.7f; });
}];
self.controlVideoPlayback(true);
}
});
}];
}]; }];
} else { } else {
TGDispatchAfter(1.32, dispatch_get_main_queue(), ^{ TGDispatchAfter(1.32, dispatch_get_main_queue(), ^{

View File

@ -59,6 +59,8 @@
TGPhotoEditorTab _currentTab; TGPhotoEditorTab _currentTab;
TGPhotoEditorTabController *_currentTabController; TGPhotoEditorTabController *_currentTabController;
TGMediaEditingContext *_standaloneEditingContext;
UIView *_backgroundView; UIView *_backgroundView;
UIView *_containerView; UIView *_containerView;
UIView *_wrapperView; UIView *_wrapperView;
@ -140,6 +142,7 @@
{ {
_context = context; _context = context;
_actionHandle = [[ASHandle alloc] initWithDelegate:self releaseOnMainThread:true]; _actionHandle = [[ASHandle alloc] initWithDelegate:self releaseOnMainThread:true];
_standaloneEditingContext = [[TGMediaEditingContext alloc] init];
self.automaticallyManageScrollViewInsets = false; self.automaticallyManageScrollViewInsets = false;
self.autoManageStatusBarBackground = false; self.autoManageStatusBarBackground = false;
@ -561,6 +564,9 @@
if (strongSelf->_ignoreDefaultPreviewViewTransitionIn) if (strongSelf->_ignoreDefaultPreviewViewTransitionIn)
{ {
__strong TGPhotoEditorController *strongSelf = weakSelf;
if (strongSelf == nil)
return;
TGDispatchOnMainThread(^ TGDispatchOnMainThread(^
{ {
if (strongSelf->_dismissed) if (strongSelf->_dismissed)
@ -575,6 +581,9 @@
{ {
[photoEditor processAnimated:false completion:^ [photoEditor processAnimated:false completion:^
{ {
__strong TGPhotoEditorController *strongSelf = weakSelf;
if (strongSelf == nil)
return;
TGDispatchOnMainThread(^ TGDispatchOnMainThread(^
{ {
if (strongSelf->_dismissed) if (strongSelf->_dismissed)
@ -1090,7 +1099,13 @@
rep = imageView; rep = imageView;
} }
[_currentTabController prepareForCustomTransitionOut]; [_currentTabController prepareForCustomTransitionOut];
self.beginCustomTransitionOut([_currentTabController transitionOutReferenceFrame], rep, completion);
TGPhotoEditorTabController *tabController = _currentTabController;
self.beginCustomTransitionOut([_currentTabController transitionOutReferenceFrame], rep, ^{
[tabController finishCustomTransitionOut];
if (completion)
completion();
});
} }
else else
{ {
@ -1125,6 +1140,8 @@
{ {
if (![currentController isDismissAllowed]) if (![currentController isDismissAllowed])
return; return;
[self savePaintingData];
currentController.switchingToTab = tab; currentController.switchingToTab = tab;
[currentController transitionOutSwitching:true completion:^ [currentController transitionOutSwitching:true completion:^
@ -1203,7 +1220,11 @@
{ {
bool skipInitialTransition = (![self presentedFromCamera] && self.navigationController != nil) || self.skipInitialTransition; bool skipInitialTransition = (![self presentedFromCamera] && self.navigationController != nil) || self.skipInitialTransition;
TGPhotoAvatarPreviewController *cropController = [[TGPhotoAvatarPreviewController alloc] initWithContext:_context photoEditor:_photoEditor previewView:_previewView scrubberView:_scrubberView dotImageView:_dotImageView fullPreviewView:_fullPreviewView]; TGPhotoAvatarPreviewController *cropController = [[TGPhotoAvatarPreviewController alloc] initWithContext:_context photoEditor:_photoEditor previewView:_previewView];
cropController.scrubberView = _scrubberView;
cropController.dotImageView = _dotImageView;
cropController.dotMarkerView = _dotMarkerView;
cropController.fullPreviewView = _fullPreviewView;
cropController.fromCamera = [self presentedFromCamera]; cropController.fromCamera = [self presentedFromCamera];
cropController.skipTransitionIn = skipInitialTransition; cropController.skipTransitionIn = skipInitialTransition;
if (snapshotImage != nil) if (snapshotImage != nil)
@ -1636,6 +1657,7 @@
self.view.frame = targetFrame; self.view.frame = targetFrame;
} completion:^(__unused BOOL finished) } completion:^(__unused BOOL finished)
{ {
[_currentTabController finishCustomTransitionOut];
if (self.navigationController != nil) { if (self.navigationController != nil) {
[self.navigationController popViewControllerAnimated:false]; [self.navigationController popViewControllerAnimated:false];
} else { } else {
@ -1646,6 +1668,7 @@
else else
{ {
if (self.navigationController != nil) { if (self.navigationController != nil) {
[_currentTabController finishCustomTransitionOut];
[self.navigationController popViewControllerAnimated:false]; [self.navigationController popViewControllerAnimated:false];
} else { } else {
[self dismiss]; [self dismiss];
@ -1760,6 +1783,18 @@
} }
} }
- (void)savePaintingData {
if (![_currentTabController isKindOfClass:[TGPhotoPaintController class]])
return;
TGPhotoPaintController *paintController = (TGPhotoPaintController *)_currentTabController;
TGPaintingData *paintingData = [paintController paintingData];
_photoEditor.paintingData = paintingData;
if (paintingData != nil)
[TGPaintingData storePaintingData:paintingData inContext:self.editingContext forItem:_item forVideo:(_intent == TGPhotoEditorControllerVideoIntent)];
}
- (void)applyEditor - (void)applyEditor
{ {
if (![_currentTabController isDismissAllowed]) if (![_currentTabController isDismissAllowed])
@ -1768,8 +1803,6 @@
self.view.userInteractionEnabled = false; self.view.userInteractionEnabled = false;
[_currentTabController prepareTransitionOutSaving:true]; [_currentTabController prepareTransitionOutSaving:true];
TGPaintingData *paintingData = _photoEditor.paintingData;
bool saving = true; bool saving = true;
NSTimeInterval videoStartValue = 0.0; NSTimeInterval videoStartValue = 0.0;
NSTimeInterval trimStartValue = 0.0; NSTimeInterval trimStartValue = 0.0;
@ -1777,12 +1810,7 @@
if ([_currentTabController isKindOfClass:[TGPhotoPaintController class]]) if ([_currentTabController isKindOfClass:[TGPhotoPaintController class]])
{ {
TGPhotoPaintController *paintController = (TGPhotoPaintController *)_currentTabController; [self savePaintingData];
paintingData = [paintController paintingData];
_photoEditor.paintingData = paintingData;
if (paintingData != nil)
[TGPaintingData storePaintingData:paintingData inContext:_editingContext forItem:_item forVideo:(_intent == TGPhotoEditorControllerVideoIntent)];
} }
else if ([_currentTabController isKindOfClass:[TGPhotoQualityController class]]) else if ([_currentTabController isKindOfClass:[TGPhotoQualityController class]])
{ {
@ -1800,6 +1828,7 @@
[self stopVideoPlayback:true]; [self stopVideoPlayback:true];
TGPaintingData *paintingData = _photoEditor.paintingData;
TGVideoEditAdjustments *adjustments = [_photoEditor exportAdjustmentsWithPaintingData:paintingData]; TGVideoEditAdjustments *adjustments = [_photoEditor exportAdjustmentsWithPaintingData:paintingData];
if ([self presentedForAvatarCreation] && _item.isVideo) { if ([self presentedForAvatarCreation] && _item.isVideo) {
[[SQueue concurrentDefaultQueue] dispatch:^ [[SQueue concurrentDefaultQueue] dispatch:^
@ -1968,7 +1997,7 @@
thumbnailImage = UIGraphicsGetImageFromCurrentImageContext(); thumbnailImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext(); UIGraphicsEndImageContext();
[_editingContext setImage:fullImage thumbnailImage:thumbnailImage forItem:_item synchronous:true]; [self.editingContext setImage:fullImage thumbnailImage:thumbnailImage forItem:_item synchronous:true];
}]; }];
}]; }];
} }
@ -1990,6 +2019,14 @@
} }
} }
- (TGMediaEditingContext *)editingContext
{
if (_editingContext)
return _editingContext;
else
return _standaloneEditingContext;
}
- (void)doneButtonLongPressed:(UIButton *)sender - (void)doneButtonLongPressed:(UIButton *)sender
{ {
if (_intent == TGPhotoEditorControllerVideoIntent) if (_intent == TGPhotoEditorControllerVideoIntent)
@ -2591,7 +2628,7 @@
return !strongSelf->_scrubberView.isScrubbing; return !strongSelf->_scrubberView.isScrubbing;
}]; }];
dispatch_async(dispatch_get_main_queue(), ^{ TGDispatchAfter(0.16, dispatch_get_main_queue(), ^{
[self updateDotImage:true]; [self updateDotImage:true];
}); });
} }

View File

@ -215,6 +215,11 @@ const CGFloat TGPhotoEditorToolbarSize = 49.0f;
} }
- (void)finishCustomTransitionOut
{
}
- (void)transitionOutSwitching:(bool)__unused switching completion:(void (^)(void))__unused completion - (void)transitionOutSwitching:(bool)__unused switching completion:(void (^)(void))__unused completion
{ {

View File

@ -1842,7 +1842,7 @@ const CGFloat TGPhotoPaintStickerKeyboardSize = 260.0f;
_appeared = true; _appeared = true;
if ([transitionView isKindOfClass:[TGPhotoEditorPreviewView class]]) { if ([transitionView isKindOfClass:[TGPhotoEditorPreviewView class]]) {
[_containerView insertSubview:transitionView belowSubview:_paintingWrapperView];
} else { } else {
[transitionView removeFromSuperview]; [transitionView removeFromSuperview];
} }
@ -1853,6 +1853,7 @@ const CGFloat TGPhotoPaintStickerKeyboardSize = 260.0f;
[previewView setPaintingHidden:true]; [previewView setPaintingHidden:true];
previewView.hidden = false; previewView.hidden = false;
[_containerView insertSubview:previewView belowSubview:_paintingWrapperView]; [_containerView insertSubview:previewView belowSubview:_paintingWrapperView];
[self updateContentViewLayout];
[previewView performTransitionInIfNeeded]; [previewView performTransitionInIfNeeded];
CGRect rect = [self fittedCropRect:true]; CGRect rect = [self fittedCropRect:true];
@ -2142,6 +2143,14 @@ const CGFloat TGPhotoPaintStickerKeyboardSize = 260.0f;
[self updateLayout:toInterfaceOrientation]; [self updateLayout:toInterfaceOrientation];
} }
- (void)updateContentViewLayout
{
CGAffineTransform rotationTransform = CGAffineTransformMakeRotation(TGRotationForOrientation(_photoEditor.cropOrientation));
_contentView.transform = rotationTransform;
_contentView.frame = self.previewView.frame;
[self resetScrollView];
}
- (void)updateLayout:(UIInterfaceOrientation)orientation - (void)updateLayout:(UIInterfaceOrientation)orientation
{ {
if ([self inFormSheet] || [UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPad) if ([self inFormSheet] || [UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPad)

View File

@ -160,8 +160,9 @@ const CGFloat TGPhotoEditorToolsLandscapePanelSize = TGPhotoEditorToolsPanelSize
if (strongSelf != nil) { if (strongSelf != nil) {
[strongSelf->_hudView setText:nil]; [strongSelf->_hudView setText:nil];
if (forVideo) { strongSelf->_photoEditor.disableAll = false;
strongSelf->_photoEditor.disableAll = false; if (!forVideo) {
[strongSelf->_photoEditor processAnimated:false completion:nil];
} }
} }
}; };
@ -171,8 +172,9 @@ const CGFloat TGPhotoEditorToolsLandscapePanelSize = TGPhotoEditorToolsPanelSize
if (strongSelf != nil) { if (strongSelf != nil) {
[strongSelf->_hudView setText:TGLocalized(@"PhotoEditor.Original")]; [strongSelf->_hudView setText:TGLocalized(@"PhotoEditor.Original")];
if (forVideo) { strongSelf->_photoEditor.disableAll = true;
strongSelf->_photoEditor.disableAll = true; if (!forVideo) {
[strongSelf->_photoEditor processAnimated:false completion:nil];
} }
} }
}; };

View File

@ -10,6 +10,59 @@
@implementation TGPhotoVideoEditor @implementation TGPhotoVideoEditor
+ (void)presentWithContext:(id<LegacyComponentsContext>)context parentController:(TGViewController *)parentController image:(UIImage *)image video:(NSURL *)video didFinishWithImage:(void (^)(UIImage *image))didFinishWithImage didFinishWithVideo:(void (^)(UIImage *image, NSURL *url, TGVideoEditAdjustments *adjustments))didFinishWithVideo
{
id<LegacyComponentsOverlayWindowManager> windowManager = [context makeOverlayWindowManager];
id<TGMediaEditableItem> editableItem;
if (image != nil) {
editableItem = image;
} else if (video != nil) {
editableItem = [[TGCameraCapturedVideo alloc] initWithURL:video];
}
TGPhotoEditorController *controller = [[TGPhotoEditorController alloc] initWithContext:[windowManager context] item:editableItem intent:TGPhotoEditorControllerAvatarIntent adjustments:nil caption:nil screenImage:nil availableTabs:[TGPhotoEditorController defaultTabsForAvatarIntent] selectedTab:TGPhotoEditorCropTab];
// controller.stickersContext = _stickersContext;
controller.dontHideStatusBar = true;
controller.didFinishEditing = ^(__unused id<TGMediaEditAdjustments> adjustments, UIImage *resultImage, __unused UIImage *thumbnailImage, __unused bool hasChanges)
{
if (didFinishWithImage != nil)
didFinishWithImage(resultImage);
};
controller.didFinishEditingVideo = ^(NSURL *url, id<TGMediaEditAdjustments> adjustments, UIImage *resultImage, UIImage *thumbnailImage, bool hasChanges) {
if (didFinishWithVideo != nil)
didFinishWithVideo(resultImage, url, adjustments);
};
controller.requestThumbnailImage = ^(id<TGMediaEditableItem> editableItem)
{
return [editableItem thumbnailImageSignal];
};
controller.requestOriginalScreenSizeImage = ^(id<TGMediaEditableItem> editableItem, NSTimeInterval position)
{
return [editableItem screenImageSignal:position];
};
controller.requestOriginalFullSizeImage = ^(id<TGMediaEditableItem> 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];
}
};
TGOverlayControllerWindow *controllerWindow = [[TGOverlayControllerWindow alloc] initWithManager:windowManager parentController:controller contentController:controller];
controllerWindow.hidden = false;
controller.view.clipsToBounds = true;
}
+ (void)presentWithContext:(id<LegacyComponentsContext>)context controller:(TGViewController *)controller caption:(NSString *)caption entities:(NSArray *)entities withItem:(id<TGMediaEditableItem, TGMediaSelectableItem>)item recipientName:(NSString *)recipientName stickersContext:(id<TGPhotoPaintStickersContext>)stickersContext completion:(void (^)(id<TGMediaEditableItem>, TGMediaEditingContext *))completion dismissed:(void (^)())dismissed + (void)presentWithContext:(id<LegacyComponentsContext>)context controller:(TGViewController *)controller caption:(NSString *)caption entities:(NSArray *)entities withItem:(id<TGMediaEditableItem, TGMediaSelectableItem>)item recipientName:(NSString *)recipientName stickersContext:(id<TGPhotoPaintStickersContext>)stickersContext completion:(void (^)(id<TGMediaEditableItem>, TGMediaEditingContext *))completion dismissed:(void (^)())dismissed
{ {
id<LegacyComponentsOverlayWindowManager> windowManager = [context makeOverlayWindowManager]; id<LegacyComponentsOverlayWindowManager> windowManager = [context makeOverlayWindowManager];

View File

@ -6,6 +6,30 @@ import LegacyComponents
import TelegramPresentationData import TelegramPresentationData
import LegacyUI import LegacyUI
public func presentLegacyAvatarEditor(theme: PresentationTheme, image: UIImage?, video: URL?, present: (ViewController, Any?) -> Void, imageCompletion: @escaping (UIImage) -> Void, videoCompletion: @escaping (UIImage, URL, TGVideoEditAdjustments?) -> Void) {
let legacyController = LegacyController(presentation: .custom, theme: theme)
legacyController.statusBar.statusBarStyle = .Ignore
let emptyController = LegacyEmptyController(context: legacyController.context)!
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)
TGPhotoVideoEditor.present(with: legacyController.context, parentController: emptyController, image: image, video: video, didFinishWithImage: { image in
if let image = image {
imageCompletion(image)
}
}, didFinishWithVideo: { image, url, adjustments in
if let image = image, let url = url {
videoCompletion(image, url, adjustments)
}
})
}
public func presentLegacyAvatarPicker(holder: Atomic<NSObject?>, signup: Bool, theme: PresentationTheme, present: (ViewController, Any?) -> Void, openCurrent: (() -> Void)?, completion: @escaping (UIImage) -> Void) { public func presentLegacyAvatarPicker(holder: Atomic<NSObject?>, signup: Bool, theme: PresentationTheme, present: (ViewController, Any?) -> Void, openCurrent: (() -> Void)?, completion: @escaping (UIImage) -> Void) {
let legacyController = LegacyController(presentation: .custom, theme: theme) let legacyController = LegacyController(presentation: .custom, theme: theme)
legacyController.statusBar.statusBarStyle = .Ignore legacyController.statusBar.statusBarStyle = .Ignore

View File

@ -161,7 +161,7 @@ public final class MediaTrackFrameBuffer {
if self.endOfStream, let decodedFrame = self.decoder.takeRemainingFrame() { if self.endOfStream, let decodedFrame = self.decoder.takeRemainingFrame() {
return .frame(decodedFrame) return .frame(decodedFrame)
} else { } else {
if let bufferedUntilTime = bufferedUntilTime { if let bufferedUntilTime = self.bufferedUntilTime {
if CMTimeCompare(bufferedUntilTime, self.duration) >= 0 || self.endOfStream { if CMTimeCompare(bufferedUntilTime, self.duration) >= 0 || self.endOfStream {
return .finished return .finished
} }

View File

@ -21,6 +21,8 @@ static_library(
"//submodules/RadialStatusNode:RadialStatusNode", "//submodules/RadialStatusNode:RadialStatusNode",
"//submodules/ShareController:ShareController", "//submodules/ShareController:ShareController",
"//submodules/AppBundle:AppBundle", "//submodules/AppBundle:AppBundle",
"//submodules/LegacyMediaPickerUI:LegacyMediaPickerUI",
"//submodules/SaveToCameraRoll:SaveToCameraRoll",
], ],
frameworks = [ frameworks = [
"$SDKROOT/System/Library/Frameworks/Foundation.framework", "$SDKROOT/System/Library/Frameworks/Foundation.framework",

View File

@ -22,6 +22,8 @@ swift_library(
"//submodules/RadialStatusNode:RadialStatusNode", "//submodules/RadialStatusNode:RadialStatusNode",
"//submodules/ShareController:ShareController", "//submodules/ShareController:ShareController",
"//submodules/AppBundle:AppBundle", "//submodules/AppBundle:AppBundle",
"//submodules/LegacyMediaPickerUI:LegacyMediaPickerUI",
"//submodules/SaveToCameraRoll:SaveToCameraRoll",
], ],
visibility = [ visibility = [
"//visibility:public", "//visibility:public",

View File

@ -10,6 +10,8 @@ import SyncCore
import TelegramPresentationData import TelegramPresentationData
import AccountContext import AccountContext
import GalleryUI import GalleryUI
import LegacyMediaPickerUI
import SaveToCameraRoll
public enum AvatarGalleryEntryId: Hashable { public enum AvatarGalleryEntryId: Hashable {
case topImage case topImage
@ -192,6 +194,8 @@ public class AvatarGalleryController: ViewController, StandalonePresentableContr
private let replaceRootController: (ViewController, Promise<Bool>?) -> Void private let replaceRootController: (ViewController, Promise<Bool>?) -> Void
private let editDisposable = MetaDisposable ()
public init(context: AccountContext, peer: Peer, sourceHasRoundCorners: Bool = true, remoteEntries: Promise<[AvatarGalleryEntry]>? = nil, centralEntryIndex: Int? = nil, replaceRootController: @escaping (ViewController, Promise<Bool>?) -> Void, synchronousLoad: Bool = false) { public init(context: AccountContext, peer: Peer, sourceHasRoundCorners: Bool = true, remoteEntries: Promise<[AvatarGalleryEntry]>? = nil, centralEntryIndex: Int? = nil, replaceRootController: @escaping (ViewController, Promise<Bool>?) -> Void, synchronousLoad: Bool = false) {
self.context = context self.context = context
self.peer = peer self.peer = peer
@ -265,7 +269,9 @@ public class AvatarGalleryController: ViewController, StandalonePresentableContr
self?.deleteEntry(entry) self?.deleteEntry(entry)
} : nil, setMain: { [weak self] in } : nil, setMain: { [weak self] in
self?.setMainEntry(entry) self?.setMainEntry(entry)
}) }, edit: { [weak self] in
self?.editEntry(entry)
})
}), centralItemIndex: strongSelf.centralEntryIndex, synchronous: !isFirstTime) }), centralItemIndex: strongSelf.centralEntryIndex, synchronous: !isFirstTime)
let ready = strongSelf.galleryNode.pager.ready() |> timeout(2.0, queue: Queue.mainQueue(), alternate: .single(Void())) |> afterNext { [weak strongSelf] _ in let ready = strongSelf.galleryNode.pager.ready() |> timeout(2.0, queue: Queue.mainQueue(), alternate: .single(Void())) |> afterNext { [weak strongSelf] _ in
@ -332,6 +338,7 @@ public class AvatarGalleryController: ViewController, StandalonePresentableContr
deinit { deinit {
self.disposable.dispose() self.disposable.dispose()
self.centralItemAttributesDisposable.dispose() self.centralItemAttributesDisposable.dispose()
self.editDisposable.dispose()
} }
@objc func donePressed() { @objc func donePressed() {
@ -384,6 +391,7 @@ public class AvatarGalleryController: ViewController, StandalonePresentableContr
self.displayNode = GalleryControllerNode(controllerInteraction: controllerInteraction) self.displayNode = GalleryControllerNode(controllerInteraction: controllerInteraction)
self.displayNodeDidLoad() self.displayNodeDidLoad()
self.galleryNode.pager.updateOnReplacement = true
self.galleryNode.statusBar = self.statusBar self.galleryNode.statusBar = self.statusBar
self.galleryNode.navigationBar = self.navigationBar self.galleryNode.navigationBar = self.navigationBar
@ -426,6 +434,8 @@ public class AvatarGalleryController: ViewController, StandalonePresentableContr
self?.deleteEntry(entry) self?.deleteEntry(entry)
} : nil, setMain: { [weak self] in } : nil, setMain: { [weak self] in
self?.setMainEntry(entry) self?.setMainEntry(entry)
}, edit: { [weak self] in
self?.editEntry(entry)
}) }), centralItemIndex: self.centralEntryIndex) }) }), centralItemIndex: self.centralEntryIndex)
self.galleryNode.pager.centralItemIndexUpdated = { [weak self] index in self.galleryNode.pager.centralItemIndexUpdated = { [weak self] index in
@ -584,7 +594,9 @@ public class AvatarGalleryController: ViewController, StandalonePresentableContr
self?.deleteEntry(entry) self?.deleteEntry(entry)
} : nil, setMain: { [weak self] in } : nil, setMain: { [weak self] in
self?.setMainEntry(entry) self?.setMainEntry(entry)
}) }), centralItemIndex: 0) }, edit: { [weak self] in
self?.editEntry(entry)
}) }), centralItemIndex: 0, synchronous: true)
self.entries = entries self.entries = entries
} }
} else { } else {
@ -605,6 +617,50 @@ public class AvatarGalleryController: ViewController, StandalonePresentableContr
} }
} }
private func editEntry(_ rawEntry: AvatarGalleryEntry) {
let mediaReference: AnyMediaReference
if let video = rawEntry.videoRepresentations.last {
mediaReference = .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: [])]))
} else {
let media = TelegramMediaImage(imageId: MediaId(namespace: 0, id: 0), representations: rawEntry.representations.map({ $0.representation }), immediateThumbnailData: nil, reference: nil, partialReference: nil, flags: [])
mediaReference = .standalone(media: media)
}
self.editDisposable.set((fetchMediaData(context: self.context, postbox: self.context.account.postbox, mediaReference: mediaReference)
|> deliverOnMainQueue).start(next: { [weak self] state, isImage in
guard let strongSelf = self else {
return
}
switch state {
case let .progress(value):
break
case let .data(data):
let image: UIImage?
let video: URL?
if isImage {
if let fileData = try? Data(contentsOf: URL(fileURLWithPath: data.path)) {
image = UIImage(data: fileData)
} else {
image = nil
}
video = nil
} else {
image = nil
video = URL(fileURLWithPath: data.path)
}
presentLegacyAvatarEditor(theme: strongSelf.presentationData.theme, image: image, video: video, present: { [weak self] c, a in
if let strongSelf = self {
strongSelf.present(c, in: .window(.root), with: a, blockInteraction: true)
}
}, imageCompletion: { [weak self] image in
}, videoCompletion: { [weak self] image, url, adjustments in
})
}
}))
}
private func deleteEntry(_ rawEntry: AvatarGalleryEntry) { private func deleteEntry(_ rawEntry: AvatarGalleryEntry) {
var entry = rawEntry var entry = rawEntry
if case .topImage = entry, !self.entries.isEmpty { if case .topImage = entry, !self.entries.isEmpty {

View File

@ -54,8 +54,9 @@ class PeerAvatarImageGalleryItem: GalleryItem {
let sourceHasRoundCorners: Bool let sourceHasRoundCorners: Bool
let delete: (() -> Void)? let delete: (() -> Void)?
let setMain: (() -> Void)? let setMain: (() -> Void)?
let edit: (() -> Void)?
init(context: AccountContext, peer: Peer, presentationData: PresentationData, entry: AvatarGalleryEntry, sourceHasRoundCorners: Bool, delete: (() -> Void)?, setMain: (() -> Void)?) { init(context: AccountContext, peer: Peer, presentationData: PresentationData, entry: AvatarGalleryEntry, sourceHasRoundCorners: Bool, delete: (() -> Void)?, setMain: (() -> Void)?, edit: (() -> Void)?) {
self.context = context self.context = context
self.peer = peer self.peer = peer
self.presentationData = presentationData self.presentationData = presentationData
@ -63,6 +64,7 @@ class PeerAvatarImageGalleryItem: GalleryItem {
self.sourceHasRoundCorners = sourceHasRoundCorners self.sourceHasRoundCorners = sourceHasRoundCorners
self.delete = delete self.delete = delete
self.setMain = setMain self.setMain = setMain
self.edit = edit
} }
func node(synchronous: Bool) -> GalleryItemNode { func node(synchronous: Bool) -> GalleryItemNode {
@ -75,6 +77,7 @@ class PeerAvatarImageGalleryItem: GalleryItem {
node.setEntry(self.entry, synchronous: synchronous) node.setEntry(self.entry, synchronous: synchronous)
node.footerContentNode.delete = self.delete node.footerContentNode.delete = self.delete
node.footerContentNode.setMain = self.setMain node.footerContentNode.setMain = self.setMain
node.edit = self.edit
return node return node
} }
@ -84,10 +87,17 @@ class PeerAvatarImageGalleryItem: GalleryItem {
if let indexData = self.entry.indexData { if let indexData = self.entry.indexData {
node._title.set(.single(self.presentationData.strings.Items_NOfM("\(indexData.position + 1)", "\(indexData.totalCount)").0)) node._title.set(.single(self.presentationData.strings.Items_NOfM("\(indexData.position + 1)", "\(indexData.totalCount)").0))
} }
let previousContentAnimations = node.imageNode.contentAnimations
if synchronous {
node.imageNode.contentAnimations = []
}
node.setEntry(self.entry, synchronous: synchronous) node.setEntry(self.entry, synchronous: synchronous)
if synchronous {
node.imageNode.contentAnimations = previousContentAnimations
}
node.footerContentNode.delete = self.delete node.footerContentNode.delete = self.delete
node.footerContentNode.setMain = self.setMain node.footerContentNode.setMain = self.setMain
node.edit = self.edit
} }
} }
@ -125,7 +135,7 @@ final class PeerAvatarImageGalleryItemNode: ZoomableContentGalleryItemNode {
private var entry: AvatarGalleryEntry? private var entry: AvatarGalleryEntry?
private let contentNode: PeerAvatarImageGalleryContentNode private let contentNode: PeerAvatarImageGalleryContentNode
private let imageNode: TransformImageNode fileprivate let imageNode: TransformImageNode
private var videoNode: UniversalVideoNode? private var videoNode: UniversalVideoNode?
private var videoContent: NativeVideoContent? private var videoContent: NativeVideoContent?
@ -142,6 +152,8 @@ final class PeerAvatarImageGalleryItemNode: ZoomableContentGalleryItemNode {
private var status: MediaResourceStatus? private var status: MediaResourceStatus?
private let playbackStatusDisposable = MetaDisposable() private let playbackStatusDisposable = MetaDisposable()
fileprivate var edit: (() -> Void)?
init(context: AccountContext, presentationData: PresentationData, peer: Peer, sourceHasRoundCorners: Bool) { init(context: AccountContext, presentationData: PresentationData, peer: Peer, sourceHasRoundCorners: Bool) {
self.context = context self.context = context
self.presentationData = presentationData self.presentationData = presentationData
@ -212,7 +224,7 @@ final class PeerAvatarImageGalleryItemNode: ZoomableContentGalleryItemNode {
var footerContent: AvatarGalleryItemFooterContent var footerContent: AvatarGalleryItemFooterContent
if self.peer.id == self.context.account.peerId { if self.peer.id == self.context.account.peerId {
footerContent = .own((entry.indexData?.position ?? 0) == 0) footerContent = .own((entry.indexData?.position ?? 0) == 0)
let rightBarButtonItem = UIBarButtonItem(title: self.presentationData.strings.Settings_EditProfileMedia, style: .plain, target: self, action: #selector(editPressed)) let rightBarButtonItem = UIBarButtonItem(title: self.presentationData.strings.Settings_EditProfileMedia, style: .plain, target: self, action: #selector(self.editPressed))
barButtonItems.append(rightBarButtonItem) barButtonItems.append(rightBarButtonItem)
} else { } else {
footerContent = .info footerContent = .info
@ -507,7 +519,7 @@ final class PeerAvatarImageGalleryItemNode: ZoomableContentGalleryItemNode {
} }
@objc private func editPressed() { @objc private func editPressed() {
self.edit?()
} }
override func footerContent() -> Signal<(GalleryFooterContentNode?, GalleryOverlayContentNode?), NoError> { override func footerContent() -> Signal<(GalleryFooterContentNode?, GalleryOverlayContentNode?), NoError> {