Video avatar fixes

This commit is contained in:
Ilya Laktyushin 2020-06-25 18:20:36 +03:00
parent 9d435f3f25
commit 4fadec2ff3
31 changed files with 3070 additions and 2974 deletions

View File

@ -5548,12 +5548,15 @@ Any member of this group will be able to see messages in the channel.";
"PhotoEditor.BlurToolPortrait" = "Portrait"; "PhotoEditor.BlurToolPortrait" = "Portrait";
"PhotoEditor.SelectCoverFrame" = "Choose a cover for your profile video"; "PhotoEditor.SelectCoverFrame" = "Choose a cover for your profile video";
"Conversation.PeerNearbyTitle" = "%@ is %@ away"; "Conversation.PeerNearbyTitle" = "%1$@ is %2$@ away";
"Conversation.PeerNearbyText" = "Send a message or tap on the greeting below to show that you are ready to chat."; "Conversation.PeerNearbyText" = "Send a message or tap on the greeting below to show that you are ready to chat.";
"Conversation.PeerNearbyDistance" = "%@ is %@ away"; "Conversation.PeerNearbyDistance" = "%1$@ is %2$@ away";
"ProfilePhoto.MainPhoto" = "Main Photo"; "ProfilePhoto.MainPhoto" = "Main Photo";
"ProfilePhoto.SetMain" = "Set as Main Photo"; "ProfilePhoto.SetMainPhoto" = "Set as Main Photo";
"ProfilePhoto.MainVideo" = "Main Photo";
"ProfilePhoto.SetMainVideo" = "Set as Main Photo";
"Stats.GroupOverview" = "OVERVIEW"; "Stats.GroupOverview" = "OVERVIEW";
"Stats.GroupMembers" = "Members"; "Stats.GroupMembers" = "Members";

View File

@ -204,13 +204,20 @@ public final class ChatPeekTimeout {
public final class ChatPeerNearbyData: Equatable { public final class ChatPeerNearbyData: Equatable {
public static func == (lhs: ChatPeerNearbyData, rhs: ChatPeerNearbyData) -> Bool { public static func == (lhs: ChatPeerNearbyData, rhs: ChatPeerNearbyData) -> Bool {
if let lhsSticker = lhs.sticker, let rhsSticker = rhs.sticker, !lhsSticker.isEqual(to: rhsSticker) {
return false
} else if (lhs.sticker == nil) != (rhs.sticker == nil) {
return false
}
return lhs.distance == rhs.distance return lhs.distance == rhs.distance
} }
public let distance: Int32 public let distance: Int32
public let sticker: TelegramMediaFile?
public init(distance: Int32) { public init(distance: Int32, sticker: TelegramMediaFile?) {
self.distance = distance self.distance = distance
self.sticker = sticker
} }
} }

View File

@ -70,6 +70,8 @@ public final class GalleryThumbnailContainerNode: ASDisplayNode, UIScrollViewDel
self.scrollNode.view.delegate = self self.scrollNode.view.delegate = self
self.scrollNode.view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.tapGesture(_:)))) self.scrollNode.view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.tapGesture(_:))))
self.scrollNode.view.showsHorizontalScrollIndicator = false
self.scrollNode.view.showsVerticalScrollIndicator = false
self.addSubnode(self.scrollNode) self.addSubnode(self.scrollNode)
} }
@ -88,7 +90,7 @@ public final class GalleryThumbnailContainerNode: ASDisplayNode, UIScrollViewDel
public func updateItems(_ items: [GalleryThumbnailItem], indexes: [Int], centralIndex: Int, progress: CGFloat) { public func updateItems(_ items: [GalleryThumbnailItem], indexes: [Int], centralIndex: Int, progress: CGFloat) {
self.indexes = indexes self.indexes = indexes
var items: [GalleryThumbnailItem] = items.count <= 1 ? [] : items let items: [GalleryThumbnailItem] = items.count <= 1 ? [] : items
var updated = false var updated = false
if self.items.count == items.count { if self.items.count == items.count {
for i in 0 ..< self.items.count { for i in 0 ..< self.items.count {

View File

@ -58,8 +58,6 @@ typedef enum {
@property (nonatomic, strong) PGCameraShotMetadata *metadata; @property (nonatomic, strong) PGCameraShotMetadata *metadata;
@property (nonatomic, strong) NSArray *faces; @property (nonatomic, strong) NSArray *faces;
@property (nonatomic, strong) NSArray *cachedVideoThumbnails;
@property (nonatomic, strong) AVPlayer *player; @property (nonatomic, strong) AVPlayer *player;
@property (nonatomic, strong) TGPhotoEntitiesContainerView *entitiesView; @property (nonatomic, strong) TGPhotoEntitiesContainerView *entitiesView;

View File

@ -16,6 +16,7 @@
- (void)prepareToRecord; - (void)prepareToRecord;
- (void)appendVideoPixelBuffer:(CVPixelBufferRef)pixelBuffer withPresentationTime:(CMTime)presentationTime; - (void)appendVideoPixelBuffer:(CVPixelBufferRef)pixelBuffer withPresentationTime:(CMTime)presentationTime;
- (void)appendVideoSampleBuffer:(CMSampleBufferRef)sampleBuffer;
- (void)appendAudioSampleBuffer:(CMSampleBufferRef)sampleBuffer; - (void)appendAudioSampleBuffer:(CMSampleBufferRef)sampleBuffer;
- (void)finishRecording:(void(^)())completed; - (void)finishRecording:(void(^)())completed;

View File

@ -22,6 +22,7 @@
@property (nonatomic, assign) TGMediaVideoConversionPreset preset; @property (nonatomic, assign) TGMediaVideoConversionPreset preset;
@property (nonatomic, weak) TGPhotoEditorPreviewView *previewOutput; @property (nonatomic, weak) TGPhotoEditorPreviewView *previewOutput;
@property (nonatomic, strong) NSArray *additionalOutputs;
@property (nonatomic, readonly) NSArray *tools; @property (nonatomic, readonly) NSArray *tools;
@property (nonatomic, readonly) bool processing; @property (nonatomic, readonly) bool processing;

View File

@ -414,12 +414,34 @@
[_finalFilter addTarget:previewOutput.imageView]; [_finalFilter addTarget:previewOutput.imageView];
} }
for (PGPhotoEditorView *view in _additionalOutputs) {
[_finalFilter addTarget:view];
}
if (_histogramGenerator != nil && !self.standalone) { if (_histogramGenerator != nil && !self.standalone) {
[_finalFilter addTarget:_histogramGenerator]; [_finalFilter addTarget:_histogramGenerator];
} }
} }
} }
- (void)setAdditionalOutputs:(NSArray *)additionalOutputs {
_additionalOutputs = additionalOutputs;
[_finalFilter removeAllTargets];
if (self.previewOutput != nil) {
[_finalFilter addTarget:self.previewOutput.imageView];
}
for (PGPhotoEditorView *view in _additionalOutputs) {
[_finalFilter addTarget:view];
}
if (_histogramGenerator != nil && !self.standalone) {
[_finalFilter addTarget:_histogramGenerator];
}
}
#pragma mark - Result #pragma mark - Result
- (void)createResultImageWithCompletion:(void (^)(UIImage *image))completion - (void)createResultImageWithCompletion:(void (^)(UIImage *image))completion

View File

@ -100,7 +100,6 @@
glEnableVertexAttribArray(displayTextureCoordinateAttribute); glEnableVertexAttribArray(displayTextureCoordinateAttribute);
[self setBackgroundColorRed:0.0 green:0.0 blue:0.0 alpha:1.0]; [self setBackgroundColorRed:0.0 green:0.0 blue:0.0 alpha:1.0];
// _fillMode = kGPUImageFillModePreserveAspectRatio;
[self createDisplayFramebuffer]; [self createDisplayFramebuffer];
}); });
} }

View File

@ -912,7 +912,7 @@ static CGPoint TGCameraControllerClampPointToScreenSize(__unused id self, __unus
{ {
case PGCameraModePhoto: case PGCameraModePhoto:
{ {
if (_intent == TGCameraControllerGenericIntent) if (_intent == TGCameraControllerGenericIntent || _intent == TGCameraControllerAvatarIntent)
{ {
_switchToVideoTimer = [TGTimerTarget scheduledMainThreadTimerWithTarget:self action:@selector(startVideoRecording) interval:0.25 repeat:false]; _switchToVideoTimer = [TGTimerTarget scheduledMainThreadTimerWithTarget:self action:@selector(startVideoRecording) interval:0.25 repeat:false];
} }
@ -2288,7 +2288,7 @@ static CGPoint TGCameraControllerClampPointToScreenSize(__unused id self, __unus
if (gestureRecognizer == _panGestureRecognizer) if (gestureRecognizer == _panGestureRecognizer)
return !_camera.isRecordingVideo && _items.count == 0; return !_camera.isRecordingVideo && _items.count == 0;
else if (gestureRecognizer == _photoSwipeGestureRecognizer || gestureRecognizer == _videoSwipeGestureRecognizer) else if (gestureRecognizer == _photoSwipeGestureRecognizer || gestureRecognizer == _videoSwipeGestureRecognizer)
return _intent == TGCameraControllerGenericIntent && !_camera.isRecordingVideo; return (_intent == TGCameraControllerGenericIntent || _intent == TGCameraControllerAvatarIntent) && !_camera.isRecordingVideo;
else if (gestureRecognizer == _pinchGestureRecognizer) else if (gestureRecognizer == _pinchGestureRecognizer)
return _camera.isZoomAvailable; return _camera.isZoomAvailable;

View File

@ -40,12 +40,14 @@ typedef enum
UIControl *_scrubberHandle; UIControl *_scrubberHandle;
UIControl *_dotHandle; UIControl *_dotHandle;
UIView *_dotContentView;
UIImageView *_dotImageView; UIImageView *_dotImageView;
__weak UIView *_dotVideoView; __weak UIView *_dotVideoView;
UIImageView *_dotFrameView; UIImageView *_dotFrameView;
UIPanGestureRecognizer *_panGestureRecognizer; UIPanGestureRecognizer *_panGestureRecognizer;
UILongPressGestureRecognizer *_pressGestureRecognizer; UILongPressGestureRecognizer *_pressGestureRecognizer;
UITapGestureRecognizer *_tapGestureRecognizer;
bool _beganInteraction; bool _beganInteraction;
bool _endedInteraction; bool _endedInteraction;
@ -235,10 +237,7 @@ typedef enum
CGFloat delta = originX - trimView.frame.origin.x; CGFloat delta = originX - trimView.frame.origin.x;
CGFloat maxWidth = availableTrimRect.size.width + normalScrubbingRect.origin.x * 2 - originX; CGFloat maxWidth = availableTrimRect.size.width + normalScrubbingRect.origin.x * 2 - originX;
CGRect trimViewRect = CGRectMake(originX, CGRect trimViewRect = CGRectMake(originX, trimView.frame.origin.y, MIN(maxWidth, trimView.frame.size.width - delta), trimView.frame.size.height);
trimView.frame.origin.y,
MIN(maxWidth, trimView.frame.size.width - delta),
trimView.frame.size.height);
NSTimeInterval trimStartPosition = 0.0; NSTimeInterval trimStartPosition = 0.0;
NSTimeInterval trimEndPosition = 0.0; NSTimeInterval trimEndPosition = 0.0;
@ -251,10 +250,7 @@ typedef enum
if (strongSelf.maximumLength > DBL_EPSILON && duration > strongSelf.maximumLength) if (strongSelf.maximumLength > DBL_EPSILON && duration > strongSelf.maximumLength)
{ {
trimViewRect = CGRectMake(trimView.frame.origin.x + delta, trimViewRect = CGRectMake(trimView.frame.origin.x + delta, trimView.frame.origin.y, trimView.frame.size.width, trimView.frame.size.height);
trimView.frame.origin.y,
trimView.frame.size.width,
trimView.frame.size.height);
[strongSelf _trimStartPosition:&trimStartPosition trimEndPosition:&trimEndPosition forTrimFrame:trimViewRect duration:strongSelf.duration]; [strongSelf _trimStartPosition:&trimStartPosition trimEndPosition:&trimEndPosition forTrimFrame:trimViewRect duration:strongSelf.duration];
} }
@ -276,6 +272,9 @@ typedef enum
UIView *handle = strongSelf->_scrubberHandle; UIView *handle = strongSelf->_scrubberHandle;
handle.center = CGPointMake(trimView.frame.origin.x + 12 + handle.frame.size.width / 2, handle.center.y); handle.center = CGPointMake(trimView.frame.origin.x + 12 + handle.frame.size.width / 2, handle.center.y);
UIView *dotHandle = strongSelf->_dotHandle;
dotHandle.center = CGPointMake(trimView.frame.origin.x + 12 + dotHandle.frame.size.width / 2, dotHandle.center.y);
id<TGMediaPickerGalleryVideoScrubberDelegate> delegate = strongSelf.delegate; id<TGMediaPickerGalleryVideoScrubberDelegate> delegate = strongSelf.delegate;
if ([delegate respondsToSelector:@selector(videoScrubber:editingStartValueDidChange:)]) if ([delegate respondsToSelector:@selector(videoScrubber:editingStartValueDidChange:)])
[delegate videoScrubber:strongSelf editingStartValueDidChange:trimStartPosition]; [delegate videoScrubber:strongSelf editingStartValueDidChange:trimStartPosition];
@ -345,6 +344,9 @@ typedef enum
UIView *handle = strongSelf->_scrubberHandle; UIView *handle = strongSelf->_scrubberHandle;
handle.center = CGPointMake(CGRectGetMaxX(trimView.frame) - 12 - handle.frame.size.width / 2, handle.center.y); handle.center = CGPointMake(CGRectGetMaxX(trimView.frame) - 12 - handle.frame.size.width / 2, handle.center.y);
UIView *dotHandle = strongSelf->_dotHandle;
dotHandle.center = CGPointMake(CGRectGetMaxX(trimView.frame) - 12 + dotHandle.frame.size.width / 2, dotHandle.center.y);
id<TGMediaPickerGalleryVideoScrubberDelegate> delegate = strongSelf.delegate; id<TGMediaPickerGalleryVideoScrubberDelegate> delegate = strongSelf.delegate;
if ([delegate respondsToSelector:@selector(videoScrubber:editingEndValueDidChange:)]) if ([delegate respondsToSelector:@selector(videoScrubber:editingEndValueDidChange:)])
[delegate videoScrubber:strongSelf editingEndValueDidChange:trimEndPosition]; [delegate videoScrubber:strongSelf editingEndValueDidChange:trimEndPosition];
@ -380,10 +382,14 @@ typedef enum
UIGraphicsEndImageContext(); UIGraphicsEndImageContext();
}); });
_dotImageView = [[UIImageView alloc] initWithFrame:CGRectInset(_dotHandle.bounds, 2.0, 2.0)]; _dotContentView = [[UIView alloc] initWithFrame:CGRectInset(_dotHandle.bounds, 2.0, 2.0)];
_dotContentView.clipsToBounds = true;
[_dotHandle addSubview:_dotContentView];
_dotImageView = [[UIImageView alloc] initWithFrame:_dotContentView.bounds];
_dotImageView.clipsToBounds = true; _dotImageView.clipsToBounds = true;
_dotImageView.contentMode = UIViewContentModeScaleAspectFill; _dotImageView.contentMode = UIViewContentModeScaleAspectFill;
[_dotHandle addSubview:_dotImageView]; [_dotContentView addSubview:_dotImageView];
_dotFrameView = [[UIImageView alloc] initWithFrame:_dotHandle.bounds]; _dotFrameView = [[UIImageView alloc] initWithFrame:_dotHandle.bounds];
_dotFrameView.image = dotFrameImage; _dotFrameView.image = dotFrameImage;
@ -423,6 +429,10 @@ typedef enum
[_scrubberHandle addGestureRecognizer:_panGestureRecognizer]; [_scrubberHandle addGestureRecognizer:_panGestureRecognizer];
[_dotHandle addGestureRecognizer:_panGestureRecognizer]; [_dotHandle addGestureRecognizer:_panGestureRecognizer];
_tapGestureRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleTap:)];
_tapGestureRecognizer.enabled = false;
[_trimView addGestureRecognizer:_tapGestureRecognizer];
_arrowView = [[UIImageView alloc] initWithImage:TGComponentsImageNamed(@"PhotoPickerArrow")]; _arrowView = [[UIImageView alloc] initWithImage:TGComponentsImageNamed(@"PhotoPickerArrow")];
_arrowView.alpha = 0.45f; _arrowView.alpha = 0.45f;
_arrowView.hidden = true; _arrowView.hidden = true;
@ -453,12 +463,13 @@ typedef enum
_hasDotPicker = hasDotPicker; _hasDotPicker = hasDotPicker;
_dotHandle.hidden = !hasDotPicker; _dotHandle.hidden = !hasDotPicker;
_scrubberHandle.hidden = true; _scrubberHandle.hidden = true;
_tapGestureRecognizer.enabled = hasDotPicker;
} }
- (void)setDotVideoView:(UIView *)dotVideoView { - (void)setDotVideoView:(UIView *)dotVideoView {
_dotVideoView = dotVideoView; _dotVideoView = dotVideoView;
_dotVideoView.frame = CGRectInset(_dotHandle.bounds, 2.0, 2.0); _dotVideoView.frame = _dotImageView.bounds;
[_dotHandle insertSubview:dotVideoView belowSubview:_dotFrameView]; [_dotContentView addSubview:_dotVideoView];
} }
- (void)setDotImage:(UIImage *)dotImage { - (void)setDotImage:(UIImage *)dotImage {
@ -677,6 +688,11 @@ typedef enum
_thumbnailAspectRatio = frameAspectRatio; _thumbnailAspectRatio = frameAspectRatio;
if (_hasDotPicker) {
CGSize videoSize = TGFillSize([self _thumbnailSize], _dotImageView.frame.size);
_dotImageView.frame = CGRectMake(TGScreenPixelFloor((_dotImageView.frame.size.width - videoSize.width) / 2.0), 0.0, videoSize.width, videoSize.height);
}
NSInteger thumbnailCount = (NSInteger)CGCeil(_summaryThumbnailWrapperView.frame.size.width / [self _thumbnailSizeWithAspectRatio:frameAspectRatio orientation:_cropOrientation].width); NSInteger thumbnailCount = (NSInteger)CGCeil(_summaryThumbnailWrapperView.frame.size.width / [self _thumbnailSizeWithAspectRatio:frameAspectRatio orientation:_cropOrientation].width);
if ([dataSource respondsToSelector:@selector(videoScrubber:evenlySpacedTimestamps:startingAt:endingAt:)]) if ([dataSource respondsToSelector:@selector(videoScrubber:evenlySpacedTimestamps:startingAt:endingAt:)])
@ -1200,6 +1216,30 @@ typedef enum
} }
} }
- (void)handleTap:(UITapGestureRecognizer *)gestureRecognizer
{
CGPoint location = [gestureRecognizer locationInView:_dotHandle.superview];
CGFloat position = MAX(_trimStartValue, MIN(_trimEndValue, [self _positionForScrubberPosition:location duration:_duration]));
_value = position;
CGPoint center = [self _dotPositionForPosition:position duration:_duration];
[UIView animateWithDuration:0.2 delay:0.0 usingSpringWithDamping:1.1 initialSpringVelocity:0.0 options:kNilOptions animations:^{
_dotHandle.center = CGPointMake(center.x, _dotHandle.center.y);
} completion:^(BOOL finished) {
}];
id<TGMediaPickerGalleryVideoScrubberDelegate> delegate = self.delegate;
if ([delegate respondsToSelector:@selector(videoScrubberDidBeginScrubbing:)])
[delegate videoScrubberDidBeginScrubbing:self];
if ([delegate respondsToSelector:@selector(videoScrubber:valueDidChange:)])
[delegate videoScrubber:self valueDidChange:position];
if ([delegate respondsToSelector:@selector(videoScrubberDidEndScrubbing:)])
[delegate videoScrubberDidEndScrubbing:self];
}
- (void)setScrubberHandleHidden:(bool)hidden animated:(bool)animated - (void)setScrubberHandleHidden:(bool)hidden animated:(bool)animated
{ {
if (animated) if (animated)

View File

@ -4,4 +4,6 @@
- (instancetype)initWithImage:(UIImage *)image originalSize:(CGSize)originalSize cropRect:(CGRect)cropRect cropOrientation:(UIImageOrientation)cropOrientation cropMirrored:(bool)cropMirrored; - (instancetype)initWithImage:(UIImage *)image originalSize:(CGSize)originalSize cropRect:(CGRect)cropRect cropOrientation:(UIImageOrientation)cropOrientation cropMirrored:(bool)cropMirrored;
- (void)updateCropping;
@end @end

View File

@ -56,6 +56,10 @@
if (_imageView == nil) if (_imageView == nil)
return; return;
[self updateCropping];
}
- (void)updateCropping {
CGAffineTransform transform = CGAffineTransformMakeRotation(TGRotationForOrientation(_cropOrientation)); CGAffineTransform transform = CGAffineTransformMakeRotation(TGRotationForOrientation(_cropOrientation));
if (_cropMirrored) if (_cropMirrored)
transform = CGAffineTransformScale(transform, -1.0f, 1.0f); transform = CGAffineTransformScale(transform, -1.0f, 1.0f);
@ -79,11 +83,11 @@
cropRect = CGRectMake(originalSize.width - cropRect.size.width - cropRect.origin.x, originalSize.height - cropRect.size.height - cropRect.origin.y, cropRect.size.width, cropRect.size.height); cropRect = CGRectMake(originalSize.width - cropRect.size.width - cropRect.origin.x, originalSize.height - cropRect.size.height - cropRect.origin.y, cropRect.size.width, cropRect.size.height);
} }
CGFloat ratio = frame.size.width / cropRect.size.width; CGFloat ratio = self.frame.size.width / cropRect.size.width;
_imageView.frame = CGRectMake(-cropRect.origin.x * ratio, -cropRect.origin.y * ratio, originalSize.width * ratio, originalSize.height * ratio); _imageView.frame = CGRectMake(-cropRect.origin.x * ratio, -cropRect.origin.y * ratio, originalSize.width * ratio, originalSize.height * ratio);
CGFloat thickness = 1.0f - TGRetinaPixel; CGFloat thickness = 1.0f - TGRetinaPixel;
_stripeView.frame = CGRectMake(frame.size.width - thickness, 0, thickness, frame.size.height); _stripeView.frame = CGRectMake(self.frame.size.width - thickness, 0, thickness, self.frame.size.height);
} }
@end @end

View File

@ -342,22 +342,18 @@ const CGFloat TGPhotoAvatarCropButtonsWrapperSize = 61.0f;
UIInterfaceOrientation orientation = self.effectiveOrientation; UIInterfaceOrientation orientation = self.effectiveOrientation;
bool hasOnScreenNavigation = false;
if (iosMajorVersion() >= 11)
hasOnScreenNavigation = (self.viewLoaded && self.view.safeAreaInsets.bottom > FLT_EPSILON) || self.context.safeAreaInset.bottom > FLT_EPSILON;
CGRect cropRectFrame = [_cropView cropRectFrameForView:self.view]; CGRect cropRectFrame = [_cropView cropRectFrameForView:self.view];
CGSize referenceSize = [self referenceViewSizeForOrientation:orientation]; CGSize referenceSize = [self referenceViewSizeForOrientation:orientation];
CGRect referenceBounds = CGRectMake(0, 0, referenceSize.width, referenceSize.height); CGRect referenceBounds = CGRectMake(0, 0, referenceSize.width, referenceSize.height);
CGRect containerFrame = [TGPhotoEditorTabController photoContainerFrameForParentViewFrame:referenceBounds toolbarLandscapeSize:self.toolbarLandscapeSize orientation:orientation panelSize:TGPhotoEditorPanelSize hasOnScreenNavigation:hasOnScreenNavigation]; CGRect containerFrame = [TGPhotoEditorTabController photoContainerFrameForParentViewFrame:referenceBounds toolbarLandscapeSize:self.toolbarLandscapeSize orientation:orientation panelSize:TGPhotoEditorPanelSize hasOnScreenNavigation:self.hasOnScreenNavigation];
if (self.switchingToTab == TGPhotoEditorPreviewTab) if (self.switchingToTab == TGPhotoEditorPreviewTab)
{ {
containerFrame = [TGPhotoEditorTabController photoContainerFrameForParentViewFrame:referenceBounds toolbarLandscapeSize:self.toolbarLandscapeSize orientation:orientation panelSize:0 hasOnScreenNavigation:hasOnScreenNavigation]; containerFrame = [TGPhotoEditorTabController photoContainerFrameForParentViewFrame:referenceBounds toolbarLandscapeSize:self.toolbarLandscapeSize orientation:orientation panelSize:0 hasOnScreenNavigation:self.hasOnScreenNavigation];
} }
else if (self.switchingToTab == TGPhotoEditorPaintTab) else if (self.switchingToTab == TGPhotoEditorPaintTab)
{ {
containerFrame = [TGPhotoPaintController photoContainerFrameForParentViewFrame:referenceBounds toolbarLandscapeSize:self.toolbarLandscapeSize orientation:orientation panelSize:TGPhotoPaintTopPanelSize + TGPhotoPaintBottomPanelSize hasOnScreenNavigation:hasOnScreenNavigation]; containerFrame = [TGPhotoPaintController photoContainerFrameForParentViewFrame:referenceBounds toolbarLandscapeSize:self.toolbarLandscapeSize orientation:orientation panelSize:TGPhotoPaintTopPanelSize + TGPhotoPaintBottomPanelSize hasOnScreenNavigation:self.hasOnScreenNavigation];
} }
CGSize fittedSize = TGScaleToSize(cropRectFrame.size, containerFrame.size); CGSize fittedSize = TGScaleToSize(cropRectFrame.size, containerFrame.size);

View File

@ -3,16 +3,14 @@
@class PGPhotoEditor; @class PGPhotoEditor;
@class PGPhotoTool; @class PGPhotoTool;
@class TGPhotoEditorPreviewView; @class TGPhotoEditorPreviewView;
@class TGMediaPickerGalleryVideoScrubber;
@interface TGPhotoAvatarPreviewController : TGPhotoEditorTabController @interface TGPhotoAvatarPreviewController : TGPhotoEditorTabController
- (instancetype)initWithContext:(id<LegacyComponentsContext>)context photoEditor:(PGPhotoEditor *)photoEditor previewView:(TGPhotoEditorPreviewView *)previewView; - (instancetype)initWithContext:(id<LegacyComponentsContext>)context photoEditor:(PGPhotoEditor *)photoEditor previewView:(TGPhotoEditorPreviewView *)previewView scrubberView:(TGMediaPickerGalleryVideoScrubber *)scrubberView;
- (void)setScrubberPosition:(NSTimeInterval)position reset:(bool)reset; - (void)beginScrubbing;
- (void)setScrubberPlaying:(bool)value; - (void)endScrubbing:(bool (^)(void))completion;
- (void)setPlayButtonHidden:(bool)hidden animated:(bool)animated;
- (NSTimeInterval)coverPosition;
- (NSTimeInterval)trimStartValue;
- (NSTimeInterval)trimEndValue;
@end @end

View File

@ -6,6 +6,7 @@
#import "TGPhotoEditorInterfaceAssets.h" #import "TGPhotoEditorInterfaceAssets.h"
#import "PGPhotoEditor.h" #import "PGPhotoEditor.h"
#import "PGPhotoEditorView.h"
#import <LegacyComponents/TGPhotoEditorUtils.h> #import <LegacyComponents/TGPhotoEditorUtils.h>
#import <LegacyComponents/TGPaintUtils.h> #import <LegacyComponents/TGPaintUtils.h>
@ -19,11 +20,12 @@
const CGFloat TGPhotoAvatarPreviewPanelSize = 96.0f; const CGFloat TGPhotoAvatarPreviewPanelSize = 96.0f;
const CGFloat TGPhotoAvatarPreviewLandscapePanelSize = TGPhotoAvatarPreviewPanelSize + 40.0f; const CGFloat TGPhotoAvatarPreviewLandscapePanelSize = TGPhotoAvatarPreviewPanelSize + 40.0f;
@interface TGPhotoAvatarPreviewController () <TGMediaPickerGalleryVideoScrubberDataSource, TGMediaPickerGalleryVideoScrubberDelegate> @interface TGPhotoAvatarPreviewController ()
{ {
bool _appeared; bool _appeared;
TGPhotoEditorSparseView *_wrapperView; TGPhotoEditorSparseView *_wrapperView;
TGMediaPickerGalleryVideoScrubber *_scrubberView;
UIView *_portraitToolsWrapperView; UIView *_portraitToolsWrapperView;
UIView *_landscapeToolsWrapperView; UIView *_landscapeToolsWrapperView;
UIView *_portraitWrapperBackgroundView; UIView *_portraitWrapperBackgroundView;
@ -35,13 +37,7 @@ const CGFloat TGPhotoAvatarPreviewLandscapePanelSize = TGPhotoAvatarPreviewPanel
UIView *_landscapeToolControlView; UIView *_landscapeToolControlView;
UIImageView *_areaMaskView; UIImageView *_areaMaskView;
CGFloat _currentDiameter; CGFloat _currentDiameter;
TGMediaPickerGalleryVideoScrubber *_scrubberView;
TGModernGalleryVideoView *_dotVideoView;
UILabel *_coverLabel; UILabel *_coverLabel;
bool _wasPlayingBeforeScrubbing;
bool _requestingThumbnails;
SMetaDisposable *_thumbnailsDisposable;
} }
@property (nonatomic, weak) PGPhotoEditor *photoEditor; @property (nonatomic, weak) PGPhotoEditor *photoEditor;
@ -51,24 +47,18 @@ const CGFloat TGPhotoAvatarPreviewLandscapePanelSize = TGPhotoAvatarPreviewPanel
@implementation TGPhotoAvatarPreviewController @implementation TGPhotoAvatarPreviewController
- (instancetype)initWithContext:(id<LegacyComponentsContext>)context photoEditor:(PGPhotoEditor *)photoEditor previewView:(TGPhotoEditorPreviewView *)previewView - (instancetype)initWithContext:(id<LegacyComponentsContext>)context photoEditor:(PGPhotoEditor *)photoEditor previewView:(TGPhotoEditorPreviewView *)previewView scrubberView:(TGMediaPickerGalleryVideoScrubber *)scrubberView
{ {
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;
_scrubberView = scrubberView;
_thumbnailsDisposable = [[SMetaDisposable alloc] init];
} }
return self; return self;
} }
- (void)dealloc
{
[_thumbnailsDisposable dispose];
}
- (void)loadView - (void)loadView
{ {
[super loadView]; [super loadView];
@ -114,10 +104,6 @@ const CGFloat TGPhotoAvatarPreviewLandscapePanelSize = TGPhotoAvatarPreviewPanel
_areaMaskView.alpha = 0.0f; _areaMaskView.alpha = 0.0f;
[self.view insertSubview:_areaMaskView aboveSubview:_videoAreaView]; [self.view insertSubview:_areaMaskView aboveSubview:_videoAreaView];
_scrubberView = [[TGMediaPickerGalleryVideoScrubber alloc] initWithFrame:CGRectMake(0.0f, 0.0, _portraitToolsWrapperView.frame.size.width, 68.0f)];
_scrubberView.autoresizingMask = UIViewAutoresizingFlexibleWidth;
_scrubberView.dataSource = self;
_scrubberView.delegate = self;
[_portraitToolsWrapperView addSubview:_scrubberView]; [_portraitToolsWrapperView addSubview:_scrubberView];
_coverLabel = [[UILabel alloc] init]; _coverLabel = [[UILabel alloc] init];
@ -137,22 +123,6 @@ const CGFloat TGPhotoAvatarPreviewLandscapePanelSize = TGPhotoAvatarPreviewPanel
[self transitionIn]; [self transitionIn];
} }
- (void)viewDidLoad
{
[super viewDidLoad];
_scrubberView.allowsTrimming = self.item.originalDuration >= TGVideoEditMinimumTrimmableDuration;
_scrubberView.hasDotPicker = true;
_scrubberView.disableZoom = true;
_scrubberView.disableTimeDisplay = true;
_scrubberView.trimStartValue = 0.0;
_scrubberView.trimEndValue = MIN(9.9, self.item.originalDuration);
[_scrubberView setTrimApplied:self.item.originalDuration > 9.9];
_scrubberView.maximumLength = 9.9;
[_scrubberView reloadData];
[_scrubberView resetToStart];
}
- (BOOL)shouldAutorotate - (BOOL)shouldAutorotate
{ {
TGPhotoEditorPreviewView *previewView = self.previewView; TGPhotoEditorPreviewView *previewView = self.previewView;
@ -264,6 +234,8 @@ const CGFloat TGPhotoAvatarPreviewLandscapePanelSize = TGPhotoAvatarPreviewPanel
{ {
_dismissing = true; _dismissing = true;
self.photoEditor.additionalOutputs = @[];
TGPhotoEditorPreviewView *previewView = self.previewView; TGPhotoEditorPreviewView *previewView = self.previewView;
[previewView prepareForTransitionOut]; [previewView prepareForTransitionOut];
@ -488,7 +460,6 @@ const CGFloat TGPhotoAvatarPreviewLandscapePanelSize = TGPhotoAvatarPreviewPanel
_portraitToolsWrapperView.frame = CGRectMake(screenEdges.left, screenEdges.bottom - panelToolbarPortraitSize, referenceSize.width, panelToolbarPortraitSize); _portraitToolsWrapperView.frame = CGRectMake(screenEdges.left, screenEdges.bottom - panelToolbarPortraitSize, referenceSize.width, panelToolbarPortraitSize);
_scrubberView.frame = CGRectMake(0.0, 0.0, _portraitToolsWrapperView.frame.size.width, _scrubberView.frame.size.height);
_coverLabel.frame = CGRectMake(floor((_portraitToolsWrapperView.frame.size.width - _coverLabel.frame.size.width) / 2.0), CGRectGetMaxY(_scrubberView.frame) + 6.0, _coverLabel.frame.size.width, _coverLabel.frame.size.height); _coverLabel.frame = CGRectMake(floor((_portraitToolsWrapperView.frame.size.width - _coverLabel.frame.size.width) / 2.0), CGRectGetMaxY(_scrubberView.frame) + 6.0, _coverLabel.frame.size.width, _coverLabel.frame.size.height);
// _portraitCollectionView.frame = CGRectMake(0, 0, _portraitToolsWrapperView.frame.size.width, panelSize); // _portraitCollectionView.frame = CGRectMake(0, 0, _portraitToolsWrapperView.frame.size.width, panelSize);
} }
@ -552,10 +523,6 @@ const CGFloat TGPhotoAvatarPreviewLandscapePanelSize = TGPhotoAvatarPreviewPanel
if (!_dismissing) if (!_dismissing)
[self updateToolViews]; [self updateToolViews];
dispatch_async(dispatch_get_main_queue(), ^{
[_scrubberView reloadThumbnails];
});
[self updatePreviewView]; [self updatePreviewView];
} }
@ -571,42 +538,13 @@ const CGFloat TGPhotoAvatarPreviewLandscapePanelSize = TGPhotoAvatarPreviewPanel
- (TGPhotoEditorTab)highlightedTabs - (TGPhotoEditorTab)highlightedTabs
{ {
bool hasSimpleValue = false; id<TGMediaEditAdjustments> adjustments = [self.photoEditor exportAdjustments];
bool hasBlur = false;
bool hasCurves = false;
bool hasTint = false;
// for (PGPhotoTool *tool in _allTools)
// {
// if (tool.isSimple)
// {
// if (tool.stringValue != nil)
// hasSimpleValue = true;
// }
// else if ([tool isKindOfClass:[PGBlurTool class]] && tool.stringValue != nil)
// {
// hasBlur = true;
// }
// else if ([tool isKindOfClass:[PGCurvesTool class]] && tool.stringValue != nil)
// {
// hasCurves = true;
// }
// else if ([tool isKindOfClass:[PGTintTool class]] && tool.stringValue != nil)
// {
// hasTint = true;
// }
// }
//
TGPhotoEditorTab tabs = TGPhotoEditorNoneTab; TGPhotoEditorTab tabs = TGPhotoEditorNoneTab;
if (hasSimpleValue) if (adjustments.toolsApplied)
tabs |= TGPhotoEditorToolsTab; tabs |= TGPhotoEditorToolsTab;
if (hasBlur) if (adjustments.hasPainting)
tabs |= TGPhotoEditorBlurTab; tabs |= TGPhotoEditorPaintTab;
if (hasCurves)
tabs |= TGPhotoEditorCurvesTab;
if (hasTint)
tabs |= TGPhotoEditorTintTab;
return tabs; return tabs;
} }
@ -632,30 +570,8 @@ const CGFloat TGPhotoAvatarPreviewLandscapePanelSize = TGPhotoAvatarPreviewPanel
// } // }
} }
#pragma mark - Video Scrubber Data Source & Delegate - (void)beginScrubbing
#pragma mark Scrubbing
- (NSTimeInterval)videoScrubberDuration:(TGMediaPickerGalleryVideoScrubber *)__unused videoScrubber
{ {
return self.item.originalDuration;
}
- (CGFloat)videoScrubberThumbnailAspectRatio:(TGMediaPickerGalleryVideoScrubber *)__unused videoScrubber
{
if (CGSizeEqualToSize(self.item.originalSize, CGSizeZero))
return 1.0f;
return self.item.originalSize.width / self.item.originalSize.height;
}
- (void)videoScrubberDidBeginScrubbing:(TGMediaPickerGalleryVideoScrubber *)__unused videoScrubber
{
_wasPlayingBeforeScrubbing = true;
self.controlVideoPlayback(false);
_dotVideoView.hidden = false;
_coverLabel.alpha = 1.0f; _coverLabel.alpha = 1.0f;
[self setPlayButtonHidden:true animated:false]; [self setPlayButtonHidden:true animated:false];
@ -665,21 +581,8 @@ const CGFloat TGPhotoAvatarPreviewLandscapePanelSize = TGPhotoAvatarPreviewPanel
}]; }];
} }
- (void)videoScrubberDidEndScrubbing:(TGMediaPickerGalleryVideoScrubber *)__unused videoScrubber - (void)endScrubbing:(bool (^)(void))completion
{ {
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:^{ [UIView animateWithDuration:0.12 animations:^{
_flashView.alpha = 1.0f; _flashView.alpha = 1.0f;
} completion:^(BOOL finished) { } completion:^(BOOL finished) {
@ -687,212 +590,17 @@ const CGFloat TGPhotoAvatarPreviewLandscapePanelSize = TGPhotoAvatarPreviewPanel
_flashView.alpha = 0.0f; _flashView.alpha = 0.0f;
} completion:^(BOOL finished) { } completion:^(BOOL finished) {
TGDispatchAfter(1.0, dispatch_get_main_queue(), ^{ TGDispatchAfter(1.0, dispatch_get_main_queue(), ^{
if (_scrubberView.isScrubbing) { if (completion()) {
return;
}
[UIView animateWithDuration:0.2 animations:^{ [UIView animateWithDuration:0.2 animations:^{
_areaMaskView.alpha = 0.0f; _areaMaskView.alpha = 0.0f;
_coverLabel.alpha = 0.7f; _coverLabel.alpha = 0.7f;
}]; }];
self.controlVideoPlayback(true); self.controlVideoPlayback(true);
}
}); });
}]; }];
}]; }];
} }
- (void)videoScrubber:(TGMediaPickerGalleryVideoScrubber *)__unused videoScrubber valueDidChange:(NSTimeInterval)position
{
self.controlVideoSeek(position);
}
#pragma mark Trimming
- (bool)hasTrimming
{
return _scrubberView.hasTrimming;
}
- (CMTimeRange)trimRange
{
return CMTimeRangeMake(CMTimeMakeWithSeconds(_scrubberView.trimStartValue , NSEC_PER_SEC), CMTimeMakeWithSeconds((_scrubberView.trimEndValue - _scrubberView.trimStartValue), NSEC_PER_SEC));
}
- (void)videoScrubberDidBeginEditing:(TGMediaPickerGalleryVideoScrubber *)__unused videoScrubber
{
self.controlVideoPlayback(false);
[self setPlayButtonHidden:true animated:false];
}
- (void)videoScrubberDidEndEditing:(TGMediaPickerGalleryVideoScrubber *)videoScrubber
{
[self updatePlayerRange:videoScrubber.trimEndValue];
self.controlVideoSeek(videoScrubber.trimStartValue);
self.controlVideoPlayback(true);
[self setPlayButtonHidden:false animated:true];
}
- (void)videoScrubber:(TGMediaPickerGalleryVideoScrubber *)__unused videoScrubber editingStartValueDidChange:(NSTimeInterval)startValue
{
self.controlVideoSeek(startValue);
}
- (void)videoScrubber:(TGMediaPickerGalleryVideoScrubber *)__unused videoScrubber editingEndValueDidChange:(NSTimeInterval)endValue
{
self.controlVideoSeek(endValue);
}
- (void)updatePlayerRange:(NSTimeInterval)trimEndValue
{
self.controlVideoEndTime(trimEndValue);
}
#pragma mark Thumbnails
- (NSArray *)videoScrubber:(TGMediaPickerGalleryVideoScrubber *)videoScrubber evenlySpacedTimestamps:(NSInteger)count startingAt:(NSTimeInterval)startTimestamp endingAt:(NSTimeInterval)endTimestamp
{
if (endTimestamp < startTimestamp)
return nil;
if (count == 0)
return nil;
NSTimeInterval duration = [self videoScrubberDuration:videoScrubber];
if (endTimestamp > duration)
endTimestamp = duration;
NSTimeInterval interval = (endTimestamp - startTimestamp) / count;
NSMutableArray *timestamps = [[NSMutableArray alloc] init];
for (NSInteger i = 0; i < count; i++)
[timestamps addObject:@(startTimestamp + i * interval)];
return timestamps;
}
- (void)videoScrubber:(TGMediaPickerGalleryVideoScrubber *)__unused videoScrubber requestThumbnailImagesForTimestamps:(NSArray *)timestamps size:(CGSize)size isSummaryThumbnails:(bool)isSummaryThumbnails
{
if (timestamps.count == 0)
return;
id<TGMediaEditAdjustments> adjustments = [self.photoEditor exportAdjustments];
NSArray *cachedThumbnails = ((TGPhotoEditorController *)self.parentViewController).cachedVideoThumbnails;
SSignal *thumbnailsSignal = nil;
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]]) {
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 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];
editor.standalone = true;
for (UIImage *image in images) {
[editor setImage:image forCropRect:adjustments.cropRect cropRotation:0.0 cropOrientation:adjustments.cropOrientation cropMirrored:adjustments.cropMirrored fullSize:false];
UIImage *resultImage = editor.currentResultImage;
if (resultImage != nil) {
[editedImages addObject:resultImage];
} else {
[editedImages addObject:image];
}
}
return editedImages;
} else {
return images;
}
}] deliverOn:[SQueue mainQueue]] startWithNext:^(NSArray *images)
{
__strong TGPhotoAvatarPreviewController *strongSelf = weakSelf;
if (strongSelf == nil)
return;
[images enumerateObjectsUsingBlock:^(UIImage *image, NSUInteger index, __unused BOOL *stop)
{
if (index < timestamps.count)
[strongSelf->_scrubberView setThumbnailImage:image forTimestamp:[timestamps[index] doubleValue] isSummaryThubmnail:isSummaryThumbnails];
}];
} completed:^
{
__strong TGPhotoAvatarPreviewController *strongSelf = weakSelf;
if (strongSelf != nil)
strongSelf->_requestingThumbnails = false;
}]];
}
- (void)videoScrubberDidFinishRequestingThumbnails:(TGMediaPickerGalleryVideoScrubber *)__unused videoScrubber
{
_requestingThumbnails = false;
// [self setScrubbingPanelHidden:false animated:true];
}
- (void)videoScrubberDidCancelRequestingThumbnails:(TGMediaPickerGalleryVideoScrubber *)__unused videoScrubber
{
_requestingThumbnails = false;
}
- (CGSize)videoScrubberOriginalSize:(TGMediaPickerGalleryVideoScrubber *)__unused videoScrubber cropRect:(CGRect *)cropRect cropOrientation:(UIImageOrientation *)cropOrientation cropMirrored:(bool *)cropMirrored
{
id<TGMediaEditAdjustments> adjustments = [self.photoEditor exportAdjustments];
if (cropRect != NULL)
*cropRect = (adjustments != nil) ? adjustments.cropRect : CGRectMake(0, 0, self.item.originalSize.width, self.item.originalSize.height);
if (cropOrientation != NULL)
*cropOrientation = (adjustments != nil) ? adjustments.cropOrientation : UIImageOrientationUp;
if (cropMirrored != NULL)
*cropMirrored = adjustments.cropMirrored;
return self.item.originalSize;
}
- (void)setScrubberPosition:(NSTimeInterval)position reset:(bool)reset
{
}
- (void)setScrubberPlaying:(bool)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.value;
}
- (NSTimeInterval)trimStartValue {
return _scrubberView.trimStartValue;
}
- (NSTimeInterval)trimEndValue {
return _scrubberView.trimEndValue;
}
@end @end

View File

@ -87,7 +87,7 @@
[_portraitButton addTarget:self action:@selector(blurButtonPressed:) forControlEvents:UIControlEventTouchUpInside]; [_portraitButton addTarget:self action:@selector(blurButtonPressed:) forControlEvents:UIControlEventTouchUpInside];
[_portraitButton setImage:TGTintedImage([UIImage imageNamed:@"Editor/BlurPortrait"], [UIColor whiteColor])]; [_portraitButton setImage:TGTintedImage([UIImage imageNamed:@"Editor/BlurPortrait"], [UIColor whiteColor])];
[_portraitButton setTitle:TGLocalized(@"PhotoEditor.BlurToolPortrait")]; [_portraitButton setTitle:TGLocalized(@"PhotoEditor.BlurToolPortrait")];
[_buttonsWrapper addSubview:_portraitButton]; // [_buttonsWrapper addSubview:_portraitButton];
_sliderView = [[TGPhotoEditorSliderView alloc] initWithFrame:CGRectZero]; _sliderView = [[TGPhotoEditorSliderView alloc] initWithFrame:CGRectZero];
_sliderView.alpha = 0.0f; _sliderView.alpha = 0.0f;
@ -266,28 +266,9 @@
{ {
_titleLabel.frame = CGRectMake((self.frame.size.width - _titleLabel.frame.size.width) / 2, 10, _titleLabel.frame.size.width, _titleLabel.frame.size.height); _titleLabel.frame = CGRectMake((self.frame.size.width - _titleLabel.frame.size.width) / 2, 10, _titleLabel.frame.size.width, _titleLabel.frame.size.height);
// _offButton.frame = CGRectMake(CGFloor(self.frame.size.width / 4 - 50), self.frame.size.height / 2 - 42, 100, 100); _offButton.frame = CGRectMake(CGFloor(self.frame.size.width / 4 - 50), self.frame.size.height / 2 - 42, 100, 100);
// _radialButton.frame = CGRectMake(self.frame.size.width / 2 - 75, self.frame.size.height / 2 - 42, 100, 100); _radialButton.frame = CGRectMake(self.frame.size.width / 2 - 50, self.frame.size.height / 2 - 42, 100, 100);
// _linearButton.frame = CGRectMake(CGCeil(self.frame.size.width / 2 - 50), self.frame.size.height / 2 - 42, 100, 100); _linearButton.frame = CGRectMake(CGCeil(self.frame.size.width / 2 + self.frame.size.width / 4 - 50), self.frame.size.height / 2 - 42, 100, 100);
// _portraitButton.frame = CGRectMake(CGCeil(self.frame.size.width / 2 + self.frame.size.width / 4 - 50), self.frame.size.height / 2 - 42, 100, 100);
NSArray *buttons = @[_offButton, _radialButton, _linearButton, _portraitButton];
UIView *leftButton = buttons.firstObject;
UIView *centerLeftButton = [buttons objectAtIndex:1];
UIView *centerRightButton = [buttons objectAtIndex:2];
UIView *rightButton = buttons.lastObject;
CGFloat offset = self.frame.size.height / 2 - 42;
CGSize buttonSize = CGSizeMake(100.0, 100.0);
leftButton.frame = CGRectMake(CGFloor(self.frame.size.width / 8 * 1.5 - 3 - buttonSize.width / 2), offset, buttonSize.width, buttonSize.height);
centerLeftButton.frame = CGRectMake(CGFloor(self.frame.size.width / 10 * 3.75 + 5 - buttonSize.width / 2), offset, buttonSize.width, buttonSize.height);
centerRightButton.frame = CGRectMake(CGCeil(self.frame.size.width - centerLeftButton.frame.origin.x - buttonSize.width), offset, buttonSize.width, buttonSize.height);
rightButton.frame = CGRectMake(CGCeil(self.frame.size.width - leftButton.frame.origin.x - buttonSize.width), offset, buttonSize.width, buttonSize.height);
_sliderView.frame = CGRectMake(TGPhotoEditorSliderViewMargin, (self.frame.size.height - 32) / 2, self.frame.size.width - 2 * TGPhotoEditorSliderViewMargin, 32); _sliderView.frame = CGRectMake(TGPhotoEditorSliderViewMargin, (self.frame.size.height - 32) / 2, self.frame.size.width - 2 * TGPhotoEditorSliderViewMargin, 32);
} }

View File

@ -18,7 +18,8 @@
#import "TGProgressWindow.h" #import "TGProgressWindow.h"
#import "PGPhotoEditor.h" #import "PGPhotoEditor.h"
#import "PGEnhanceTool.h" #import "PGPhotoEditorView.h"
#import "TGPaintFaceDetector.h" #import "TGPaintFaceDetector.h"
#import <LegacyComponents/PGPhotoEditorValues.h> #import <LegacyComponents/PGPhotoEditorValues.h>
@ -42,13 +43,14 @@
#import "TGPhotoAvatarPreviewController.h" #import "TGPhotoAvatarPreviewController.h"
#import "TGMessageImageViewOverlayView.h" #import "TGMessageImageViewOverlayView.h"
#import "TGMediaPickerGalleryVideoScrubber.h"
#import "TGMenuSheetController.h" #import "TGMenuSheetController.h"
#import <LegacyComponents/AVURLAsset+TGMediaItem.h> #import <LegacyComponents/AVURLAsset+TGMediaItem.h>
#import "TGCameraCapturedVideo.h" #import "TGCameraCapturedVideo.h"
@interface TGPhotoEditorController () <ASWatcher, TGViewControllerNavigationBarAppearance, UIDocumentInteractionControllerDelegate> @interface TGPhotoEditorController () <ASWatcher, TGViewControllerNavigationBarAppearance, TGMediaPickerGalleryVideoScrubberDataSource, TGMediaPickerGalleryVideoScrubberDelegate, UIDocumentInteractionControllerDelegate>
{ {
bool _switchingTab; bool _switchingTab;
TGPhotoEditorTab _availableTabs; TGPhotoEditorTab _availableTabs;
@ -75,8 +77,6 @@
SMetaDisposable *_playerItemDisposable; SMetaDisposable *_playerItemDisposable;
id _playerStartedObserver; id _playerStartedObserver;
id _playerReachedEndObserver; id _playerReachedEndObserver;
bool _registeredKeypathObserver;
NSTimer *_positionTimer;
bool _scheduledVideoPlayback; bool _scheduledVideoPlayback;
id<TGMediaEditAdjustments> _initialAdjustments; id<TGMediaEditAdjustments> _initialAdjustments;
@ -97,6 +97,13 @@
SMetaDisposable *_faceDetectorDisposable; SMetaDisposable *_faceDetectorDisposable;
bool _initializedScrubber;
TGMediaPickerGalleryVideoScrubber *_scrubberView;
PGPhotoEditorView *_dotVideoView;
bool _requestingThumbnails;
SMetaDisposable *_thumbnailsDisposable;
id<LegacyComponentsContext> _context; id<LegacyComponentsContext> _context;
} }
@ -145,6 +152,8 @@
_photoEditor.trimEndValue = videoAdjustments.trimEndValue; _photoEditor.trimEndValue = videoAdjustments.trimEndValue;
} }
_thumbnailsDisposable = [[SMetaDisposable alloc] init];
self.customAppearanceMethodsForwarding = true; self.customAppearanceMethodsForwarding = true;
} }
return self; return self;
@ -155,6 +164,7 @@
[self stopVideoPlayback:true]; [self stopVideoPlayback:true];
[_actionHandle reset]; [_actionHandle reset];
[_faceDetectorDisposable dispose]; [_faceDetectorDisposable dispose];
[_thumbnailsDisposable dispose];
} }
- (void)loadView - (void)loadView
@ -297,6 +307,12 @@
[_photoEditor setPreviewOutput:_previewView]; [_photoEditor setPreviewOutput:_previewView];
[self updatePreviewView]; [self updatePreviewView];
if (_intent == TGPhotoEditorControllerAvatarIntent && _item.isVideo) {
_scrubberView = [[TGMediaPickerGalleryVideoScrubber alloc] initWithFrame:CGRectMake(0.0f, 0.0, _portraitToolbarView.frame.size.width, 68.0f)];
_scrubberView.dataSource = self;
_scrubberView.delegate = self;
}
[self detectFaces]; [self detectFaces];
[self presentEditorTab:_currentTab]; [self presentEditorTab:_currentTab];
@ -364,6 +380,17 @@
if ([_currentTabController isKindOfClass:[TGPhotoCropController class]]) if ([_currentTabController isKindOfClass:[TGPhotoCropController class]])
return; return;
_scrubberView.allowsTrimming = self.item.originalDuration >= TGVideoEditMinimumTrimmableDuration;
_scrubberView.hasDotPicker = true;
_scrubberView.disableZoom = true;
_scrubberView.disableTimeDisplay = true;
_scrubberView.trimStartValue = 0.0;
_scrubberView.trimEndValue = MIN(9.9, self.item.originalDuration);
[_scrubberView setTrimApplied:self.item.originalDuration > 9.9];
_scrubberView.maximumLength = 9.9;
[self setVideoEndTime:_scrubberView.trimEndValue];
NSTimeInterval position = 0; NSTimeInterval position = 0;
TGMediaVideoEditAdjustments *adjustments = [_photoEditor exportAdjustments]; TGMediaVideoEditAdjustments *adjustments = [_photoEditor exportAdjustments];
if ([adjustments isKindOfClass:[TGMediaVideoEditAdjustments class]]) if ([adjustments isKindOfClass:[TGMediaVideoEditAdjustments class]])
@ -481,6 +508,9 @@
- (void)_setupPlaybackReachedEndObserver - (void)_setupPlaybackReachedEndObserver
{ {
if (_playerReachedEndObserver != nil)
[_player removeTimeObserver:_playerReachedEndObserver];
PGPhotoEditor *photoEditor = _photoEditor; PGPhotoEditor *photoEditor = _photoEditor;
CMTime endTime = CMTimeSubtract(_player.currentItem.duration, CMTimeMake(10, 100)); CMTime endTime = CMTimeSubtract(_player.currentItem.duration, CMTimeMake(10, 100));
if (photoEditor.trimEndValue > DBL_EPSILON && photoEditor.trimEndValue < CMTimeGetSeconds(_player.currentItem.duration)) if (photoEditor.trimEndValue > DBL_EPSILON && photoEditor.trimEndValue < CMTimeGetSeconds(_player.currentItem.duration))
@ -496,9 +526,6 @@
__strong TGPhotoEditorController *strongSelf = weakSelf; __strong TGPhotoEditorController *strongSelf = weakSelf;
if (strongSelf != nil) { if (strongSelf != nil) {
[strongSelf->_player seekToTime:startTime]; [strongSelf->_player seekToTime:startTime];
if ([strongSelf->_currentTabController isKindOfClass:[TGPhotoAvatarPreviewController class]]) {
[(TGPhotoAvatarPreviewController *)strongSelf->_currentTabController setScrubberPosition:photoEditor.trimStartValue reset:true];
}
} }
}]; }];
} }
@ -518,17 +545,9 @@
[_player.currentItem seekToTime:targetTime toleranceBefore:kCMTimeZero toleranceAfter:kCMTimeZero]; [_player.currentItem seekToTime:targetTime toleranceBefore:kCMTimeZero toleranceAfter:kCMTimeZero];
[self _setupPlaybackStartedObserver]; [self _setupPlaybackStartedObserver];
if (!_registeredKeypathObserver) {
[_player addObserver:self forKeyPath:@"rate" options:NSKeyValueObservingOptionNew context:nil];
_registeredKeypathObserver = true;
}
} }
[_player play]; [_player play];
_positionTimer = [TGTimerTarget scheduledMainThreadTimerWithTarget:self action:@selector(positionTimerEvent) interval:0.25 repeat:true];
[self positionTimerEvent];
} }
- (void)stopVideoPlayback:(bool)reset { - (void)stopVideoPlayback:(bool)reset {
@ -537,33 +556,8 @@
[_player removeTimeObserver:_playerStartedObserver]; [_player removeTimeObserver:_playerStartedObserver];
if (_playerReachedEndObserver != nil) if (_playerReachedEndObserver != nil)
[_player removeTimeObserver:_playerReachedEndObserver]; [_player removeTimeObserver:_playerReachedEndObserver];
if (_registeredKeypathObserver) {
[_player removeObserver:self forKeyPath:@"rate" context:nil];
_registeredKeypathObserver = false;
}
} }
[_player pause]; [_player pause];
[_positionTimer invalidate];
_positionTimer = nil;
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)__unused change context:(void *)__unused context
{
if (object == _player && [keyPath isEqualToString:@"rate"])
{
if ([_currentTabController isKindOfClass:[TGPhotoAvatarPreviewController class]]) {
[(TGPhotoAvatarPreviewController *)_currentTabController setScrubberPlaying:_player.rate > FLT_EPSILON];
}
}
}
- (void)positionTimerEvent
{
if ([_currentTabController isKindOfClass:[TGPhotoAvatarPreviewController class]]) {
[(TGPhotoAvatarPreviewController *)_currentTabController setScrubberPosition:CMTimeGetSeconds(_player.currentItem.currentTime) reset:false];
}
} }
- (void)seekVideo:(NSTimeInterval)position { - (void)seekVideo:(NSTimeInterval)position {
@ -573,6 +567,7 @@
- (void)setVideoEndTime:(NSTimeInterval)endTime { - (void)setVideoEndTime:(NSTimeInterval)endTime {
_player.currentItem.forwardPlaybackEndTime = CMTimeMakeWithSeconds(endTime, NSEC_PER_SEC); _player.currentItem.forwardPlaybackEndTime = CMTimeMakeWithSeconds(endTime, NSEC_PER_SEC);
[self _setupPlaybackReachedEndObserver];
} }
- (void)viewWillAppear:(BOOL)animated - (void)viewWillAppear:(BOOL)animated
@ -1330,7 +1325,11 @@
case TGPhotoEditorPreviewTab: case TGPhotoEditorPreviewTab:
{ {
TGPhotoAvatarPreviewController *previewController = [[TGPhotoAvatarPreviewController alloc] initWithContext:_context photoEditor:_photoEditor previewView:_previewView]; if ([_currentTabController isKindOfClass:[TGPhotoToolsController class]]) {
[_scrubberView reloadThumbnails];
}
TGPhotoAvatarPreviewController *previewController = [[TGPhotoAvatarPreviewController alloc] initWithContext:_context photoEditor:_photoEditor previewView:_previewView scrubberView:_scrubberView];
previewController.item = _item; previewController.item = _item;
previewController.toolbarLandscapeSize = TGPhotoEditorToolbarSize; previewController.toolbarLandscapeSize = TGPhotoEditorToolbarSize;
previewController.beginTransitionIn = ^UIView *(CGRect *referenceFrame, UIView **parentView, bool *noTransitionView) previewController.beginTransitionIn = ^UIView *(CGRect *referenceFrame, UIView **parentView, bool *noTransitionView)
@ -1339,6 +1338,10 @@
*parentView = transitionParentView; *parentView = transitionParentView;
*noTransitionView = transitionNoTransitionView; *noTransitionView = transitionNoTransitionView;
__strong TGPhotoEditorController *strongSelf = weakSelf;
if (strongSelf != nil)
[strongSelf startVideoPlayback:true];
return transitionReferenceView; return transitionReferenceView;
}; };
previewController.finishedTransitionIn = ^ previewController.finishedTransitionIn = ^
@ -1351,7 +1354,6 @@
strongSelf.finishedTransitionIn(); strongSelf.finishedTransitionIn();
strongSelf->_switchingTab = false; strongSelf->_switchingTab = false;
[strongSelf startVideoPlayback:true];
}; };
previewController.controlVideoPlayback = ^(bool play) { previewController.controlVideoPlayback = ^(bool play) {
__strong TGPhotoEditorController *strongSelf = weakSelf; __strong TGPhotoEditorController *strongSelf = weakSelf;
@ -1635,10 +1637,9 @@
[[NSUserDefaults standardUserDefaults] setObject:@(qualityController.preset) forKey:@"TG_preferredVideoPreset_v0"]; [[NSUserDefaults standardUserDefaults] setObject:@(qualityController.preset) forKey:@"TG_preferredVideoPreset_v0"];
} else if ([_currentTabController isKindOfClass:[TGPhotoAvatarPreviewController class]]) } else if ([_currentTabController isKindOfClass:[TGPhotoAvatarPreviewController class]])
{ {
TGPhotoAvatarPreviewController *previewController = (TGPhotoAvatarPreviewController *)_currentTabController; videoStartValue = _scrubberView.value;
videoStartValue = previewController.coverPosition; trimStartValue = _scrubberView.trimStartValue;
trimStartValue = previewController.trimStartValue; trimEndValue = _scrubberView.trimEndValue;
trimEndValue = previewController.trimEndValue;
} }
TGVideoEditAdjustments *adjustments = [_photoEditor exportAdjustmentsWithPaintingData:paintingData]; TGVideoEditAdjustments *adjustments = [_photoEditor exportAdjustmentsWithPaintingData:paintingData];
@ -2034,6 +2035,21 @@
if ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPad) if ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPad)
portraitToolbarViewBottomEdge = screenEdges.bottom; portraitToolbarViewBottomEdge = screenEdges.bottom;
_portraitToolbarView.frame = CGRectMake(screenEdges.left, portraitToolbarViewBottomEdge - TGPhotoEditorToolbarSize - safeAreaInset.bottom, referenceSize.width, TGPhotoEditorToolbarSize + safeAreaInset.bottom); _portraitToolbarView.frame = CGRectMake(screenEdges.left, portraitToolbarViewBottomEdge - TGPhotoEditorToolbarSize - safeAreaInset.bottom, referenceSize.width, TGPhotoEditorToolbarSize + safeAreaInset.bottom);
_scrubberView.frame = CGRectMake(0.0, 0.0, _portraitToolbarView.frame.size.width, _scrubberView.frame.size.height);
dispatch_async(dispatch_get_main_queue(), ^{
if (!_initializedScrubber) {
[_scrubberView layoutSubviews];
_initializedScrubber = true;
[_scrubberView reloadData];
[_scrubberView resetToStart];
[self updateDotImage];
} else {
[_scrubberView reloadThumbnails];
}
});
} }
- (void)_setScreenImage:(UIImage *)screenImage - (void)_setScreenImage:(UIImage *)screenImage
@ -2167,4 +2183,247 @@
return avatarTabs; return avatarTabs;
} }
#pragma mark - Video Scrubber Data Source & Delegate
#pragma mark Scrubbing
- (id<TGMediaEditableItem>)item {
return _item;
}
- (NSTimeInterval)videoScrubberDuration:(TGMediaPickerGalleryVideoScrubber *)__unused videoScrubber
{
return self.item.originalDuration;
}
- (CGFloat)videoScrubberThumbnailAspectRatio:(TGMediaPickerGalleryVideoScrubber *)__unused videoScrubber
{
if (CGSizeEqualToSize(self.item.originalSize, CGSizeZero))
return 1.0f;
return self.item.originalSize.width / self.item.originalSize.height;
}
- (void)videoScrubberDidBeginScrubbing:(TGMediaPickerGalleryVideoScrubber *)__unused videoScrubber
{
[self stopVideoPlayback:false];
if (_dotVideoView == nil) {
_dotVideoView = [[PGPhotoEditorView alloc] initWithFrame:CGRectMake(0.0, 0.0, 26.0, 44.0)];
[_scrubberView setDotVideoView:_dotVideoView];
_photoEditor.additionalOutputs = @[_dotVideoView];
} else {
_dotVideoView.hidden = false;
}
TGPhotoAvatarPreviewController *previewController = (TGPhotoAvatarPreviewController *)_currentTabController;
if (![previewController isKindOfClass:[TGPhotoAvatarPreviewController class]])
return;
[previewController beginScrubbing];
}
- (void)updateDotImage {
id<TGMediaEditAdjustments> adjustments = [_photoEditor exportAdjustments];
AVPlayer *player = _player;
[[SQueue concurrentDefaultQueue] dispatch:^{
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 *image = [[UIImage alloc] initWithCGImage:imageRef];
CGImageRelease(imageRef);
if (adjustments.toolsApplied) {
PGPhotoEditor *editor = [[PGPhotoEditor alloc] initWithOriginalSize:adjustments.originalSize adjustments:adjustments forVideo:false enableStickers:true];
editor.standalone = true;
[editor setImage:image forCropRect:adjustments.cropRect cropRotation:0.0 cropOrientation:adjustments.cropOrientation cropMirrored:adjustments.cropMirrored fullSize:false];
image = editor.currentResultImage;
}
TGDispatchOnMainThread(^{
[_scrubberView setDotImage:image];
_dotVideoView.hidden = true;
});
}];
}
- (void)videoScrubberDidEndScrubbing:(TGMediaPickerGalleryVideoScrubber *)__unused videoScrubber
{
[self updateDotImage];
__weak TGPhotoEditorController *weakSelf = self;
TGPhotoAvatarPreviewController *previewController = (TGPhotoAvatarPreviewController *)_currentTabController;
if (![previewController isKindOfClass:[TGPhotoAvatarPreviewController class]])
return;
[previewController endScrubbing:^bool{
__strong TGPhotoEditorController *strongSelf = weakSelf;
if (strongSelf == nil)
return false;
return !strongSelf->_scrubberView.isScrubbing;
}];
}
- (void)videoScrubber:(TGMediaPickerGalleryVideoScrubber *)__unused videoScrubber valueDidChange:(NSTimeInterval)position
{
[self seekVideo:position];
}
#pragma mark Trimming
- (bool)hasTrimming
{
return _scrubberView.hasTrimming;
}
- (CMTimeRange)trimRange
{
return CMTimeRangeMake(CMTimeMakeWithSeconds(_scrubberView.trimStartValue , NSEC_PER_SEC), CMTimeMakeWithSeconds((_scrubberView.trimEndValue - _scrubberView.trimStartValue), NSEC_PER_SEC));
}
- (void)videoScrubberDidBeginEditing:(TGMediaPickerGalleryVideoScrubber *)__unused videoScrubber
{
[self stopVideoPlayback:false];
TGPhotoAvatarPreviewController *previewController = (TGPhotoAvatarPreviewController *)_currentTabController;
if (![previewController isKindOfClass:[TGPhotoAvatarPreviewController class]])
return;
[previewController setPlayButtonHidden:true animated:false];
}
- (void)videoScrubberDidEndEditing:(TGMediaPickerGalleryVideoScrubber *)videoScrubber
{
[self setVideoEndTime:videoScrubber.trimEndValue];
[self seekVideo:videoScrubber.trimStartValue];
[self stopVideoPlayback:false];
TGPhotoAvatarPreviewController *previewController = (TGPhotoAvatarPreviewController *)_currentTabController;
if (![previewController isKindOfClass:[TGPhotoAvatarPreviewController class]])
return;
[previewController setPlayButtonHidden:true animated:false];
}
- (void)videoScrubber:(TGMediaPickerGalleryVideoScrubber *)__unused videoScrubber editingStartValueDidChange:(NSTimeInterval)startValue
{
[self seekVideo:startValue];
}
- (void)videoScrubber:(TGMediaPickerGalleryVideoScrubber *)__unused videoScrubber editingEndValueDidChange:(NSTimeInterval)endValue
{
[self seekVideo:endValue];
}
#pragma mark Thumbnails
- (NSArray *)videoScrubber:(TGMediaPickerGalleryVideoScrubber *)videoScrubber evenlySpacedTimestamps:(NSInteger)count startingAt:(NSTimeInterval)startTimestamp endingAt:(NSTimeInterval)endTimestamp
{
if (endTimestamp < startTimestamp)
return nil;
if (count == 0)
return nil;
NSTimeInterval duration = [self videoScrubberDuration:videoScrubber];
if (endTimestamp > duration)
endTimestamp = duration;
NSTimeInterval interval = (endTimestamp - startTimestamp) / count;
NSMutableArray *timestamps = [[NSMutableArray alloc] init];
for (NSInteger i = 0; i < count; i++)
[timestamps addObject:@(startTimestamp + i * interval)];
return timestamps;
}
- (void)videoScrubber:(TGMediaPickerGalleryVideoScrubber *)__unused videoScrubber requestThumbnailImagesForTimestamps:(NSArray *)timestamps size:(CGSize)size isSummaryThumbnails:(bool)isSummaryThumbnails
{
if (timestamps.count == 0)
return;
id<TGMediaEditAdjustments> adjustments = [_photoEditor exportAdjustments];
SSignal *thumbnailsSignal = nil;
if ([self.item isKindOfClass:[TGMediaAsset class]]) {
thumbnailsSignal = [TGMediaAssetImageSignals videoThumbnailsForAsset:(TGMediaAsset *)self.item size:size timestamps:timestamps];
} 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 TGPhotoEditorController *weakSelf = self;
[_thumbnailsDisposable setDisposable:[[[thumbnailsSignal 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];
editor.standalone = true;
for (UIImage *image in images) {
[editor setImage:image forCropRect:adjustments.cropRect cropRotation:0.0 cropOrientation:adjustments.cropOrientation cropMirrored:adjustments.cropMirrored fullSize:false];
UIImage *resultImage = editor.currentResultImage;
if (resultImage != nil) {
[editedImages addObject:resultImage];
} else {
[editedImages addObject:image];
}
}
return editedImages;
} else {
return images;
}
}] deliverOn:[SQueue mainQueue]] startWithNext:^(NSArray *images)
{
__strong TGPhotoEditorController *strongSelf = weakSelf;
if (strongSelf == nil)
return;
[images enumerateObjectsUsingBlock:^(UIImage *image, NSUInteger index, __unused BOOL *stop)
{
if (index < timestamps.count)
[strongSelf->_scrubberView setThumbnailImage:image forTimestamp:[timestamps[index] doubleValue] isSummaryThubmnail:isSummaryThumbnails];
}];
} completed:^
{
__strong TGPhotoEditorController *strongSelf = weakSelf;
if (strongSelf != nil)
strongSelf->_requestingThumbnails = false;
}]];
}
- (void)videoScrubberDidFinishRequestingThumbnails:(TGMediaPickerGalleryVideoScrubber *)__unused videoScrubber
{
_requestingThumbnails = false;
}
- (void)videoScrubberDidCancelRequestingThumbnails:(TGMediaPickerGalleryVideoScrubber *)__unused videoScrubber
{
_requestingThumbnails = false;
}
- (CGSize)videoScrubberOriginalSize:(TGMediaPickerGalleryVideoScrubber *)__unused videoScrubber cropRect:(CGRect *)cropRect cropOrientation:(UIImageOrientation *)cropOrientation cropMirrored:(bool *)cropMirrored
{
id<TGMediaEditAdjustments> adjustments = [_photoEditor exportAdjustments];
if (cropRect != NULL)
*cropRect = (adjustments != nil) ? adjustments.cropRect : CGRectMake(0, 0, self.item.originalSize.width, self.item.originalSize.height);
if (cropOrientation != NULL)
*cropOrientation = (adjustments != nil) ? adjustments.cropOrientation : UIImageOrientationUp;
if (cropMirrored != NULL)
*cropMirrored = adjustments.cropMirrored;
return self.item.originalSize;
}
@end @end

View File

@ -164,6 +164,11 @@ typedef enum {
} }
} }
- (void)appendVideoSampleBuffer:(CMSampleBufferRef)sampleBuffer
{
[self appendSampleBuffer:sampleBuffer ofMediaType:AVMediaTypeVideo];
}
- (void)appendAudioSampleBuffer:(CMSampleBufferRef)sampleBuffer - (void)appendAudioSampleBuffer:(CMSampleBufferRef)sampleBuffer
{ {
[self appendSampleBuffer:sampleBuffer ofMediaType:AVMediaTypeAudio]; [self appendSampleBuffer:sampleBuffer ofMediaType:AVMediaTypeAudio];

View File

@ -373,8 +373,10 @@ const NSInteger TGVideoCameraRetainedBufferCount = 16;
{ {
if (self.outputVideoFormatDescription == NULL) if (self.outputVideoFormatDescription == NULL)
[self setupVideoPipelineWithInputFormatDescription:formatDescription]; [self setupVideoPipelineWithInputFormatDescription:formatDescription];
else else {
[self renderVideoSampleBuffer:sampleBuffer]; [_recorder appendVideoSampleBuffer:sampleBuffer];
// [self renderVideoSampleBuffer:sampleBuffer];
}
} }
else if (connection == _audioConnection) else if (connection == _audioConnection)
{ {

View File

@ -77,7 +77,7 @@ final class AvatarGalleryItemFooterContentNode: GalleryFooterContentNode {
self.setMainButton = HighlightableButtonNode() self.setMainButton = HighlightableButtonNode()
self.setMainButton.isHidden = true self.setMainButton.isHidden = true
self.setMainButton.setAttributedTitle(NSAttributedString(string: self.strings.ProfilePhoto_SetMain, font: Font.regular(17.0), textColor: .white), for: .normal) self.setMainButton.setAttributedTitle(NSAttributedString(string: self.strings.ProfilePhoto_SetMainPhoto, font: Font.regular(17.0), textColor: .white), for: .normal)
self.mainNode = ASTextNode() self.mainNode = ASTextNode()
self.mainNode.maximumNumberOfLines = 1 self.mainNode.maximumNumberOfLines = 1

View File

@ -4585,6 +4585,12 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
if let strongSelf = self { if let strongSelf = self {
strongSelf.openScheduledMessages() strongSelf.openScheduledMessages()
} }
}, openPeersNearby: { [weak self] in
if let strongSelf = self {
let controller = strongSelf.context.sharedContext.makePeersNearbyController(context: strongSelf.context)
controller.navigationPresentation = .master
strongSelf.effectiveNavigationController?.pushViewController(controller, animated: true, completion: { })
}
}, displaySearchResultsTooltip: { [weak self] node, nodeRect in }, displaySearchResultsTooltip: { [weak self] node, nodeRect in
if let strongSelf = self { if let strongSelf = self {
strongSelf.searchResultsTooltipController?.dismiss() strongSelf.searchResultsTooltipController?.dismiss()

View File

@ -128,6 +128,7 @@ private final class ChatEmptyNodeNearbyChatContent: ASDisplayNode, ChatEmptyNode
var displayName = "" var displayName = ""
let distance = interfaceState.peerNearbyData?.distance ?? 0 let distance = interfaceState.peerNearbyData?.distance ?? 0
if let renderedPeer = interfaceState.renderedPeer { if let renderedPeer = interfaceState.renderedPeer {
if let chatPeer = renderedPeer.peers[renderedPeer.peerId] { if let chatPeer = renderedPeer.peers[renderedPeer.peerId] {
displayName = chatPeer.compactDisplayTitle displayName = chatPeer.compactDisplayTitle
@ -146,8 +147,18 @@ private final class ChatEmptyNodeNearbyChatContent: ASDisplayNode, ChatEmptyNode
if let item = self.stickerItem { if let item = self.stickerItem {
self.stickerNode.updateLayout(item: item, size: stickerSize, isVisible: true, synchronousLoads: true) self.stickerNode.updateLayout(item: item, size: stickerSize, isVisible: true, synchronousLoads: true)
} else if !self.didSetupSticker { } else if !self.didSetupSticker {
let sticker: Signal<TelegramMediaFile?, NoError>
if let preloadedSticker = interfaceState.peerNearbyData?.sticker {
sticker = .single(preloadedSticker)
} else {
sticker = randomGreetingSticker(account: self.account)
|> map { item -> TelegramMediaFile? in
return item?.file
}
}
self.didSetupSticker = true self.didSetupSticker = true
self.disposable.set((randomGreetingSticker(account: self.account) self.disposable.set((sticker
|> deliverOnMainQueue).start(next: { [weak self] sticker in |> deliverOnMainQueue).start(next: { [weak self] sticker in
if let strongSelf = self, let sticker = sticker { if let strongSelf = self, let sticker = sticker {
let inputNodeInteraction = ChatMediaInputNodeInteraction( let inputNodeInteraction = ChatMediaInputNodeInteraction(
@ -172,7 +183,7 @@ private final class ChatEmptyNodeNearbyChatContent: ASDisplayNode, ChatEmptyNode
let index = ItemCollectionItemIndex(index: 0, id: 0) let index = ItemCollectionItemIndex(index: 0, id: 0)
let collectionId = ItemCollectionId(namespace: 0, id: 0) let collectionId = ItemCollectionId(namespace: 0, id: 0)
let stickerPackItem = StickerPackItem(index: index, file: sticker.file, indexKeys: []) let stickerPackItem = StickerPackItem(index: index, file: sticker, indexKeys: [])
let item = ChatMediaInputStickerGridItem(account: strongSelf.account, collectionId: collectionId, stickerPackInfo: nil, index: ItemCollectionViewEntryIndex(collectionIndex: 0, collectionId: collectionId, itemIndex: index), stickerItem: stickerPackItem, canManagePeerSpecificPack: nil, interfaceInteraction: nil, inputNodeInteraction: inputNodeInteraction, hasAccessory: false, theme: interfaceState.theme, large: true, selected: {}) let item = ChatMediaInputStickerGridItem(account: strongSelf.account, collectionId: collectionId, stickerPackInfo: nil, index: ItemCollectionViewEntryIndex(collectionIndex: 0, collectionId: collectionId, itemIndex: index), stickerItem: stickerPackItem, canManagePeerSpecificPack: nil, interfaceInteraction: nil, inputNodeInteraction: inputNodeInteraction, hasAccessory: false, theme: interfaceState.theme, large: true, selected: {})
strongSelf.stickerItem = item strongSelf.stickerItem = item
strongSelf.stickerNode.updateLayout(item: item, size: stickerSize, isVisible: true, synchronousLoads: true) strongSelf.stickerNode.updateLayout(item: item, size: stickerSize, isVisible: true, synchronousLoads: true)

View File

@ -360,7 +360,8 @@ final class ChatMediaInputStickerGridItemNode: GridItemNode {
if let item = self.item, isPlaying, !self.didSetUpAnimationNode { if let item = self.item, isPlaying, !self.didSetUpAnimationNode {
self.didSetUpAnimationNode = true self.didSetUpAnimationNode = true
let dimensions = item.stickerItem.file.dimensions ?? PixelDimensions(width: 512, height: 512) let dimensions = item.stickerItem.file.dimensions ?? PixelDimensions(width: 512, height: 512)
let fittedDimensions = dimensions.cgSize.aspectFitted(CGSize(width: 160.0, height: 160.0)) let fitSize = item.large ? CGSize(width: 384.0, height: 384.0) : CGSize(width: 160.0, height: 160.0)
let fittedDimensions = dimensions.cgSize.aspectFitted(fitSize)
self.animationNode?.setup(source: AnimatedStickerResourceSource(account: item.account, resource: item.stickerItem.file.resource), width: Int(fittedDimensions.width), height: Int(fittedDimensions.height), mode: .cached) self.animationNode?.setup(source: AnimatedStickerResourceSource(account: item.account, resource: item.stickerItem.file.resource), width: Int(fittedDimensions.width), height: Int(fittedDimensions.height), mode: .cached)
} }
} }

View File

@ -118,9 +118,10 @@ final class ChatPanelInterfaceInteraction {
let displaySendMessageOptions: (ASDisplayNode, ContextGesture) -> Void let displaySendMessageOptions: (ASDisplayNode, ContextGesture) -> Void
let openScheduledMessages: () -> Void let openScheduledMessages: () -> Void
let displaySearchResultsTooltip: (ASDisplayNode, CGRect) -> Void let displaySearchResultsTooltip: (ASDisplayNode, CGRect) -> Void
let openPeersNearby: () -> Void
let statuses: ChatPanelInterfaceInteractionStatuses? let statuses: ChatPanelInterfaceInteractionStatuses?
init(setupReplyMessage: @escaping (MessageId, @escaping (ContainedViewLayoutTransition) -> Void) -> Void, setupEditMessage: @escaping (MessageId?, @escaping (ContainedViewLayoutTransition) -> Void) -> Void, beginMessageSelection: @escaping ([MessageId], @escaping (ContainedViewLayoutTransition) -> Void) -> Void, deleteSelectedMessages: @escaping () -> Void, reportSelectedMessages: @escaping () -> Void, reportMessages: @escaping ([Message], ContextController?) -> Void, deleteMessages: @escaping ([Message], ContextController?, @escaping (ContextMenuActionResult) -> Void) -> Void, forwardSelectedMessages: @escaping () -> Void, forwardCurrentForwardMessages: @escaping () -> Void, forwardMessages: @escaping ([Message]) -> Void, shareSelectedMessages: @escaping () -> Void, updateTextInputStateAndMode: @escaping ((ChatTextInputState, ChatInputMode) -> (ChatTextInputState, ChatInputMode)) -> Void, updateInputModeAndDismissedButtonKeyboardMessageId: @escaping ((ChatPresentationInterfaceState) -> (ChatInputMode, MessageId?)) -> Void, openStickers: @escaping () -> Void, editMessage: @escaping () -> Void, beginMessageSearch: @escaping (ChatSearchDomain, String) -> Void, dismissMessageSearch: @escaping () -> Void, updateMessageSearch: @escaping (String) -> Void, openSearchResults: @escaping () -> Void, navigateMessageSearch: @escaping (ChatPanelSearchNavigationAction) -> Void, openCalendarSearch: @escaping () -> Void, toggleMembersSearch: @escaping (Bool) -> Void, navigateToMessage: @escaping (MessageId) -> Void, navigateToChat: @escaping (PeerId) -> Void, navigateToProfile: @escaping (PeerId) -> Void, openPeerInfo: @escaping () -> Void, togglePeerNotifications: @escaping () -> Void, sendContextResult: @escaping (ChatContextResultCollection, ChatContextResult, ASDisplayNode, CGRect) -> Bool, sendBotCommand: @escaping (Peer, String) -> Void, sendBotStart: @escaping (String?) -> Void, botSwitchChatWithPayload: @escaping (PeerId, String) -> Void, beginMediaRecording: @escaping (Bool) -> Void, finishMediaRecording: @escaping (ChatFinishMediaRecordingAction) -> Void, stopMediaRecording: @escaping () -> Void, lockMediaRecording: @escaping () -> Void, deleteRecordedMedia: @escaping () -> Void, sendRecordedMedia: @escaping () -> Void, displayRestrictedInfo: @escaping (ChatPanelRestrictionInfoSubject, ChatPanelRestrictionInfoDisplayType) -> Void, displayVideoUnmuteTip: @escaping (CGPoint?) -> Void, switchMediaRecordingMode: @escaping () -> Void, setupMessageAutoremoveTimeout: @escaping () -> Void, sendSticker: @escaping (FileMediaReference, ASDisplayNode, CGRect) -> Bool, unblockPeer: @escaping () -> Void, pinMessage: @escaping (MessageId) -> Void, unpinMessage: @escaping () -> Void, shareAccountContact: @escaping () -> Void, reportPeer: @escaping () -> Void, presentPeerContact: @escaping () -> Void, dismissReportPeer: @escaping () -> Void, deleteChat: @escaping () -> Void, beginCall: @escaping (Bool) -> Void, toggleMessageStickerStarred: @escaping (MessageId) -> Void, presentController: @escaping (ViewController, Any?) -> Void, getNavigationController: @escaping () -> NavigationController?, presentGlobalOverlayController: @escaping (ViewController, Any?) -> Void, navigateFeed: @escaping () -> Void, openGrouping: @escaping () -> Void, toggleSilentPost: @escaping () -> Void, requestUnvoteInMessage: @escaping (MessageId) -> Void, requestStopPollInMessage: @escaping (MessageId) -> Void, updateInputLanguage: @escaping ((String?) -> String?) -> Void, unarchiveChat: @escaping () -> Void, openLinkEditing: @escaping () -> Void, reportPeerIrrelevantGeoLocation: @escaping () -> Void, displaySlowmodeTooltip: @escaping (ASDisplayNode, CGRect) -> Void, displaySendMessageOptions: @escaping (ASDisplayNode, ContextGesture) -> Void, openScheduledMessages: @escaping () -> Void, displaySearchResultsTooltip: @escaping (ASDisplayNode, CGRect) -> Void, statuses: ChatPanelInterfaceInteractionStatuses?) { init(setupReplyMessage: @escaping (MessageId, @escaping (ContainedViewLayoutTransition) -> Void) -> Void, setupEditMessage: @escaping (MessageId?, @escaping (ContainedViewLayoutTransition) -> Void) -> Void, beginMessageSelection: @escaping ([MessageId], @escaping (ContainedViewLayoutTransition) -> Void) -> Void, deleteSelectedMessages: @escaping () -> Void, reportSelectedMessages: @escaping () -> Void, reportMessages: @escaping ([Message], ContextController?) -> Void, deleteMessages: @escaping ([Message], ContextController?, @escaping (ContextMenuActionResult) -> Void) -> Void, forwardSelectedMessages: @escaping () -> Void, forwardCurrentForwardMessages: @escaping () -> Void, forwardMessages: @escaping ([Message]) -> Void, shareSelectedMessages: @escaping () -> Void, updateTextInputStateAndMode: @escaping ((ChatTextInputState, ChatInputMode) -> (ChatTextInputState, ChatInputMode)) -> Void, updateInputModeAndDismissedButtonKeyboardMessageId: @escaping ((ChatPresentationInterfaceState) -> (ChatInputMode, MessageId?)) -> Void, openStickers: @escaping () -> Void, editMessage: @escaping () -> Void, beginMessageSearch: @escaping (ChatSearchDomain, String) -> Void, dismissMessageSearch: @escaping () -> Void, updateMessageSearch: @escaping (String) -> Void, openSearchResults: @escaping () -> Void, navigateMessageSearch: @escaping (ChatPanelSearchNavigationAction) -> Void, openCalendarSearch: @escaping () -> Void, toggleMembersSearch: @escaping (Bool) -> Void, navigateToMessage: @escaping (MessageId) -> Void, navigateToChat: @escaping (PeerId) -> Void, navigateToProfile: @escaping (PeerId) -> Void, openPeerInfo: @escaping () -> Void, togglePeerNotifications: @escaping () -> Void, sendContextResult: @escaping (ChatContextResultCollection, ChatContextResult, ASDisplayNode, CGRect) -> Bool, sendBotCommand: @escaping (Peer, String) -> Void, sendBotStart: @escaping (String?) -> Void, botSwitchChatWithPayload: @escaping (PeerId, String) -> Void, beginMediaRecording: @escaping (Bool) -> Void, finishMediaRecording: @escaping (ChatFinishMediaRecordingAction) -> Void, stopMediaRecording: @escaping () -> Void, lockMediaRecording: @escaping () -> Void, deleteRecordedMedia: @escaping () -> Void, sendRecordedMedia: @escaping () -> Void, displayRestrictedInfo: @escaping (ChatPanelRestrictionInfoSubject, ChatPanelRestrictionInfoDisplayType) -> Void, displayVideoUnmuteTip: @escaping (CGPoint?) -> Void, switchMediaRecordingMode: @escaping () -> Void, setupMessageAutoremoveTimeout: @escaping () -> Void, sendSticker: @escaping (FileMediaReference, ASDisplayNode, CGRect) -> Bool, unblockPeer: @escaping () -> Void, pinMessage: @escaping (MessageId) -> Void, unpinMessage: @escaping () -> Void, shareAccountContact: @escaping () -> Void, reportPeer: @escaping () -> Void, presentPeerContact: @escaping () -> Void, dismissReportPeer: @escaping () -> Void, deleteChat: @escaping () -> Void, beginCall: @escaping (Bool) -> Void, toggleMessageStickerStarred: @escaping (MessageId) -> Void, presentController: @escaping (ViewController, Any?) -> Void, getNavigationController: @escaping () -> NavigationController?, presentGlobalOverlayController: @escaping (ViewController, Any?) -> Void, navigateFeed: @escaping () -> Void, openGrouping: @escaping () -> Void, toggleSilentPost: @escaping () -> Void, requestUnvoteInMessage: @escaping (MessageId) -> Void, requestStopPollInMessage: @escaping (MessageId) -> Void, updateInputLanguage: @escaping ((String?) -> String?) -> Void, unarchiveChat: @escaping () -> Void, openLinkEditing: @escaping () -> Void, reportPeerIrrelevantGeoLocation: @escaping () -> Void, displaySlowmodeTooltip: @escaping (ASDisplayNode, CGRect) -> Void, displaySendMessageOptions: @escaping (ASDisplayNode, ContextGesture) -> Void, openScheduledMessages: @escaping () -> Void, openPeersNearby: @escaping () -> Void, displaySearchResultsTooltip: @escaping (ASDisplayNode, CGRect) -> Void, statuses: ChatPanelInterfaceInteractionStatuses?) {
self.setupReplyMessage = setupReplyMessage self.setupReplyMessage = setupReplyMessage
self.setupEditMessage = setupEditMessage self.setupEditMessage = setupEditMessage
self.beginMessageSelection = beginMessageSelection self.beginMessageSelection = beginMessageSelection
@ -188,6 +189,7 @@ final class ChatPanelInterfaceInteraction {
self.displaySlowmodeTooltip = displaySlowmodeTooltip self.displaySlowmodeTooltip = displaySlowmodeTooltip
self.displaySendMessageOptions = displaySendMessageOptions self.displaySendMessageOptions = displaySendMessageOptions
self.openScheduledMessages = openScheduledMessages self.openScheduledMessages = openScheduledMessages
self.openPeersNearby = openPeersNearby
self.displaySearchResultsTooltip = displaySearchResultsTooltip self.displaySearchResultsTooltip = displaySearchResultsTooltip
self.statuses = statuses self.statuses = statuses
} }

View File

@ -121,6 +121,7 @@ final class ChatRecentActionsController: TelegramBaseController {
}, displaySlowmodeTooltip: { _, _ in }, displaySlowmodeTooltip: { _, _ in
}, displaySendMessageOptions: { _, _ in }, displaySendMessageOptions: { _, _ in
}, openScheduledMessages: { }, openScheduledMessages: {
}, openPeersNearby: {
}, displaySearchResultsTooltip: { _, _ in }, displaySearchResultsTooltip: { _, _ in
}, statuses: nil) }, statuses: nil)

View File

@ -186,7 +186,11 @@ private final class ChatInfoTitlePanelPeerNearbyInfoNode: ASDisplayNode {
private let labelNode: ImmediateTextNode private let labelNode: ImmediateTextNode
private let filledBackgroundNode: LinkHighlightingNode private let filledBackgroundNode: LinkHighlightingNode
init(openPeer: @escaping () -> Void) { private let openPeersNearby: () -> Void
init(openPeersNearby: @escaping () -> Void) {
self.openPeersNearby = openPeersNearby
self.labelNode = ImmediateTextNode() self.labelNode = ImmediateTextNode()
self.labelNode.maximumNumberOfLines = 1 self.labelNode.maximumNumberOfLines = 1
self.labelNode.textAlignment = .center self.labelNode.textAlignment = .center
@ -197,22 +201,17 @@ private final class ChatInfoTitlePanelPeerNearbyInfoNode: ASDisplayNode {
self.addSubnode(self.filledBackgroundNode) self.addSubnode(self.filledBackgroundNode)
self.addSubnode(self.labelNode) self.addSubnode(self.labelNode)
}
self.labelNode.highlightAttributeAction = { attributes in override func didLoad() {
for (key, _) in attributes { super.didLoad()
if key.rawValue == "_Link" {
return key let tapRecognizer = UITapGestureRecognizer(target: self, action: #selector(self.tapGesture(_:)))
} self.view.addGestureRecognizer(tapRecognizer)
}
return nil
}
self.labelNode.tapAttributeAction = { attributes, _ in
for (key, _) in attributes {
if key.rawValue == "_Link" {
openPeer()
}
}
} }
@objc private func tapGesture(_ gestureRecognizer: UITapGestureRecognizer) {
self.openPeersNearby()
} }
func update(width: CGFloat, theme: PresentationTheme, strings: PresentationStrings, wallpaper: TelegramWallpaper, chatPeer: Peer, distance: Int32, transition: ContainedViewLayoutTransition) -> CGFloat { func update(width: CGFloat, theme: PresentationTheme, strings: PresentationStrings, wallpaper: TelegramWallpaper, chatPeer: Peer, distance: Int32, transition: ContainedViewLayoutTransition) -> CGFloat {
@ -233,7 +232,7 @@ private final class ChatInfoTitlePanelPeerNearbyInfoNode: ASDisplayNode {
let attributedString = NSMutableAttributedString(string: stringAndRanges.0, font: Font.regular(13.0), textColor: primaryTextColor) let attributedString = NSMutableAttributedString(string: stringAndRanges.0, font: Font.regular(13.0), textColor: primaryTextColor)
let boldAttributes = [NSAttributedString.Key.font: Font.semibold(13.0), NSAttributedString.Key(rawValue: "_Link"): true as NSNumber] let boldAttributes = [NSAttributedString.Key.font: Font.semibold(13.0), NSAttributedString.Key(rawValue: "_Link"): true as NSNumber]
for (_, range) in stringAndRanges.1 { for (_, range) in stringAndRanges.1.prefix(1) {
attributedString.addAttributes(boldAttributes, range: range) attributedString.addAttributes(boldAttributes, range: range)
} }
@ -438,8 +437,8 @@ final class ChatReportPeerTitlePanelNode: ChatTitleAccessoryPanelNode {
peerNearbyInfoNode = current peerNearbyInfoNode = current
} else { } else {
peerNearbyInfoTransition = .immediate peerNearbyInfoTransition = .immediate
peerNearbyInfoNode = ChatInfoTitlePanelPeerNearbyInfoNode(openPeer: { [weak self] in peerNearbyInfoNode = ChatInfoTitlePanelPeerNearbyInfoNode(openPeersNearby: { [weak self] in
self?.interfaceInteraction?.navigateToProfile(chatPeer.id) self?.interfaceInteraction?.openPeersNearby()
}) })
self.addSubnode(peerNearbyInfoNode) self.addSubnode(peerNearbyInfoNode)
self.peerNearbyInfoNode = peerNearbyInfoNode self.peerNearbyInfoNode = peerNearbyInfoNode

View File

@ -148,7 +148,7 @@ private func chatMessageGalleryControllerData(context: AccountContext, message:
} else if ext == "wav" || ext == "opus" { } else if ext == "wav" || ext == "opus" {
return .audio(file) return .audio(file)
} else if ext == "json", let fileSize = file.size, fileSize < 1024 * 1024 { } else if ext == "json", let fileSize = file.size, fileSize < 1024 * 1024 {
if let path = context.account.postbox.mediaBox.completedResourcePath(file.resource), let _ = LOTComposition(filePath: path) { if let path = context.account.postbox.mediaBox.completedResourcePath(file.resource), let composition = LOTComposition(filePath: path), composition.timeDuration > 0.0 {
let gallery = GalleryController(context: context, source: .peerMessagesAtId(message.id), invertItemOrder: reverseMessageGalleryOrder, streamSingleVideo: stream, fromPlayingVideo: autoplayingVideo, landscape: landscape, timecode: timecode, synchronousLoad: synchronousLoad, replaceRootController: { [weak navigationController] controller, ready in let gallery = GalleryController(context: context, source: .peerMessagesAtId(message.id), invertItemOrder: reverseMessageGalleryOrder, streamSingleVideo: stream, fromPlayingVideo: autoplayingVideo, landscape: landscape, timecode: timecode, synchronousLoad: synchronousLoad, replaceRootController: { [weak navigationController] controller, ready in
navigationController?.replaceTopController(controller, animated: false, ready: ready) navigationController?.replaceTopController(controller, animated: false, ready: ready)
}, baseNavigationController: navigationController, actionInteraction: actionInteraction) }, baseNavigationController: navigationController, actionInteraction: actionInteraction)

View File

@ -37,6 +37,7 @@ import LocationUI
import Geocoding import Geocoding
import TextFormat import TextFormat
import StatisticsUI import StatisticsUI
import StickerResources
protocol PeerInfoScreenItem: class { protocol PeerInfoScreenItem: class {
var id: AnyHashable { get } var id: AnyHashable { get }
@ -413,6 +414,7 @@ final class PeerInfoSelectionPanelNode: ASDisplayNode {
}, displaySlowmodeTooltip: { _, _ in }, displaySlowmodeTooltip: { _, _ in
}, displaySendMessageOptions: { _, _ in }, displaySendMessageOptions: { _, _ in
}, openScheduledMessages: { }, openScheduledMessages: {
}, openPeersNearby: {
}, displaySearchResultsTooltip: { _, _ in }, displaySearchResultsTooltip: { _, _ in
}, statuses: nil) }, statuses: nil)
@ -1092,6 +1094,9 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD
} }
private var didSetReady = false private var didSetReady = false
private let preloadedSticker = Promise<TelegramMediaFile?>(nil)
private let preloadStickerDisposable = MetaDisposable()
init(controller: PeerInfoScreen, context: AccountContext, peerId: PeerId, avatarInitiallyExpanded: Bool, isOpenedFromChat: Bool, nearbyPeerDistance: Int32?, callMessages: [Message], ignoreGroupInCommon: PeerId?) { init(controller: PeerInfoScreen, context: AccountContext, peerId: PeerId, avatarInitiallyExpanded: Bool, isOpenedFromChat: Bool, nearbyPeerDistance: Int32?, callMessages: [Message], ignoreGroupInCommon: PeerId?) {
self.controller = controller self.controller = controller
self.context = context self.context = context
@ -2011,6 +2016,27 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD
} }
strongSelf.updateData(data) strongSelf.updateData(data)
}) })
if let _ = nearbyPeerDistance {
self.preloadedSticker.set(.single(nil)
|> then(randomGreetingSticker(account: context.account)
|> map { item in
return item?.file
}))
self.preloadStickerDisposable.set((self.preloadedSticker.get()
|> mapToSignal { sticker -> Signal<Void, NoError> in
if let sticker = sticker {
let _ = freeMediaFileInteractiveFetched(account: context.account, fileReference: .standalone(media: sticker)).start()
return chatMessageAnimationData(postbox: context.account.postbox, resource: sticker.resource, fitzModifier: nil, width: 384, height: 384, synchronousLoad: false)
|> mapToSignal { _ -> Signal<Void, NoError> in
return .complete()
}
} else {
return .complete()
}
}).start())
}
} }
deinit { deinit {
@ -2023,6 +2049,7 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD
self.updateAvatarDisposable.dispose() self.updateAvatarDisposable.dispose()
self.selectAddMemberDisposable.dispose() self.selectAddMemberDisposable.dispose()
self.addMemberDisposable.dispose() self.addMemberDisposable.dispose()
self.preloadStickerDisposable.dispose()
} }
override func didLoad() { override func didLoad() {
@ -2206,7 +2233,13 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD
switch key { switch key {
case .message: case .message:
if let navigationController = controller.navigationController as? NavigationController { if let navigationController = controller.navigationController as? NavigationController {
self.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: self.context, chatLocation: .peer(self.peerId), peerNearbyData: self.nearbyPeerDistance.flatMap({ ChatPeerNearbyData(distance: $0) }))) let _ = (self.preloadedSticker.get()
|> take(1)
|> deliverOnMainQueue).start(next: { [weak self] sticker in
if let strongSelf = self {
strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .peer(strongSelf.peerId), peerNearbyData: strongSelf.nearbyPeerDistance.flatMap({ ChatPeerNearbyData(distance: $0, sticker: sticker) })))
}
})
} }
case .discussion: case .discussion:
if let cachedData = self.data?.cachedData as? CachedChannelData, let linkedDiscussionPeerId = cachedData.linkedDiscussionPeerId { if let cachedData = self.data?.cachedData as? CachedChannelData, let linkedDiscussionPeerId = cachedData.linkedDiscussionPeerId {
@ -2414,7 +2447,13 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD
private func openChatWithMessageSearch() { private func openChatWithMessageSearch() {
if let navigationController = (self.controller?.navigationController as? NavigationController) { if let navigationController = (self.controller?.navigationController as? NavigationController) {
self.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: self.context, chatLocation: .peer(self.peerId), activateMessageSearch: (.everything, ""), peerNearbyData: self.nearbyPeerDistance.flatMap({ ChatPeerNearbyData(distance: $0) }))) let _ = (self.preloadedSticker.get()
|> take(1)
|> deliverOnMainQueue).start(next: { [weak self] sticker in
if let strongSelf = self {
strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .peer(strongSelf.peerId), activateMessageSearch: (.everything, ""), peerNearbyData: strongSelf.nearbyPeerDistance.flatMap({ ChatPeerNearbyData(distance: $0, sticker: sticker) })))
}
})
} }
} }
@ -2718,7 +2757,13 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD
private func openChat() { private func openChat() {
if let navigationController = self.controller?.navigationController as? NavigationController { if let navigationController = self.controller?.navigationController as? NavigationController {
self.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: self.context, chatLocation: .peer(self.peerId), peerNearbyData: self.nearbyPeerDistance.flatMap({ ChatPeerNearbyData(distance: $0) }))) let _ = (self.preloadedSticker.get()
|> take(1)
|> deliverOnMainQueue).start(next: { [weak self] sticker in
if let strongSelf = self {
strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .peer(strongSelf.peerId), peerNearbyData: strongSelf.nearbyPeerDistance.flatMap({ ChatPeerNearbyData(distance: $0, sticker: sticker) })))
}
})
} }
} }

View File

@ -548,6 +548,7 @@ public class PeerMediaCollectionController: TelegramBaseController {
}, displaySlowmodeTooltip: { _, _ in }, displaySlowmodeTooltip: { _, _ in
}, displaySendMessageOptions: { _, _ in }, displaySendMessageOptions: { _, _ in
}, openScheduledMessages: { }, openScheduledMessages: {
}, openPeersNearby: {
}, displaySearchResultsTooltip: { _, _ in }, displaySearchResultsTooltip: { _, _ in
}, statuses: nil) }, statuses: nil)