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.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.PeerNearbyDistance" = "%@ is %@ away";
"Conversation.PeerNearbyDistance" = "%1$@ is %2$@ away";
"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.GroupMembers" = "Members";

View File

@ -204,13 +204,20 @@ public final class ChatPeekTimeout {
public final class ChatPeerNearbyData: Equatable {
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
}
public let distance: Int32
public let sticker: TelegramMediaFile?
public init(distance: Int32) {
public init(distance: Int32, sticker: TelegramMediaFile?) {
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.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.tapGesture(_:))))
self.scrollNode.view.showsHorizontalScrollIndicator = false
self.scrollNode.view.showsVerticalScrollIndicator = false
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) {
self.indexes = indexes
var items: [GalleryThumbnailItem] = items.count <= 1 ? [] : items
let items: [GalleryThumbnailItem] = items.count <= 1 ? [] : items
var updated = false
if self.items.count == 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) NSArray *faces;
@property (nonatomic, strong) NSArray *cachedVideoThumbnails;
@property (nonatomic, strong) AVPlayer *player;
@property (nonatomic, strong) TGPhotoEntitiesContainerView *entitiesView;

View File

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

View File

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

View File

@ -414,12 +414,34 @@
[_finalFilter addTarget:previewOutput.imageView];
}
for (PGPhotoEditorView *view in _additionalOutputs) {
[_finalFilter addTarget:view];
}
if (_histogramGenerator != nil && !self.standalone) {
[_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
- (void)createResultImageWithCompletion:(void (^)(UIImage *image))completion

View File

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

View File

@ -912,7 +912,7 @@ static CGPoint TGCameraControllerClampPointToScreenSize(__unused id self, __unus
{
case PGCameraModePhoto:
{
if (_intent == TGCameraControllerGenericIntent)
if (_intent == TGCameraControllerGenericIntent || _intent == TGCameraControllerAvatarIntent)
{
_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)
return !_camera.isRecordingVideo && _items.count == 0;
else if (gestureRecognizer == _photoSwipeGestureRecognizer || gestureRecognizer == _videoSwipeGestureRecognizer)
return _intent == TGCameraControllerGenericIntent && !_camera.isRecordingVideo;
return (_intent == TGCameraControllerGenericIntent || _intent == TGCameraControllerAvatarIntent) && !_camera.isRecordingVideo;
else if (gestureRecognizer == _pinchGestureRecognizer)
return _camera.isZoomAvailable;

View File

@ -40,12 +40,14 @@ typedef enum
UIControl *_scrubberHandle;
UIControl *_dotHandle;
UIView *_dotContentView;
UIImageView *_dotImageView;
__weak UIView *_dotVideoView;
UIImageView *_dotFrameView;
UIPanGestureRecognizer *_panGestureRecognizer;
UILongPressGestureRecognizer *_pressGestureRecognizer;
UITapGestureRecognizer *_tapGestureRecognizer;
bool _beganInteraction;
bool _endedInteraction;
@ -235,10 +237,7 @@ typedef enum
CGFloat delta = originX - trimView.frame.origin.x;
CGFloat maxWidth = availableTrimRect.size.width + normalScrubbingRect.origin.x * 2 - originX;
CGRect trimViewRect = CGRectMake(originX,
trimView.frame.origin.y,
MIN(maxWidth, trimView.frame.size.width - delta),
trimView.frame.size.height);
CGRect trimViewRect = CGRectMake(originX, trimView.frame.origin.y, MIN(maxWidth, trimView.frame.size.width - delta), trimView.frame.size.height);
NSTimeInterval trimStartPosition = 0.0;
NSTimeInterval trimEndPosition = 0.0;
@ -251,10 +250,7 @@ typedef enum
if (strongSelf.maximumLength > DBL_EPSILON && duration > strongSelf.maximumLength)
{
trimViewRect = CGRectMake(trimView.frame.origin.x + delta,
trimView.frame.origin.y,
trimView.frame.size.width,
trimView.frame.size.height);
trimViewRect = CGRectMake(trimView.frame.origin.x + delta, trimView.frame.origin.y, trimView.frame.size.width, trimView.frame.size.height);
[strongSelf _trimStartPosition:&trimStartPosition trimEndPosition:&trimEndPosition forTrimFrame:trimViewRect duration:strongSelf.duration];
}
@ -276,6 +272,9 @@ typedef enum
UIView *handle = strongSelf->_scrubberHandle;
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;
if ([delegate respondsToSelector:@selector(videoScrubber:editingStartValueDidChange:)])
[delegate videoScrubber:strongSelf editingStartValueDidChange:trimStartPosition];
@ -345,6 +344,9 @@ typedef enum
UIView *handle = strongSelf->_scrubberHandle;
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;
if ([delegate respondsToSelector:@selector(videoScrubber:editingEndValueDidChange:)])
[delegate videoScrubber:strongSelf editingEndValueDidChange:trimEndPosition];
@ -380,10 +382,14 @@ typedef enum
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.contentMode = UIViewContentModeScaleAspectFill;
[_dotHandle addSubview:_dotImageView];
[_dotContentView addSubview:_dotImageView];
_dotFrameView = [[UIImageView alloc] initWithFrame:_dotHandle.bounds];
_dotFrameView.image = dotFrameImage;
@ -423,6 +429,10 @@ typedef enum
[_scrubberHandle 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.alpha = 0.45f;
_arrowView.hidden = true;
@ -453,12 +463,13 @@ typedef enum
_hasDotPicker = hasDotPicker;
_dotHandle.hidden = !hasDotPicker;
_scrubberHandle.hidden = true;
_tapGestureRecognizer.enabled = hasDotPicker;
}
- (void)setDotVideoView:(UIView *)dotVideoView {
_dotVideoView = dotVideoView;
_dotVideoView.frame = CGRectInset(_dotHandle.bounds, 2.0, 2.0);
[_dotHandle insertSubview:dotVideoView belowSubview:_dotFrameView];
_dotVideoView.frame = _dotImageView.bounds;
[_dotContentView addSubview:_dotVideoView];
}
- (void)setDotImage:(UIImage *)dotImage {
@ -674,9 +685,14 @@ typedef enum
frameAspectRatio = _cropRect.size.width / _cropRect.size.height;
else
frameAspectRatio = originalAspectRatio;
_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);
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
{
if (animated)

View File

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

View File

@ -56,6 +56,10 @@
if (_imageView == nil)
return;
[self updateCropping];
}
- (void)updateCropping {
CGAffineTransform transform = CGAffineTransformMakeRotation(TGRotationForOrientation(_cropOrientation));
if (_cropMirrored)
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);
}
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);
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

View File

@ -342,22 +342,18 @@ const CGFloat TGPhotoAvatarCropButtonsWrapperSize = 61.0f;
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];
CGSize referenceSize = [self referenceViewSizeForOrientation:orientation];
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)
{
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)
{
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);

View File

@ -3,16 +3,14 @@
@class PGPhotoEditor;
@class PGPhotoTool;
@class TGPhotoEditorPreviewView;
@class TGMediaPickerGalleryVideoScrubber;
@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)setScrubberPlaying:(bool)value;
- (NSTimeInterval)coverPosition;
- (NSTimeInterval)trimStartValue;
- (NSTimeInterval)trimEndValue;
- (void)beginScrubbing;
- (void)endScrubbing:(bool (^)(void))completion;
- (void)setPlayButtonHidden:(bool)hidden animated:(bool)animated;
@end

View File

@ -6,6 +6,7 @@
#import "TGPhotoEditorInterfaceAssets.h"
#import "PGPhotoEditor.h"
#import "PGPhotoEditorView.h"
#import <LegacyComponents/TGPhotoEditorUtils.h>
#import <LegacyComponents/TGPaintUtils.h>
@ -19,11 +20,12 @@
const CGFloat TGPhotoAvatarPreviewPanelSize = 96.0f;
const CGFloat TGPhotoAvatarPreviewLandscapePanelSize = TGPhotoAvatarPreviewPanelSize + 40.0f;
@interface TGPhotoAvatarPreviewController () <TGMediaPickerGalleryVideoScrubberDataSource, TGMediaPickerGalleryVideoScrubberDelegate>
@interface TGPhotoAvatarPreviewController ()
{
bool _appeared;
TGPhotoEditorSparseView *_wrapperView;
TGMediaPickerGalleryVideoScrubber *_scrubberView;
UIView *_portraitToolsWrapperView;
UIView *_landscapeToolsWrapperView;
UIView *_portraitWrapperBackgroundView;
@ -35,13 +37,7 @@ const CGFloat TGPhotoAvatarPreviewLandscapePanelSize = TGPhotoAvatarPreviewPanel
UIView *_landscapeToolControlView;
UIImageView *_areaMaskView;
CGFloat _currentDiameter;
TGMediaPickerGalleryVideoScrubber *_scrubberView;
TGModernGalleryVideoView *_dotVideoView;
UILabel *_coverLabel;
bool _wasPlayingBeforeScrubbing;
bool _requestingThumbnails;
SMetaDisposable *_thumbnailsDisposable;
}
@property (nonatomic, weak) PGPhotoEditor *photoEditor;
@ -51,24 +47,18 @@ const CGFloat TGPhotoAvatarPreviewLandscapePanelSize = TGPhotoAvatarPreviewPanel
@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];
if (self != nil)
{
self.photoEditor = photoEditor;
self.previewView = previewView;
_thumbnailsDisposable = [[SMetaDisposable alloc] init];
_scrubberView = scrubberView;
}
return self;
}
- (void)dealloc
{
[_thumbnailsDisposable dispose];
}
- (void)loadView
{
[super loadView];
@ -114,10 +104,6 @@ const CGFloat TGPhotoAvatarPreviewLandscapePanelSize = TGPhotoAvatarPreviewPanel
_areaMaskView.alpha = 0.0f;
[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];
_coverLabel = [[UILabel alloc] init];
@ -137,22 +123,6 @@ const CGFloat TGPhotoAvatarPreviewLandscapePanelSize = TGPhotoAvatarPreviewPanel
[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
{
TGPhotoEditorPreviewView *previewView = self.previewView;
@ -264,6 +234,8 @@ const CGFloat TGPhotoAvatarPreviewLandscapePanelSize = TGPhotoAvatarPreviewPanel
{
_dismissing = true;
self.photoEditor.additionalOutputs = @[];
TGPhotoEditorPreviewView *previewView = self.previewView;
[previewView prepareForTransitionOut];
@ -488,7 +460,6 @@ const CGFloat TGPhotoAvatarPreviewLandscapePanelSize = TGPhotoAvatarPreviewPanel
_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);
// _portraitCollectionView.frame = CGRectMake(0, 0, _portraitToolsWrapperView.frame.size.width, panelSize);
}
@ -551,11 +522,7 @@ const CGFloat TGPhotoAvatarPreviewLandscapePanelSize = TGPhotoAvatarPreviewPanel
if (!_dismissing)
[self updateToolViews];
dispatch_async(dispatch_get_main_queue(), ^{
[_scrubberView reloadThumbnails];
});
[self updatePreviewView];
}
@ -571,42 +538,13 @@ const CGFloat TGPhotoAvatarPreviewLandscapePanelSize = TGPhotoAvatarPreviewPanel
- (TGPhotoEditorTab)highlightedTabs
{
bool hasSimpleValue = false;
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;
// }
// }
//
id<TGMediaEditAdjustments> adjustments = [self.photoEditor exportAdjustments];
TGPhotoEditorTab tabs = TGPhotoEditorNoneTab;
if (hasSimpleValue)
if (adjustments.toolsApplied)
tabs |= TGPhotoEditorToolsTab;
if (hasBlur)
tabs |= TGPhotoEditorBlurTab;
if (hasCurves)
tabs |= TGPhotoEditorCurvesTab;
if (hasTint)
tabs |= TGPhotoEditorTintTab;
if (adjustments.hasPainting)
tabs |= TGPhotoEditorPaintTab;
return tabs;
}
@ -632,30 +570,8 @@ const CGFloat TGPhotoAvatarPreviewLandscapePanelSize = TGPhotoAvatarPreviewPanel
// }
}
#pragma mark - Video Scrubber Data Source & Delegate
#pragma mark Scrubbing
- (NSTimeInterval)videoScrubberDuration:(TGMediaPickerGalleryVideoScrubber *)__unused videoScrubber
- (void)beginScrubbing
{
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;
[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:^{
_flashView.alpha = 1.0f;
} completion:^(BOOL finished) {
@ -687,212 +590,17 @@ const CGFloat TGPhotoAvatarPreviewLandscapePanelSize = TGPhotoAvatarPreviewPanel
_flashView.alpha = 0.0f;
} completion:^(BOOL finished) {
TGDispatchAfter(1.0, dispatch_get_main_queue(), ^{
if (_scrubberView.isScrubbing) {
return;
if (completion()) {
[UIView animateWithDuration:0.2 animations:^{
_areaMaskView.alpha = 0.0f;
_coverLabel.alpha = 0.7f;
}];
self.controlVideoPlayback(true);
}
[UIView animateWithDuration:0.2 animations:^{
_areaMaskView.alpha = 0.0f;
_coverLabel.alpha = 0.7f;
}];
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

View File

@ -87,7 +87,7 @@
[_portraitButton addTarget:self action:@selector(blurButtonPressed:) forControlEvents:UIControlEventTouchUpInside];
[_portraitButton setImage:TGTintedImage([UIImage imageNamed:@"Editor/BlurPortrait"], [UIColor whiteColor])];
[_portraitButton setTitle:TGLocalized(@"PhotoEditor.BlurToolPortrait")];
[_buttonsWrapper addSubview:_portraitButton];
// [_buttonsWrapper addSubview:_portraitButton];
_sliderView = [[TGPhotoEditorSliderView alloc] initWithFrame:CGRectZero];
_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);
// _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);
// _linearButton.frame = CGRectMake(CGCeil(self.frame.size.width / 2 - 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);
_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 - 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);
_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 "PGPhotoEditor.h"
#import "PGEnhanceTool.h"
#import "PGPhotoEditorView.h"
#import "TGPaintFaceDetector.h"
#import <LegacyComponents/PGPhotoEditorValues.h>
@ -42,13 +43,14 @@
#import "TGPhotoAvatarPreviewController.h"
#import "TGMessageImageViewOverlayView.h"
#import "TGMediaPickerGalleryVideoScrubber.h"
#import "TGMenuSheetController.h"
#import <LegacyComponents/AVURLAsset+TGMediaItem.h>
#import "TGCameraCapturedVideo.h"
@interface TGPhotoEditorController () <ASWatcher, TGViewControllerNavigationBarAppearance, UIDocumentInteractionControllerDelegate>
@interface TGPhotoEditorController () <ASWatcher, TGViewControllerNavigationBarAppearance, TGMediaPickerGalleryVideoScrubberDataSource, TGMediaPickerGalleryVideoScrubberDelegate, UIDocumentInteractionControllerDelegate>
{
bool _switchingTab;
TGPhotoEditorTab _availableTabs;
@ -75,8 +77,6 @@
SMetaDisposable *_playerItemDisposable;
id _playerStartedObserver;
id _playerReachedEndObserver;
bool _registeredKeypathObserver;
NSTimer *_positionTimer;
bool _scheduledVideoPlayback;
id<TGMediaEditAdjustments> _initialAdjustments;
@ -97,6 +97,13 @@
SMetaDisposable *_faceDetectorDisposable;
bool _initializedScrubber;
TGMediaPickerGalleryVideoScrubber *_scrubberView;
PGPhotoEditorView *_dotVideoView;
bool _requestingThumbnails;
SMetaDisposable *_thumbnailsDisposable;
id<LegacyComponentsContext> _context;
}
@ -145,6 +152,8 @@
_photoEditor.trimEndValue = videoAdjustments.trimEndValue;
}
_thumbnailsDisposable = [[SMetaDisposable alloc] init];
self.customAppearanceMethodsForwarding = true;
}
return self;
@ -155,6 +164,7 @@
[self stopVideoPlayback:true];
[_actionHandle reset];
[_faceDetectorDisposable dispose];
[_thumbnailsDisposable dispose];
}
- (void)loadView
@ -297,6 +307,12 @@
[_photoEditor setPreviewOutput:_previewView];
[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 presentEditorTab:_currentTab];
@ -364,6 +380,17 @@
if ([_currentTabController isKindOfClass:[TGPhotoCropController class]])
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;
TGMediaVideoEditAdjustments *adjustments = [_photoEditor exportAdjustments];
if ([adjustments isKindOfClass:[TGMediaVideoEditAdjustments class]])
@ -481,6 +508,9 @@
- (void)_setupPlaybackReachedEndObserver
{
if (_playerReachedEndObserver != nil)
[_player removeTimeObserver:_playerReachedEndObserver];
PGPhotoEditor *photoEditor = _photoEditor;
CMTime endTime = CMTimeSubtract(_player.currentItem.duration, CMTimeMake(10, 100));
if (photoEditor.trimEndValue > DBL_EPSILON && photoEditor.trimEndValue < CMTimeGetSeconds(_player.currentItem.duration))
@ -496,9 +526,6 @@
__strong TGPhotoEditorController *strongSelf = weakSelf;
if (strongSelf != nil) {
[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];
[self _setupPlaybackStartedObserver];
if (!_registeredKeypathObserver) {
[_player addObserver:self forKeyPath:@"rate" options:NSKeyValueObservingOptionNew context:nil];
_registeredKeypathObserver = true;
}
}
[_player play];
_positionTimer = [TGTimerTarget scheduledMainThreadTimerWithTarget:self action:@selector(positionTimerEvent) interval:0.25 repeat:true];
[self positionTimerEvent];
}
- (void)stopVideoPlayback:(bool)reset {
@ -537,33 +556,8 @@
[_player removeTimeObserver:_playerStartedObserver];
if (_playerReachedEndObserver != nil)
[_player removeTimeObserver:_playerReachedEndObserver];
if (_registeredKeypathObserver) {
[_player removeObserver:self forKeyPath:@"rate" context:nil];
_registeredKeypathObserver = false;
}
}
[_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 {
@ -573,6 +567,7 @@
- (void)setVideoEndTime:(NSTimeInterval)endTime {
_player.currentItem.forwardPlaybackEndTime = CMTimeMakeWithSeconds(endTime, NSEC_PER_SEC);
[self _setupPlaybackReachedEndObserver];
}
- (void)viewWillAppear:(BOOL)animated
@ -1330,7 +1325,11 @@
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.toolbarLandscapeSize = TGPhotoEditorToolbarSize;
previewController.beginTransitionIn = ^UIView *(CGRect *referenceFrame, UIView **parentView, bool *noTransitionView)
@ -1338,6 +1337,10 @@
*referenceFrame = transitionReferenceFrame;
*parentView = transitionParentView;
*noTransitionView = transitionNoTransitionView;
__strong TGPhotoEditorController *strongSelf = weakSelf;
if (strongSelf != nil)
[strongSelf startVideoPlayback:true];
return transitionReferenceView;
};
@ -1351,7 +1354,6 @@
strongSelf.finishedTransitionIn();
strongSelf->_switchingTab = false;
[strongSelf startVideoPlayback:true];
};
previewController.controlVideoPlayback = ^(bool play) {
__strong TGPhotoEditorController *strongSelf = weakSelf;
@ -1635,10 +1637,9 @@
[[NSUserDefaults standardUserDefaults] setObject:@(qualityController.preset) forKey:@"TG_preferredVideoPreset_v0"];
} else if ([_currentTabController isKindOfClass:[TGPhotoAvatarPreviewController class]])
{
TGPhotoAvatarPreviewController *previewController = (TGPhotoAvatarPreviewController *)_currentTabController;
videoStartValue = previewController.coverPosition;
trimStartValue = previewController.trimStartValue;
trimEndValue = previewController.trimEndValue;
videoStartValue = _scrubberView.value;
trimStartValue = _scrubberView.trimStartValue;
trimEndValue = _scrubberView.trimEndValue;
}
TGVideoEditAdjustments *adjustments = [_photoEditor exportAdjustmentsWithPaintingData:paintingData];
@ -2034,6 +2035,21 @@
if ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPad)
portraitToolbarViewBottomEdge = screenEdges.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
@ -2167,4 +2183,247 @@
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

View File

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

View File

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

View File

@ -77,7 +77,7 @@ final class AvatarGalleryItemFooterContentNode: GalleryFooterContentNode {
self.setMainButton = HighlightableButtonNode()
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.maximumNumberOfLines = 1

View File

@ -4585,6 +4585,12 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
if let strongSelf = self {
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
if let strongSelf = self {
strongSelf.searchResultsTooltipController?.dismiss()

View File

@ -128,6 +128,7 @@ private final class ChatEmptyNodeNearbyChatContent: ASDisplayNode, ChatEmptyNode
var displayName = ""
let distance = interfaceState.peerNearbyData?.distance ?? 0
if let renderedPeer = interfaceState.renderedPeer {
if let chatPeer = renderedPeer.peers[renderedPeer.peerId] {
displayName = chatPeer.compactDisplayTitle
@ -146,8 +147,18 @@ private final class ChatEmptyNodeNearbyChatContent: ASDisplayNode, ChatEmptyNode
if let item = self.stickerItem {
self.stickerNode.updateLayout(item: item, size: stickerSize, isVisible: true, synchronousLoads: true)
} 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.disposable.set((randomGreetingSticker(account: self.account)
self.disposable.set((sticker
|> deliverOnMainQueue).start(next: { [weak self] sticker in
if let strongSelf = self, let sticker = sticker {
let inputNodeInteraction = ChatMediaInputNodeInteraction(
@ -172,7 +183,7 @@ private final class ChatEmptyNodeNearbyChatContent: ASDisplayNode, ChatEmptyNode
let index = ItemCollectionItemIndex(index: 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: {})
strongSelf.stickerItem = item
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 {
self.didSetUpAnimationNode = true
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)
}
}

View File

@ -118,9 +118,10 @@ final class ChatPanelInterfaceInteraction {
let displaySendMessageOptions: (ASDisplayNode, ContextGesture) -> Void
let openScheduledMessages: () -> Void
let displaySearchResultsTooltip: (ASDisplayNode, CGRect) -> Void
let openPeersNearby: () -> Void
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.setupEditMessage = setupEditMessage
self.beginMessageSelection = beginMessageSelection
@ -188,6 +189,7 @@ final class ChatPanelInterfaceInteraction {
self.displaySlowmodeTooltip = displaySlowmodeTooltip
self.displaySendMessageOptions = displaySendMessageOptions
self.openScheduledMessages = openScheduledMessages
self.openPeersNearby = openPeersNearby
self.displaySearchResultsTooltip = displaySearchResultsTooltip
self.statuses = statuses
}

View File

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

View File

@ -186,7 +186,11 @@ private final class ChatInfoTitlePanelPeerNearbyInfoNode: ASDisplayNode {
private let labelNode: ImmediateTextNode
private let filledBackgroundNode: LinkHighlightingNode
init(openPeer: @escaping () -> Void) {
private let openPeersNearby: () -> Void
init(openPeersNearby: @escaping () -> Void) {
self.openPeersNearby = openPeersNearby
self.labelNode = ImmediateTextNode()
self.labelNode.maximumNumberOfLines = 1
self.labelNode.textAlignment = .center
@ -197,22 +201,17 @@ private final class ChatInfoTitlePanelPeerNearbyInfoNode: ASDisplayNode {
self.addSubnode(self.filledBackgroundNode)
self.addSubnode(self.labelNode)
}
override func didLoad() {
super.didLoad()
self.labelNode.highlightAttributeAction = { attributes in
for (key, _) in attributes {
if key.rawValue == "_Link" {
return key
}
}
return nil
}
self.labelNode.tapAttributeAction = { attributes, _ in
for (key, _) in attributes {
if key.rawValue == "_Link" {
openPeer()
}
}
}
let tapRecognizer = UITapGestureRecognizer(target: self, action: #selector(self.tapGesture(_:)))
self.view.addGestureRecognizer(tapRecognizer)
}
@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 {
@ -233,7 +232,7 @@ private final class ChatInfoTitlePanelPeerNearbyInfoNode: ASDisplayNode {
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]
for (_, range) in stringAndRanges.1 {
for (_, range) in stringAndRanges.1.prefix(1) {
attributedString.addAttributes(boldAttributes, range: range)
}
@ -438,8 +437,8 @@ final class ChatReportPeerTitlePanelNode: ChatTitleAccessoryPanelNode {
peerNearbyInfoNode = current
} else {
peerNearbyInfoTransition = .immediate
peerNearbyInfoNode = ChatInfoTitlePanelPeerNearbyInfoNode(openPeer: { [weak self] in
self?.interfaceInteraction?.navigateToProfile(chatPeer.id)
peerNearbyInfoNode = ChatInfoTitlePanelPeerNearbyInfoNode(openPeersNearby: { [weak self] in
self?.interfaceInteraction?.openPeersNearby()
})
self.addSubnode(peerNearbyInfoNode)
self.peerNearbyInfoNode = peerNearbyInfoNode

View File

@ -148,7 +148,7 @@ private func chatMessageGalleryControllerData(context: AccountContext, message:
} else if ext == "wav" || ext == "opus" {
return .audio(file)
} 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
navigationController?.replaceTopController(controller, animated: false, ready: ready)
}, baseNavigationController: navigationController, actionInteraction: actionInteraction)

View File

@ -37,6 +37,7 @@ import LocationUI
import Geocoding
import TextFormat
import StatisticsUI
import StickerResources
protocol PeerInfoScreenItem: class {
var id: AnyHashable { get }
@ -413,6 +414,7 @@ final class PeerInfoSelectionPanelNode: ASDisplayNode {
}, displaySlowmodeTooltip: { _, _ in
}, displaySendMessageOptions: { _, _ in
}, openScheduledMessages: {
}, openPeersNearby: {
}, displaySearchResultsTooltip: { _, _ in
}, statuses: nil)
@ -1092,6 +1094,9 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD
}
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?) {
self.controller = controller
self.context = context
@ -2011,6 +2016,27 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD
}
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 {
@ -2023,6 +2049,7 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD
self.updateAvatarDisposable.dispose()
self.selectAddMemberDisposable.dispose()
self.addMemberDisposable.dispose()
self.preloadStickerDisposable.dispose()
}
override func didLoad() {
@ -2206,7 +2233,13 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD
switch key {
case .message:
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:
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() {
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() {
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
}, displaySendMessageOptions: { _, _ in
}, openScheduledMessages: {
}, openPeersNearby: {
}, displaySearchResultsTooltip: { _, _ in
}, statuses: nil)