Merge commit '51a95f3f6ff23403490f3371b58dffac7eff87d8'

This commit is contained in:
Ali 2020-06-29 23:07:23 +04:00
commit 95b7ea333d
16 changed files with 510 additions and 238 deletions

View File

@ -5636,5 +5636,5 @@ Any member of this group will be able to see messages in the channel.";
"Settings.SetProfilePhotoOrVideo" = "Set Profile Photo or Video";
"Settings.SetNewProfilePhotoOrVideo" = "Set New Profile Photo or Video";
"Conversation.Unarchive" = "Unarhive";
"Conversation.Unarchive" = "Unarchive";
"Conversation.UnarchiveDone" = "The chat was moved to your main list.";

View File

@ -3,15 +3,15 @@
typedef NS_OPTIONS(NSUInteger, TGPhotoEditorTab) {
TGPhotoEditorNoneTab = 0,
TGPhotoEditorCropTab = 1 << 0,
TGPhotoEditorPaintTab = 1 << 1,
TGPhotoEditorEraserTab = 1 << 2,
TGPhotoEditorStickerTab = 1 << 3,
TGPhotoEditorTextTab = 1 << 4,
TGPhotoEditorToolsTab = 1 << 5,
TGPhotoEditorRotateTab = 1 << 6,
TGPhotoEditorQualityTab = 1 << 7,
TGPhotoEditorTimerTab = 1 << 8,
TGPhotoEditorMirrorTab = 1 << 9,
TGPhotoEditorRotateTab = 1 << 1,
TGPhotoEditorMirrorTab = 1 << 2,
TGPhotoEditorPaintTab = 1 << 3,
TGPhotoEditorEraserTab = 1 << 4,
TGPhotoEditorStickerTab = 1 << 5,
TGPhotoEditorTextTab = 1 << 6,
TGPhotoEditorToolsTab = 1 << 7,
TGPhotoEditorQualityTab = 1 << 8,
TGPhotoEditorTimerTab = 1 << 9,
TGPhotoEditorAspectRatioTab = 1 << 10,
TGPhotoEditorTintTab = 1 << 11,
TGPhotoEditorBlurTab = 1 << 12,

View File

@ -908,8 +908,6 @@ const NSUInteger TGAttachmentDisplayedAssetLimit = 500;
if (strongSelf.avatarCompletionBlock != nil)
strongSelf.avatarCompletionBlock(resultImage);
[strongController dismissAnimated:true];
};
controller.didFinishEditingVideo = ^(NSURL *url, id<TGMediaEditAdjustments> adjustments, UIImage *resultImage, UIImage *thumbnailImage, bool hasChanges) {
if (!hasChanges)
@ -925,8 +923,6 @@ const NSUInteger TGAttachmentDisplayedAssetLimit = 500;
if (strongSelf.avatarVideoCompletionBlock != nil)
strongSelf.avatarVideoCompletionBlock(resultImage, url, adjustments);
[strongController dismissAnimated:true];
};
controller.requestThumbnailImage = ^(id<TGMediaEditableItem> editableItem)
{

View File

@ -1012,7 +1012,9 @@ static CGPoint TGCameraControllerClampPointToScreenSize(__unused id self, __unus
TGCameraCapturedVideo *capturedVideo = [[TGCameraCapturedVideo alloc] initWithURL:outputURL];
if (strongSelf->_intent == TGCameraControllerAvatarIntent || strongSelf->_intent == TGCameraControllerSignupAvatarIntent)
{
[strongSelf presentPhotoResultControllerWithImage:capturedVideo metadata:nil completion:^{}];
[strongSelf presentPhotoResultControllerWithImage:capturedVideo metadata:nil completion:^{
[strongSelf->_interfaceView setRecordingVideo:false animated:true];
}];
} else {
[strongSelf addResultItem:capturedVideo];
if (![strongSelf maybePresentResultControllerForItem:capturedVideo completion:nil])

View File

@ -44,6 +44,8 @@
- (void)setRecipientName:(NSString *)recipientName;
- (CGPoint)scrubberPositionForPosition:(NSTimeInterval)position;
@end
@protocol TGMediaPickerGalleryVideoScrubberDelegate <NSObject>

View File

@ -1286,6 +1286,11 @@ typedef enum
}
}
- (CGPoint)scrubberPositionForPosition:(NSTimeInterval)position
{
return [self _scrubberPositionForPosition:position duration:_duration];
}
- (CGPoint)_scrubberPositionForPosition:(NSTimeInterval)position duration:(NSTimeInterval)duration
{
return [self _scrubberPositionForPosition:position duration:duration zoomedIn:_zoomedIn];

View File

@ -374,15 +374,24 @@ const CGFloat TGPhotoAvatarCropButtonsWrapperSize = 61.0f;
[self.view addSubview:snapshotView];
CGRect targetCropViewFrame = [self.view convertRect:targetFrame toView:_wrapperView];
if (!self.item.isVideo) {
_previewView.hidden = true;
}
[UIView animateWithDuration:0.3f delay:0.0f options:UIViewAnimationOptionCurveEaseInOut | UIViewAnimationOptionLayoutSubviews animations:^
{
snapshotView.frame = targetFrame;
snapshotView.alpha = 1.0f;
if (self.item.isVideo) {
_cropView.alpha = 0.0f;
} else {
snapshotView.alpha = 1.0f;
}
_cropView.frame = targetCropViewFrame;
[_cropView invalidateCropRect];
} completion:^(__unused BOOL finished)
{
_previewView.hidden = false;
if (self.finishedTransitionOut != nil)
self.finishedTransitionOut();
}];

View File

@ -7,10 +7,9 @@
@interface TGPhotoAvatarPreviewController : TGPhotoEditorTabController
- (instancetype)initWithContext:(id<LegacyComponentsContext>)context photoEditor:(PGPhotoEditor *)photoEditor previewView:(TGPhotoEditorPreviewView *)previewView scrubberView:(TGMediaPickerGalleryVideoScrubber *)scrubberView;
- (instancetype)initWithContext:(id<LegacyComponentsContext>)context photoEditor:(PGPhotoEditor *)photoEditor previewView:(TGPhotoEditorPreviewView *)previewView scrubberView:(TGMediaPickerGalleryVideoScrubber *)scrubberView dotImageView:(UIImageView *)dotImageView;
- (void)beginScrubbing;
- (void)endScrubbing:(bool (^)(void))completion;
- (void)setPlayButtonHidden:(bool)hidden animated:(bool)animated;
- (void)beginScrubbing:(bool)flash;
- (void)endScrubbing:(bool)flash completion:(bool (^)(void))completion;
@end

View File

@ -26,6 +26,9 @@ const CGFloat TGPhotoAvatarPreviewLandscapePanelSize = TGPhotoAvatarPreviewPanel
TGPhotoEditorSparseView *_wrapperView;
TGMediaPickerGalleryVideoScrubber *_scrubberView;
UIImageView *_dotImageView;
UIView *_portraitToolsWrapperView;
UIView *_landscapeToolsWrapperView;
UIView *_portraitWrapperBackgroundView;
@ -35,8 +38,6 @@ const CGFloat TGPhotoAvatarPreviewLandscapePanelSize = TGPhotoAvatarPreviewPanel
UIView *_flashView;
UIView *_portraitToolControlView;
UIView *_landscapeToolControlView;
UIImageView *_areaMaskView;
CGFloat _currentDiameter;
UILabel *_coverLabel;
}
@ -47,7 +48,7 @@ const CGFloat TGPhotoAvatarPreviewLandscapePanelSize = TGPhotoAvatarPreviewPanel
@implementation TGPhotoAvatarPreviewController
- (instancetype)initWithContext:(id<LegacyComponentsContext>)context photoEditor:(PGPhotoEditor *)photoEditor previewView:(TGPhotoEditorPreviewView *)previewView scrubberView:(TGMediaPickerGalleryVideoScrubber *)scrubberView
- (instancetype)initWithContext:(id<LegacyComponentsContext>)context photoEditor:(PGPhotoEditor *)photoEditor previewView:(TGPhotoEditorPreviewView *)previewView scrubberView:(TGMediaPickerGalleryVideoScrubber *)scrubberView dotImageView:(UIImageView *)dotImageView
{
self = [super initWithContext:context];
if (self != nil)
@ -55,6 +56,8 @@ const CGFloat TGPhotoAvatarPreviewLandscapePanelSize = TGPhotoAvatarPreviewPanel
self.photoEditor = photoEditor;
self.previewView = previewView;
_scrubberView = scrubberView;
_dotImageView = dotImageView;
}
return self;
}
@ -92,28 +95,26 @@ const CGFloat TGPhotoAvatarPreviewLandscapePanelSize = TGPhotoAvatarPreviewPanel
_videoAreaView = [[UIView alloc] init];
[self.view insertSubview:_videoAreaView belowSubview:_wrapperView];
[_portraitToolsWrapperView addSubview:_scrubberView];
_flashView = [[UIView alloc] init];
_flashView.alpha = 0.0;
_flashView.backgroundColor = [UIColor whiteColor];
_flashView.userInteractionEnabled = false;
[_videoAreaView addSubview:_flashView];
_coverLabel = [[UILabel alloc] init];
_coverLabel.alpha = 0.7f;
_coverLabel.backgroundColor = [UIColor clearColor];
_coverLabel.font = TGSystemFontOfSize(14.0f);
_coverLabel.textColor = [UIColor whiteColor];
_coverLabel.text = TGLocalized(@"PhotoEditor.SelectCoverFrame");
[_coverLabel sizeToFit];
[_portraitToolsWrapperView addSubview:_coverLabel];
[_wrapperView addSubview:_dotImageView];
}
_flashView = [[UIView alloc] init];
_flashView.alpha = 0.0;
_flashView.backgroundColor = [UIColor whiteColor];
_flashView.userInteractionEnabled = false;
[_videoAreaView addSubview:_flashView];
_areaMaskView = [[UIImageView alloc] init];
_areaMaskView.alpha = 0.0f;
[self.view insertSubview:_areaMaskView aboveSubview:_videoAreaView];
[_portraitToolsWrapperView addSubview:_scrubberView];
_coverLabel = [[UILabel alloc] init];
_coverLabel.alpha = 0.7f;
_coverLabel.backgroundColor = [UIColor clearColor];
_coverLabel.font = TGSystemFontOfSize(14.0f);
_coverLabel.textColor = [UIColor whiteColor];
_coverLabel.text = TGLocalized(@"PhotoEditor.SelectCoverFrame");
[_coverLabel sizeToFit];
[_portraitToolsWrapperView addSubview:_coverLabel];
}
- (void)viewDidAppear:(BOOL)animated
@ -232,7 +233,6 @@ const CGFloat TGPhotoAvatarPreviewLandscapePanelSize = TGPhotoAvatarPreviewPanel
_portraitToolsWrapperView.alpha = 0.0f;
_landscapeToolsWrapperView.alpha = 0.0f;
_videoAreaView.alpha = 0.0f;
_areaMaskView.alpha = 0.0f;
} completion:^(__unused BOOL finished)
{
if (completion != nil)
@ -320,7 +320,6 @@ const CGFloat TGPhotoAvatarPreviewLandscapePanelSize = TGPhotoAvatarPreviewPanel
_portraitToolsWrapperView.alpha = 0.0f;
_landscapeToolsWrapperView.alpha = 0.0f;
_videoAreaView.alpha = 0.0f;
_areaMaskView.alpha = 0.0f;
} completion:nil];
}
@ -400,12 +399,10 @@ const CGFloat TGPhotoAvatarPreviewLandscapePanelSize = TGPhotoAvatarPreviewPanel
CGSize referenceSize = [self referenceViewSize];
CGFloat screenSide = MAX(referenceSize.width, referenceSize.height) + 2 * TGPhotoAvatarPreviewPanelSize;
CGFloat screenSide = MAX(referenceSize.width, referenceSize.height);
_wrapperView.frame = CGRectMake((referenceSize.width - screenSide) / 2, (referenceSize.height - screenSide) / 2, screenSide, screenSide);
CGFloat panelSize = UIInterfaceOrientationIsPortrait(orientation) ? TGPhotoAvatarPreviewPanelSize : TGPhotoAvatarPreviewLandscapePanelSize;
// if (_portraitToolControlView != nil)
// panelSize = TGPhotoEditorPanelSize;
CGFloat panelToolbarPortraitSize = panelSize + TGPhotoEditorToolbarSize;
CGFloat panelToolbarLandscapeSize = panelSize + TGPhotoEditorToolbarSize;
@ -497,36 +494,9 @@ const CGFloat TGPhotoAvatarPreviewLandscapePanelSize = TGPhotoAvatarPreviewPanel
{
_videoAreaView.frame = _previewView.frame;
_flashView.frame = _videoAreaView.bounds;
_areaMaskView.frame = _previewView.frame;
[self updateCircleImage];
}];
}
- (void)updateCircleImage
{
CGFloat diameter = _areaMaskView.frame.size.width;
if (fabs(diameter - _currentDiameter) < DBL_EPSILON)
return;
_currentDiameter = diameter;
UIGraphicsBeginImageContextWithOptions(CGSizeMake(diameter, diameter), false, 0.0f);
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetFillColorWithColor(context, [TGPhotoEditorInterfaceAssets cropTransparentOverlayColor].CGColor);
UIBezierPath *path = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(0, 0, diameter, diameter)];
[path appendPath:[UIBezierPath bezierPathWithRect:CGRectMake(0, 0, diameter, diameter)]];
path.usesEvenOddFillRule = true;
[path fill];
UIImage *areaMaskImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
_areaMaskView.image = areaMaskImage;
}
- (void)updateLayout:(UIInterfaceOrientation)orientation
{
if ([self inFormSheet] || TGIsPad())
@ -540,7 +510,7 @@ const CGFloat TGPhotoAvatarPreviewLandscapePanelSize = TGPhotoAvatarPreviewPanel
- (TGPhotoEditorTab)availableTabs
{
return TGPhotoEditorCropTab | TGPhotoEditorPaintTab | TGPhotoEditorToolsTab;
return TGPhotoEditorRotateTab | TGPhotoEditorMirrorTab | TGPhotoEditorPaintTab | TGPhotoEditorToolsTab;
}
- (TGPhotoEditorTab)activeTab
@ -561,58 +531,74 @@ const CGFloat TGPhotoAvatarPreviewLandscapePanelSize = TGPhotoAvatarPreviewPanel
return tabs;
}
- (void)setPlayButtonHidden:(bool)hidden animated:(bool)animated
- (void)handleTabAction:(TGPhotoEditorTab)tab
{
// if (animated)
// {
// _actionButton.hidden = false;
// [UIView animateWithDuration:0.15f animations:^
// {
// _actionButton.alpha = hidden ? 0.0f : 1.0f;
// } completion:^(BOOL finished)
// {
// if (finished)
// _actionButton.hidden = hidden;
// }];
// }
// else
// {
// _actionButton.alpha = hidden ? 0.0f : 1.0f;
// _actionButton.hidden = hidden;
// }
switch (tab)
{
case TGPhotoEditorRotateTab:
{
[self rotate];
}
break;
case TGPhotoEditorMirrorTab:
{
[self mirror];
}
break;
default:
break;
}
}
- (void)beginScrubbing
{
_coverLabel.alpha = 1.0f;
[self setPlayButtonHidden:true animated:false];
[UIView animateWithDuration:0.2 animations:^{
_areaMaskView.alpha = 1.0f;
}];
#pragma mark - Cropping
- (void)rotate {
// [_cropView rotate90DegreesCCWAnimated:true];
}
- (void)endScrubbing:(bool (^)(void))completion
- (void)mirror {
// [_cropView mirror];
}
- (void)beginScrubbing:(bool)flash
{
[UIView animateWithDuration:0.12 animations:^{
_flashView.alpha = 1.0f;
} completion:^(BOOL finished) {
[UIView animateWithDuration:0.2 animations:^{
_flashView.alpha = 0.0f;
if (flash)
_coverLabel.alpha = 1.0f;
}
- (void)endScrubbing:(bool)flash completion:(bool (^)(void))completion
{
if (flash) {
[UIView animateWithDuration:0.12 animations:^{
_flashView.alpha = 1.0f;
} completion:^(BOOL finished) {
TGDispatchAfter(1.0, dispatch_get_main_queue(), ^{
if (completion()) {
[UIView animateWithDuration:0.2 animations:^{
_areaMaskView.alpha = 0.0f;
_coverLabel.alpha = 0.7f;
}];
self.controlVideoPlayback(true);
}
});
[UIView animateWithDuration:0.2 animations:^{
_flashView.alpha = 0.0f;
} completion:^(BOOL finished) {
TGDispatchAfter(1.0, dispatch_get_main_queue(), ^{
if (completion()) {
[UIView animateWithDuration:0.2 animations:^{
_coverLabel.alpha = 0.7f;
}];
self.controlVideoPlayback(true);
}
});
}];
}];
}];
} else {
TGDispatchAfter(1.32, dispatch_get_main_queue(), ^{
if (completion()) {
[UIView animateWithDuration:0.2 animations:^{
_coverLabel.alpha = 0.7f;
}];
self.controlVideoPlayback(true);
}
});
}
}
@end

View File

@ -75,10 +75,13 @@
CMTime _chaseTime;
bool _chasingTime;
bool _isPlaying;
AVPlayerItem *_playerItem;
SMetaDisposable *_playerItemDisposable;
id _playerStartedObserver;
id _playerReachedEndObserver;
bool _registeredKeypathObserver;
NSTimer *_positionTimer;
bool _scheduledVideoPlayback;
id<TGMediaEditAdjustments> _initialAdjustments;
@ -94,15 +97,22 @@
TGMenuContainerView *_menuContainerView;
UIDocumentInteractionController *_documentController;
bool _dismissed;
bool _hadProgress;
bool _progressVisible;
TGMessageImageViewOverlayView *_progressView;
SMetaDisposable *_faceDetectorDisposable;
bool _wasPlaying;
bool _initializedScrubber;
TGMediaPickerGalleryVideoScrubber *_scrubberView;
PGPhotoEditorView *_dotVideoView;
NSTimeInterval _dotPosition;
UIImageView *_dotMarkerView;
UIImageView *_dotImageView;
UIView *_dotImageSnapshotView;
bool _requestingThumbnails;
SMetaDisposable *_thumbnailsDisposable;
@ -166,7 +176,6 @@
- (void)dealloc
{
[self stopVideoPlayback:true];
[_actionHandle reset];
[_faceDetectorDisposable dispose];
[_thumbnailsDisposable dispose];
@ -308,10 +317,22 @@
[_photoEditor setPreviewOutput:_previewView];
[self updatePreviewView];
_dotMarkerView = [[UIImageView alloc] initWithImage:TGCircleImage(7.0, [TGPhotoEditorInterfaceAssets accentColor])];
[_scrubberView addSubview:_dotMarkerView];
_dotMarkerView.center = CGPointMake(30.0, -20.0);
_dotImageView = [[UIImageView alloc] initWithFrame:CGRectMake(0.0, 0.0, 160.0, 160.0)];
_dotImageView.userInteractionEnabled = true;
UITapGestureRecognizer *dotTapRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleDotTap)];
[_dotImageView addGestureRecognizer:dotTapRecognizer];
if ([self presentedForAvatarCreation] && _item.isVideo) {
_scrubberView = [[TGMediaPickerGalleryVideoScrubber alloc] initWithFrame:CGRectMake(0.0f, 0.0, _portraitToolbarView.frame.size.width, 68.0f)];
_scrubberView.dataSource = self;
_scrubberView.delegate = self;
_scrubberView.clipsToBounds = false;
}
[self detectFaces];
@ -319,6 +340,28 @@
[self presentEditorTab:_currentTab];
}
- (void)handleDotTap {
TGPhotoAvatarPreviewController *previewController = (TGPhotoAvatarPreviewController *)_currentTabController;
if (![previewController isKindOfClass:[TGPhotoAvatarPreviewController class]])
return;
[self stopVideoPlayback:false];
[self seekVideo:_dotPosition];
[previewController beginScrubbing:false];
[_scrubberView setValue:_dotPosition resetPosition:true];
__weak TGPhotoEditorController *weakSelf = self;
[previewController endScrubbing:false completion:^bool{
__strong TGPhotoEditorController *strongSelf = weakSelf;
if (strongSelf == nil)
return false;
return !strongSelf->_scrubberView.isScrubbing;
}];
}
- (void)setToolbarHidden:(bool)hidden animated:(bool)animated
{
if (self.requestToolbarsHidden == nil)
@ -383,7 +426,6 @@
if (self.item.isVideo) {
_scrubberView.allowsTrimming = self.item.originalDuration >= TGVideoEditMinimumTrimmableDuration;
_scrubberView.hasDotPicker = true;
_scrubberView.disableZoom = true;
_scrubberView.disableTimeDisplay = true;
_scrubberView.trimStartValue = 0.0;
@ -423,36 +465,46 @@
}
}
__weak TGPhotoEditorController *weakSelf = self;
PGPhotoEditor *photoEditor = _photoEditor;
[signal startWithNext:^(id next)
{
__strong TGPhotoEditorController *strongSelf = weakSelf;
if (strongSelf == nil)
return;
if (strongSelf->_dismissed)
return;
CGFloat progress = 0.0;
bool progressVisible = false;
bool doneEnabled = true;
if ([next isKindOfClass:[UIImage class]]) {
[_photoEditor setImage:(UIImage *)next forCropRect:_photoEditor.cropRect cropRotation:_photoEditor.cropRotation cropOrientation:_photoEditor.cropOrientation cropMirrored:_photoEditor.cropMirrored fullSize:false];
[photoEditor setImage:(UIImage *)next forCropRect:photoEditor.cropRect cropRotation:photoEditor.cropRotation cropOrientation:photoEditor.cropOrientation cropMirrored:photoEditor.cropMirrored fullSize:false];
if (!((UIImage *)next).degraded) {
progress = 1.0f;
}
} else if ([next isKindOfClass:[AVAsset class]]) {
_playerItem = [AVPlayerItem playerItemWithAsset:(AVAsset *)next];
_player = [AVPlayer playerWithPlayerItem:_playerItem];
_player.actionAtItemEnd = AVPlayerActionAtItemEndNone;
_player.muted = true;
strongSelf->_playerItem = [AVPlayerItem playerItemWithAsset:(AVAsset *)next];
strongSelf->_player = [AVPlayer playerWithPlayerItem:strongSelf->_playerItem];
strongSelf->_player.actionAtItemEnd = AVPlayerActionAtItemEndNone;
strongSelf->_player.muted = true;
[_photoEditor setPlayerItem:_playerItem forCropRect:_photoEditor.cropRect cropRotation:0.0 cropOrientation:_photoEditor.cropOrientation cropMirrored:_photoEditor.cropMirrored];
[photoEditor setPlayerItem:strongSelf->_playerItem forCropRect:photoEditor.cropRect cropRotation:0.0 cropOrientation:photoEditor.cropOrientation cropMirrored:photoEditor.cropMirrored];
TGDispatchOnMainThread(^
{
if ([_currentTabController isKindOfClass:[TGPhotoAvatarCropController class]])
[(TGPhotoAvatarCropController *)_currentTabController setPlayer:_player];
if ([strongSelf->_currentTabController isKindOfClass:[TGPhotoAvatarCropController class]])
[(TGPhotoAvatarCropController *)strongSelf->_currentTabController setPlayer:strongSelf->_player];
[_previewView performTransitionInWithCompletion:^
[strongSelf->_previewView performTransitionInWithCompletion:^
{
}];
if (_scheduledVideoPlayback) {
_scheduledVideoPlayback = false;
[self startVideoPlayback:true];
if (strongSelf->_scheduledVideoPlayback) {
strongSelf->_scheduledVideoPlayback = false;
[strongSelf startVideoPlayback:true];
}
});
progress = 1.0f;
@ -464,14 +516,16 @@
}
TGDispatchOnMainThread(^{
if (progressVisible)
_hadProgress = true;
[self setProgressVisible:progressVisible value:progress animated:true];
[self updateDoneButtonEnabled:doneEnabled animated:true];
if (strongSelf->_dismissed)
return;
if (_hadProgress) {
[_scrubberView reloadThumbnails];
[self updateDotImage];
if (progressVisible)
strongSelf->_hadProgress = true;
[strongSelf setProgressVisible:progressVisible value:progress animated:true];
[strongSelf updateDoneButtonEnabled:doneEnabled animated:true];
if (strongSelf->_hadProgress) {
[strongSelf->_scrubberView reloadThumbnails];
}
});
@ -479,25 +533,29 @@
return;
}
if (_ignoreDefaultPreviewViewTransitionIn)
if (strongSelf->_ignoreDefaultPreviewViewTransitionIn)
{
TGDispatchOnMainThread(^
{
if ([_currentTabController isKindOfClass:[TGPhotoQualityController class]])
[_previewView setSnapshotImageOnTransition:next];
if (strongSelf->_dismissed)
return;
if ([strongSelf->_currentTabController isKindOfClass:[TGPhotoQualityController class]])
[strongSelf->_previewView setSnapshotImageOnTransition:next];
else
[_previewView setSnapshotImage:next];
[strongSelf->_previewView setSnapshotImage:next];
});
}
else
{
[_photoEditor processAnimated:false completion:^
[photoEditor processAnimated:false completion:^
{
TGDispatchOnMainThread(^
{
[_previewView performTransitionInWithCompletion:^
if (strongSelf->_dismissed)
return;
[strongSelf->_previewView performTransitionInWithCompletion:^
{
[_previewView setSnapshotImage:next];
[strongSelf->_previewView setSnapshotImage:next];
}];
});
}];
@ -544,8 +602,9 @@
_playerReachedEndObserver = [_player addBoundaryTimeObserverForTimes:@[[NSValue valueWithCMTime:endTime]] queue:NULL usingBlock:^
{
__strong TGPhotoEditorController *strongSelf = weakSelf;
if (strongSelf != nil) {
if (strongSelf != nil && !strongSelf->_dismissed) {
[strongSelf->_player seekToTime:startTime];
[strongSelf->_scrubberView setValue:strongSelf->_photoEditor.trimEndValue resetPosition:true];
}
}];
}
@ -565,9 +624,19 @@
[_player.currentItem seekToTime:targetTime toleranceBefore:kCMTimeZero toleranceAfter:kCMTimeZero];
[self _setupPlaybackStartedObserver];
if (!_registeredKeypathObserver) {
[_player addObserver:self forKeyPath:@"rate" options:NSKeyValueObservingOptionNew context:nil];
_registeredKeypathObserver = true;
}
}
_isPlaying = true;
[_player play];
[_positionTimer invalidate];
_positionTimer = [TGTimerTarget scheduledMainThreadTimerWithTarget:self action:@selector(positionTimerEvent) interval:0.25 repeat:true];
[self positionTimerEvent];
}
- (void)stopVideoPlayback:(bool)reset {
@ -576,8 +645,35 @@
[_player removeTimeObserver:_playerStartedObserver];
if (_playerReachedEndObserver != nil)
[_player removeTimeObserver:_playerReachedEndObserver];
if (_registeredKeypathObserver) {
[_player removeObserver:self forKeyPath:@"rate" context:nil];
_registeredKeypathObserver = false;
}
}
_isPlaying = 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]]) {
[_scrubberView setIsPlaying:_player.rate > FLT_EPSILON];
}
}
}
- (void)positionTimerEvent
{
if ([_currentTabController isKindOfClass:[TGPhotoAvatarPreviewController class]]) {
[_scrubberView setValue:CMTimeGetSeconds(_player.currentItem.currentTime) resetPosition:false];
}
}
- (void)seekVideo:(NSTimeInterval)position {
@ -730,10 +826,11 @@
- (void)createEditedImageWithEditorValues:(id<TGMediaEditAdjustments>)editorValues createThumbnail:(bool)createThumbnail saveOnly:(bool)saveOnly completion:(void (^)(UIImage *))completion
{
bool avatar = [self presentedForAvatarCreation];
if (!saveOnly)
{
bool forAvatar = [self presentedForAvatarCreation];
if (!forAvatar && [editorValues isDefaultValuesForAvatar:false])
if (!avatar && [editorValues isDefaultValuesForAvatar:false])
{
if (self.willFinishEditing != nil)
self.willFinishEditing(nil, [_currentTabController currentResultRepresentation], true);
@ -751,7 +848,7 @@
if (!saveOnly && self.willFinishEditing != nil)
self.willFinishEditing(editorValues, [_currentTabController currentResultRepresentation], true);
if (!saveOnly && completion != nil)
if (!saveOnly && !avatar && completion != nil)
completion(nil);
UIImage *fullSizeImage = self.fullSizeImage;
@ -832,19 +929,22 @@
}
else
{
void (^didFinishRenderingFullSizeImage)(UIImage *) = self.didFinishRenderingFullSizeImage;
void (^didFinishEditing)(id<TGMediaEditAdjustments>, UIImage *, UIImage *, bool ) = self.didFinishEditing;
[[[[renderedImageSignal map:^id(UIImage *image)
{
if (!hasImageAdjustments)
{
if (hasPainting && !hasAnimation && self.didFinishRenderingFullSizeImage != nil)
self.didFinishRenderingFullSizeImage(image);
if (hasPainting && !hasAnimation && didFinishRenderingFullSizeImage != nil)
didFinishRenderingFullSizeImage(image);
return image;
}
else
{
if (!saveOnly && !hasAnimation && self.didFinishRenderingFullSizeImage != nil)
self.didFinishRenderingFullSizeImage(image);
if (!saveOnly && !hasAnimation && didFinishRenderingFullSizeImage != nil)
didFinishRenderingFullSizeImage(image);
return TGPhotoEditorFitImage(image, TGPhotoEditorResultImageMaxSize);
}
@ -881,8 +981,11 @@
UIImage *image = result[@"image"];
UIImage *thumbnailImage = result[@"thumbnail"];
if (!saveOnly && self.didFinishEditing != nil)
self.didFinishEditing(editorValues, image, thumbnailImage, true);
if (avatar && completion != nil)
completion(image);
if (!saveOnly && didFinishEditing != nil)
didFinishEditing(editorValues, image, thumbnailImage, true);
} error:^(__unused id error)
{
TGLegacyLog(@"renderedImageSignal error");
@ -923,6 +1026,11 @@
- (void)transitionOutSaving:(bool)saving completion:(void (^)(void))completion
{
_dismissed = true;
if (!saving) {
[self stopVideoPlayback:true];
}
[UIView animateWithDuration:0.3f animations:^
{
_portraitToolbarView.alpha = 0.0f;
@ -932,7 +1040,7 @@
_currentTabController.beginTransitionOut = self.beginTransitionOut;
[self setToolbarHidden:false animated:true];
if (self.beginCustomTransitionOut != nil)
if (self.beginCustomTransitionOut != nil && !saving)
{
id rep = [_currentTabController currentResultRepresentation];
if ([rep isKindOfClass:[UIImage class]])
@ -1373,10 +1481,10 @@
{
if ([_currentTabController isKindOfClass:[TGPhotoToolsController class]]) {
[_scrubberView reloadDataAndReset:false];
[self updateDotImage];
[self updateDotImage:false];
}
TGPhotoAvatarPreviewController *previewController = [[TGPhotoAvatarPreviewController alloc] initWithContext:_context photoEditor:_photoEditor previewView:_previewView scrubberView:_scrubberView];
TGPhotoAvatarPreviewController *previewController = [[TGPhotoAvatarPreviewController alloc] initWithContext:_context photoEditor:_photoEditor previewView:_previewView scrubberView:_scrubberView dotImageView:_dotImageView];
previewController.item = _item;
previewController.toolbarLandscapeSize = TGPhotoEditorToolbarSize;
previewController.beginTransitionIn = ^UIView *(CGRect *referenceFrame, UIView **parentView, bool *noTransitionView)
@ -1386,8 +1494,16 @@
*noTransitionView = transitionNoTransitionView;
__strong TGPhotoEditorController *strongSelf = weakSelf;
if (strongSelf != nil)
[strongSelf startVideoPlayback:true];
if (strongSelf != nil) {
if ([currentController isKindOfClass:[TGPhotoAvatarCropController class]]) {
dispatch_async(dispatch_get_main_queue(), ^{
[strongSelf stopVideoPlayback:false];
[strongSelf seekVideo:0];
});
} else {
[strongSelf startVideoPlayback:true];
}
}
return transitionReferenceView;
};
@ -1400,6 +1516,10 @@
if (isInitialAppearance && strongSelf.finishedTransitionIn != nil)
strongSelf.finishedTransitionIn();
if ([currentController isKindOfClass:[TGPhotoAvatarCropController class]]) {
[strongSelf startVideoPlayback:false];
}
strongSelf->_switchingTab = false;
};
previewController.controlVideoPlayback = ^(bool play) {
@ -1522,8 +1642,13 @@
- (void)dismissAnimated:(bool)animated
{
_dismissed = true;
self.view.userInteractionEnabled = false;
if (self.navigationController != nil)
animated = false;
if (animated)
{
const CGFloat velocity = 2000.0f;
@ -1535,12 +1660,20 @@
self.view.frame = targetFrame;
} completion:^(__unused BOOL finished)
{
[self dismiss];
if (self.navigationController != nil) {
[self.navigationController popViewControllerAnimated:false];
} else {
[self dismiss];
}
}];
}
else
{
[self dismiss];
if (self.navigationController != nil) {
[self.navigationController popViewControllerAnimated:false];
} else {
[self dismiss];
}
}
}
@ -1684,11 +1817,13 @@
[[NSUserDefaults standardUserDefaults] setObject:@(qualityController.preset) forKey:@"TG_preferredVideoPreset_v0"];
} else if ([_currentTabController isKindOfClass:[TGPhotoAvatarPreviewController class]])
{
videoStartValue = _scrubberView.value;
videoStartValue = _dotPosition;
trimStartValue = _scrubberView.trimStartValue;
trimEndValue = _scrubberView.trimEndValue;
}
[self stopVideoPlayback:true];
TGVideoEditAdjustments *adjustments = [_photoEditor exportAdjustmentsWithPaintingData:paintingData];
if ([self presentedForAvatarCreation] && _item.isVideo) {
[[SQueue concurrentDefaultQueue] dispatch:^
@ -1745,10 +1880,7 @@
if (self.didFinishEditingVideo != nil)
self.didFinishEditingVideo(asset.URL, [adjustments editAdjustmentsWithPreset:TGMediaVideoConversionPresetProfile videoStartValue:videoStartValue trimStartValue:trimStartValue trimEndValue:trimEndValue], fullImage, nil, true);
[self transitionOutSaving:true completion:^
{
[self dismiss];
}];
[self dismissAnimated:true];
});
}];
}];
@ -1766,9 +1898,10 @@
[NSObject cancelPreviousPerformRequestsWithTarget:progressWindow selector:@selector(showAnimated) object:nil];
[progressWindow dismiss:true];
if (forAvatar)
if (forAvatar) {
[self dismissAnimated:true];
return;
}
[self transitionOutSaving:true completion:^
{
[self dismiss];
@ -1856,10 +1989,14 @@
if (self.didFinishEditing != nil)
self.didFinishEditing(hasChanges ? adjustments : nil, nil, nil, hasChanges);
[self transitionOutSaving:saving completion:^
{
[self dismiss];
}];
if ([self presentedForAvatarCreation]) {
[self dismissAnimated:true];
} else {
[self transitionOutSaving:saving completion:^
{
[self dismiss];
}];
}
}
}
@ -2040,6 +2177,12 @@
return orientation;
}
- (UIEdgeInsets)screenEdges {
CGSize referenceSize = [self referenceViewSize];
CGFloat screenSide = MAX(referenceSize.width, referenceSize.height);
return UIEdgeInsetsMake((screenSide - referenceSize.height) / 2, (screenSide - referenceSize.width) / 2, (screenSide + referenceSize.height) / 2, (screenSide + referenceSize.width) / 2);
}
- (void)updateLayout:(UIInterfaceOrientation)orientation
{
orientation = [self effectiveOrientation:orientation];
@ -2098,8 +2241,6 @@
_initializedScrubber = true;
[_scrubberView reloadData];
[_scrubberView resetToStart];
[self updateDotImage];
} else {
[_scrubberView reloadThumbnails];
}
@ -2240,6 +2381,27 @@
return avatarTabs;
}
- (void)setPlayButtonHidden:(bool)hidden animated:(bool)animated
{
// if (animated)
// {
// _actionButton.hidden = false;
// [UIView animateWithDuration:0.15f animations:^
// {
// _actionButton.alpha = hidden ? 0.0f : 1.0f;
// } completion:^(BOOL finished)
// {
// if (finished)
// _actionButton.hidden = hidden;
// }];
// }
// else
// {
// _actionButton.alpha = hidden ? 0.0f : 1.0f;
// _actionButton.hidden = hidden;
// }
}
#pragma mark - Video Scrubber Data Source & Delegate
#pragma mark Scrubbing
@ -2264,24 +2426,17 @@
- (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;
}
[self setPlayButtonHidden:true animated:false];
TGPhotoAvatarPreviewController *previewController = (TGPhotoAvatarPreviewController *)_currentTabController;
if (![previewController isKindOfClass:[TGPhotoAvatarPreviewController class]])
return;
[previewController beginScrubbing];
[previewController beginScrubbing:true];
}
- (void)updateDotImage {
- (void)updateDotImage:(bool)animated {
AVPlayer *player = _player;
if (player == nil) {
return;
@ -2290,9 +2445,10 @@
[[SQueue concurrentDefaultQueue] dispatch:^{
AVAssetImageGenerator *generator = [[AVAssetImageGenerator alloc] initWithAsset:player.currentItem.asset];
generator.appliesPreferredTrackTransform = true;
generator.maximumSize = CGSizeMake(128.0f, 128.0f);
generator.maximumSize = CGSizeMake(160.0f, 160.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);
@ -2304,21 +2460,102 @@
image = editor.currentResultImage;
}
UIGraphicsBeginImageContextWithOptions(CGSizeMake(160.0, 160.0), false, 1.0);
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextAddEllipseInRect(context, CGRectMake(0.0, 0.0, 160.0, 160.0));
CGContextClip(context);
CGSize filledSize = TGScaleToFill(image.size, CGSizeMake(160, 160));
[image drawInRect:CGRectMake((160.0 - filledSize.width) / 2.0, (160.0 - filledSize.height) / 2.0, filledSize.width, filledSize.height)];
UIImage *finalImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
TGDispatchOnMainThread(^{
[_scrubberView setDotImage:image];
_dotVideoView.hidden = true;
if (animated) {
UIView *snapshotView = nil;
UIView *dotSnapshotView = nil;
if (_dotImageView.image != nil) {
dotSnapshotView = [_dotMarkerView snapshotViewAfterScreenUpdates:false];
dotSnapshotView.frame = _dotMarkerView.frame;
[_dotMarkerView.superview addSubview:dotSnapshotView];
snapshotView = [_dotImageView snapshotViewAfterScreenUpdates:false];
snapshotView.frame = [_dotImageView.superview convertRect:_dotImageView.frame toView:_dotMarkerView.superview];
[_dotMarkerView.superview addSubview:snapshotView];
}
if (snapshotView != nil) {
[UIView animateWithDuration:0.15 animations:^{
snapshotView.center = _dotMarkerView.center;
snapshotView.transform = CGAffineTransformMakeScale(0.05, 0.05);
snapshotView.alpha = 0.0f;
dotSnapshotView.transform = CGAffineTransformMakeScale(0.3, 0.3);
dotSnapshotView.alpha = 0.0f;
} completion:^(BOOL finished) {
[snapshotView removeFromSuperview];
[dotSnapshotView removeFromSuperview];
}];
}
_dotImageView.image = finalImage;
[_scrubberView addSubview:_dotMarkerView];
_dotMarkerView.center = CGPointMake([_scrubberView scrubberPositionForPosition:_dotPosition].x + 7.0, 9.5);
_dotMarkerView.transform = CGAffineTransformMakeScale(0.3, 0.3);
_dotMarkerView.alpha = 0.0;
[UIView animateWithDuration:0.3 animations:^{
_dotMarkerView.transform = CGAffineTransformIdentity;
_dotMarkerView.alpha = 1.0;
}];
UIEdgeInsets screenEdges = [self screenEdges];
CGSize referenceSize = [self referenceViewSize];
CGRect containerFrame = [TGPhotoEditorTabController photoContainerFrameForParentViewFrame:CGRectMake(0, 0, referenceSize.width, referenceSize.height) toolbarLandscapeSize:self.toolbarLandscapeSize orientation:self.effectiveOrientation panelSize:0.0 hasOnScreenNavigation:self.hasOnScreenNavigation];
containerFrame.origin.x += screenEdges.left;
containerFrame.origin.y += screenEdges.top;
CGFloat scale = containerFrame.size.width / 160.0;
_dotImageView.center = CGPointMake(CGRectGetMidX(containerFrame), CGRectGetMidY(containerFrame));
_dotImageView.transform = CGAffineTransformMakeScale(scale, scale);
CGPoint targetCenter = [_dotMarkerView.superview convertPoint:_dotMarkerView.center toView:_wrapperView];
targetCenter.y -= 27.0;
[UIView animateWithDuration:0.4 delay:0.0 usingSpringWithDamping:1.1 initialSpringVelocity:0.1 options:kNilOptions animations:^{
_dotImageView.center = targetCenter;
_dotImageView.transform = CGAffineTransformMakeScale(0.225, 0.225);
} completion:^(BOOL finished) {
}];
} else {
if (_dotImageView.image != nil) {
[_scrubberView addSubview:_dotMarkerView];
UIView *snapshotView;
if (_dotImageView.image != nil) {
_dotImageSnapshotView = [_dotImageView snapshotViewAfterScreenUpdates:false];
snapshotView.frame = _dotImageView.bounds;
[_dotImageView addSubview:snapshotView];
}
_dotImageView.image = finalImage;
}
}
});
}];
}
- (void)videoScrubberDidEndScrubbing:(TGMediaPickerGalleryVideoScrubber *)__unused videoScrubber
- (void)videoScrubberDidEndScrubbing:(TGMediaPickerGalleryVideoScrubber *)videoScrubber
{
__weak TGPhotoEditorController *weakSelf = self;
TGPhotoAvatarPreviewController *previewController = (TGPhotoAvatarPreviewController *)_currentTabController;
if (![previewController isKindOfClass:[TGPhotoAvatarPreviewController class]])
return;
[previewController endScrubbing:^bool{
_dotPosition = videoScrubber.value;
[previewController endScrubbing:true completion:^bool{
__strong TGPhotoEditorController *strongSelf = weakSelf;
if (strongSelf == nil)
return false;
@ -2327,7 +2564,7 @@
}];
dispatch_async(dispatch_get_main_queue(), ^{
[self updateDotImage];
[self updateDotImage:true];
});
}
@ -2352,11 +2589,7 @@
{
[self stopVideoPlayback:false];
TGPhotoAvatarPreviewController *previewController = (TGPhotoAvatarPreviewController *)_currentTabController;
if (![previewController isKindOfClass:[TGPhotoAvatarPreviewController class]])
return;
[previewController setPlayButtonHidden:true animated:false];
[self setPlayButtonHidden:true animated:false];
}
- (void)videoScrubberDidEndEditing:(TGMediaPickerGalleryVideoScrubber *)videoScrubber
@ -2366,11 +2599,7 @@
[self seekVideo:videoScrubber.trimStartValue];
[self stopVideoPlayback:false];
TGPhotoAvatarPreviewController *previewController = (TGPhotoAvatarPreviewController *)_currentTabController;
if (![previewController isKindOfClass:[TGPhotoAvatarPreviewController class]])
return;
[previewController setPlayButtonHidden:true animated:false];
[self setPlayButtonHidden:true animated:false];
}
- (void)videoScrubber:(TGMediaPickerGalleryVideoScrubber *)__unused videoScrubber editingStartValueDidChange:(NSTimeInterval)startValue
@ -2454,6 +2683,15 @@
if (index < timestamps.count)
[strongSelf->_scrubberView setThumbnailImage:image forTimestamp:[timestamps[index] doubleValue] isSummaryThubmnail:isSummaryThumbnails];
}];
if (strongSelf->_dotImageSnapshotView != nil) {
[UIView animateWithDuration:0.2 animations:^{
strongSelf->_dotImageSnapshotView.alpha = 0.0f;
} completion:^(BOOL finished) {
[strongSelf->_dotImageSnapshotView removeFromSuperview];
strongSelf->_dotImageSnapshotView = nil;
}];
}
} completed:^
{
__strong TGPhotoEditorController *strongSelf = weakSelf;

View File

@ -113,8 +113,8 @@ public func fetchedAvatarGalleryEntries(account: Account, peer: Peer) -> Signal<
var index: Int32 = 0
for photo in photos {
let indexData = GalleryItemIndexData(position: index, totalCount: Int32(photos.count))
if result.isEmpty, let first = initialEntries.first, photo.image.videoRepresentations.isEmpty {
result.append(.image(photo.image.imageId, photo.image.reference, first.representations, first.videoRepresentations, peer, photo.date, indexData, photo.messageId, photo.image.immediateThumbnailData))
if result.isEmpty, let first = initialEntries.first {
result.append(.image(photo.image.imageId, photo.image.reference, first.representations, photo.image.videoRepresentations, peer, photo.date, indexData, photo.messageId, photo.image.immediateThumbnailData))
} else {
result.append(.image(photo.image.imageId, photo.image.reference, photo.image.representations.map({ ImageRepresentationWithReference(representation: $0, reference: MediaResourceReference.standalone(resource: $0.resource)) }), photo.image.videoRepresentations, peer, photo.date, indexData, photo.messageId, photo.image.immediateThumbnailData))
}
@ -140,8 +140,8 @@ public func fetchedAvatarGalleryEntries(account: Account, peer: Peer, firstEntry
var index: Int32 = 0
for photo in photos {
let indexData = GalleryItemIndexData(position: index, totalCount: Int32(photos.count))
if result.isEmpty, let first = initialEntries.first, photo.image.videoRepresentations.isEmpty {
result.append(.image(photo.image.imageId, photo.image.reference, first.representations, first.videoRepresentations, peer, photo.date, indexData, photo.messageId, photo.image.immediateThumbnailData))
if result.isEmpty, let first = initialEntries.first {
result.append(.image(photo.image.imageId, photo.image.reference, first.representations, photo.image.videoRepresentations, peer, photo.date, indexData, photo.messageId, photo.image.immediateThumbnailData))
} else {
result.append(.image(photo.image.imageId, photo.image.reference, photo.image.representations.map({ ImageRepresentationWithReference(representation: $0, reference: MediaResourceReference.standalone(resource: $0.resource)) }), photo.image.videoRepresentations, peer, photo.date, indexData, photo.messageId, photo.image.immediateThumbnailData))
}

View File

@ -1415,7 +1415,7 @@ public func settingsController(context: AccountContext, accountManager: AccountM
if let profileImage = peer?.smallProfileImage {
state.updatingAvatar = .image(profileImage, false)
} else {
state.updatingAvatar = .none
state.updatingAvatar = ItemListAvatarAndNameInfoItemUpdatingAvatar.none
}
return state
}

View File

@ -1121,7 +1121,7 @@ final class ChatMessageInteractiveMediaNode: ASDisplayNode, GalleryItemTransitio
if file.isAnimated {
badgeContent = .mediaDownload(backgroundColor: messageTheme.mediaDateAndStatusFillColor, foregroundColor: messageTheme.mediaDateAndStatusTextColor, duration: "\(gifTitle)", size: nil, muted: false, active: false)
} else {
badgeContent = .mediaDownload(backgroundColor: messageTheme.mediaDateAndStatusFillColor, foregroundColor: messageTheme.mediaDateAndStatusTextColor, duration: strings.Conversation_Processing, size: nil, muted: false, active: active)
badgeContent = .mediaDownload(backgroundColor: messageTheme.mediaDateAndStatusFillColor, foregroundColor: messageTheme.mediaDateAndStatusTextColor, duration: strings.Conversation_Processing, size: nil, muted: false, active: false)
}
}
if file.isAnimated && isMediaStreamable(message: message, media: file) {

View File

@ -236,7 +236,7 @@ final class PeerInfoAvatarListItemNode: ASDisplayNode {
if let video = videoRepresentations.last, let id = id {
let mediaManager = self.context.sharedContext.mediaManager
let videoFileReference = FileMediaReference.standalone(media: TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: 0), partialReference: nil, resource: video.resource, previewRepresentations: representations.map { $0.representation }, videoThumbnails: [], immediateThumbnailData: immediateThumbnailData, mimeType: "video/mp4", size: nil, attributes: [.Animated, .Video(duration: 0, size: video.dimensions, flags: [])]))
let videoContent = NativeVideoContent(id: .profileVideo(id), fileReference: videoFileReference, streamVideo: isMediaStreamable(resource: video.resource) ? .conservative : .none, loopVideo: true, enableSound: false, fetchAutomatically: true, onlyFullSizeThumbnail: true, continuePlayingWithoutSoundOnLostAudioSession: false, placeholderColor: .clear)
let videoContent = NativeVideoContent(id: .profileVideo(id), fileReference: videoFileReference, streamVideo: isMediaStreamable(resource: video.resource) ? .conservative : .none, loopVideo: true, enableSound: false, fetchAutomatically: true, onlyFullSizeThumbnail: true, autoFetchFullSizeThumbnail: true, continuePlayingWithoutSoundOnLostAudioSession: false, placeholderColor: .clear)
let videoNode = UniversalVideoNode(postbox: self.context.account.postbox, audioSession: mediaManager.audioSession, manager: mediaManager.universalVideoManager, decoration: GalleryVideoDecoration(), content: videoContent, priority: .embedded)
videoNode.isUserInteractionEnabled = false
videoNode.ownsContentNodeUpdated = { [weak self] owns in
@ -626,7 +626,7 @@ final class PeerInfoAvatarListContainerNode: ASDisplayNode {
strongSelf.items = items
strongSelf.itemsUpdated?(items)
if let size = strongSelf.validLayout {
strongSelf.updateItems(size: size, update: true, transition: .immediate, stripTransition: .immediate, synchronous: true)
strongSelf.updateItems(size: size, update: true, transition: .immediate, stripTransition: .immediate, synchronous: synchronous)
}
if items.isEmpty {
if !strongSelf.didSetReady {
@ -762,7 +762,7 @@ final class PeerInfoAvatarTransformContainerNode: ASDisplayNode {
let context: AccountContext
let avatarNode: AvatarNode
private var videoNode: UniversalVideoNode?
fileprivate var videoNode: UniversalVideoNode?
private var videoContent: NativeVideoContent?
var tapped: (() -> Void)?
@ -788,6 +788,21 @@ final class PeerInfoAvatarTransformContainerNode: ASDisplayNode {
}
}
func reattachVideoNode() {
if let videoNode = self.videoNode {
let maskPath = UIBezierPath(ovalIn: CGRect(origin: CGPoint(), size: self.avatarNode.frame.size))
let shape = CAShapeLayer()
shape.path = maskPath.cgPath
videoNode.layer.mask = shape
videoNode.transform = CATransform3DIdentity
videoNode.updateLayout(size: self.avatarNode.frame.size, transition: .immediate)
videoNode.frame = self.avatarNode.frame
self.addSubnode(videoNode)
}
}
func update(peer: Peer?, item: PeerInfoAvatarListItem?, theme: PresentationTheme, avatarSize: CGFloat, isExpanded: Bool) {
if let peer = peer {
var overrideImage: AvatarNodeImageOverride?
@ -803,7 +818,7 @@ final class PeerInfoAvatarTransformContainerNode: ASDisplayNode {
if let item = item, case let .image(reference, representations, videoRepresentations, immediateThumbnailData) = item, let video = videoRepresentations.last, case let .cloud(imageId, _, _) = reference {
let id = imageId
let videoFileReference = FileMediaReference.standalone(media: TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: 0), partialReference: nil, resource: video.resource, previewRepresentations: representations.map { $0.representation }, videoThumbnails: [], immediateThumbnailData: immediateThumbnailData, mimeType: "video/mp4", size: nil, attributes: [.Animated, .Video(duration: 0, size: video.dimensions, flags: [])]))
let videoContent = NativeVideoContent(id: .profileVideo(id), fileReference: videoFileReference, streamVideo: isMediaStreamable(resource: video.resource) ? .conservative : .none, loopVideo: true, enableSound: false, fetchAutomatically: true, onlyFullSizeThumbnail: false, continuePlayingWithoutSoundOnLostAudioSession: false, placeholderColor: .clear)
let videoContent = NativeVideoContent(id: .profileVideo(id), fileReference: videoFileReference, streamVideo: isMediaStreamable(resource: video.resource) ? .conservative : .none, loopVideo: true, enableSound: false, fetchAutomatically: true, onlyFullSizeThumbnail: false, autoFetchFullSizeThumbnail: true, continuePlayingWithoutSoundOnLostAudioSession: false, placeholderColor: .clear)
if videoContent.id != self.videoContent?.id {
let mediaManager = self.context.sharedContext.mediaManager
let videoNode = UniversalVideoNode(postbox: self.context.account.postbox, audioSession: mediaManager.audioSession, manager: mediaManager.universalVideoManager, decoration: GalleryVideoDecoration(), content: videoContent, priority: .embedded)
@ -835,9 +850,19 @@ final class PeerInfoAvatarTransformContainerNode: ASDisplayNode {
videoNode.frame = self.avatarNode.frame
if isExpanded == videoNode.canAttachContent {
videoNode.canAttachContent = !isExpanded
if videoNode.canAttachContent {
videoNode.play()
let update = {
videoNode.canAttachContent = !isExpanded
if videoNode.canAttachContent {
videoNode.seek(0.0)
videoNode.play()
}
}
if isExpanded {
DispatchQueue.main.async {
update()
}
} else {
update()
}
}
}
@ -1011,20 +1036,28 @@ final class PeerInfoAvatarListNode: ASDisplayNode {
}
func animateAvatarCollapse(transition: ContainedViewLayoutTransition) {
if let currentItemNode = self.listContainerNode.currentItemNode, let unroundedImage = self.avatarContainerNode.avatarNode.unroundedImage, case .animated = transition {
let avatarCopyView = UIImageView()
avatarCopyView.image = unroundedImage
avatarCopyView.frame = self.avatarContainerNode.avatarNode.frame
avatarCopyView.center = currentItemNode.imageNode.position
currentItemNode.view.addSubview(avatarCopyView)
let scale = currentItemNode.imageNode.bounds.height / avatarCopyView.bounds.height
avatarCopyView.layer.transform = CATransform3DMakeScale(scale, scale, scale)
avatarCopyView.alpha = 0.0
transition.updateAlpha(layer: avatarCopyView.layer, alpha: 1.0, completion: { [weak avatarCopyView] _ in
Queue.mainQueue().after(0.1, {
avatarCopyView?.removeFromSuperview()
if let currentItemNode = self.listContainerNode.currentItemNode, case .animated = transition {
if let videoNode = self.avatarContainerNode.videoNode {
// videoNode.position = currentItemNode.imageNode.position
// currentItemNode.addSubnode(videoNode)
// let scale = currentItemNode.imageNode.bounds.height / videoNode.bounds.height
// avatarCopyView.layer.transform = CATransform3DMakeScale(scale, scale, scale)
// self.avatarContainerNode.reattachVideoNode()
} else if let unroundedImage = self.avatarContainerNode.avatarNode.unroundedImage {
let avatarCopyView = UIImageView()
avatarCopyView.image = unroundedImage
avatarCopyView.frame = self.avatarContainerNode.avatarNode.frame
avatarCopyView.center = currentItemNode.imageNode.position
currentItemNode.view.addSubview(avatarCopyView)
let scale = currentItemNode.imageNode.bounds.height / avatarCopyView.bounds.height
avatarCopyView.layer.transform = CATransform3DMakeScale(scale, scale, scale)
avatarCopyView.alpha = 0.0
transition.updateAlpha(layer: avatarCopyView.layer, alpha: 1.0, completion: { [weak avatarCopyView] _ in
Queue.mainQueue().after(0.1, {
avatarCopyView?.removeFromSuperview()
})
})
})
}
}
}
}

View File

@ -103,7 +103,7 @@ final class StickerPaneSearchGlobalItem: GridItem {
}
}
return (128.0 + additionalHeight, false)
return (128.0 + additionalHeight, !self.listAppearance)
}
init(account: Account, theme: PresentationTheme, strings: PresentationStrings, listAppearance: Bool, info: StickerPackCollectionInfo, topItems: [StickerPackItem], topSeparator: Bool, regularInsets: Bool, installed: Bool, installing: Bool = false, unread: Bool, open: @escaping () -> Void, install: @escaping () -> Void, getItemIsPreviewed: @escaping (StickerPackItem) -> Bool, itemContext: StickerPaneSearchGlobalItemContext, sectionTitle: String? = nil) {

View File

@ -31,11 +31,12 @@ public final class NativeVideoContent: UniversalVideoContent {
public let baseRate: Double
let fetchAutomatically: Bool
let onlyFullSizeThumbnail: Bool
let autoFetchFullSizeThumbnail: Bool
let continuePlayingWithoutSoundOnLostAudioSession: Bool
let placeholderColor: UIColor
let tempFilePath: String?
public init(id: NativeVideoContentId, fileReference: FileMediaReference, imageReference: ImageMediaReference? = nil, streamVideo: MediaPlayerStreaming = .none, loopVideo: Bool = false, enableSound: Bool = true, baseRate: Double = 1.0, fetchAutomatically: Bool = true, onlyFullSizeThumbnail: Bool = false, continuePlayingWithoutSoundOnLostAudioSession: Bool = false, placeholderColor: UIColor = .white, tempFilePath: String? = nil) {
public init(id: NativeVideoContentId, fileReference: FileMediaReference, imageReference: ImageMediaReference? = nil, streamVideo: MediaPlayerStreaming = .none, loopVideo: Bool = false, enableSound: Bool = true, baseRate: Double = 1.0, fetchAutomatically: Bool = true, onlyFullSizeThumbnail: Bool = false, autoFetchFullSizeThumbnail: Bool = false, continuePlayingWithoutSoundOnLostAudioSession: Bool = false, placeholderColor: UIColor = .white, tempFilePath: String? = nil) {
self.id = id
self.nativeId = id
self.fileReference = fileReference
@ -60,13 +61,14 @@ public final class NativeVideoContent: UniversalVideoContent {
self.baseRate = baseRate
self.fetchAutomatically = fetchAutomatically
self.onlyFullSizeThumbnail = onlyFullSizeThumbnail
self.autoFetchFullSizeThumbnail = autoFetchFullSizeThumbnail
self.continuePlayingWithoutSoundOnLostAudioSession = continuePlayingWithoutSoundOnLostAudioSession
self.placeholderColor = placeholderColor
self.tempFilePath = tempFilePath
}
public func makeContentNode(postbox: Postbox, audioSession: ManagedAudioSession) -> UniversalVideoContentNode & ASDisplayNode {
return NativeVideoContentNode(postbox: postbox, audioSessionManager: audioSession, fileReference: self.fileReference, imageReference: self.imageReference, streamVideo: self.streamVideo, loopVideo: self.loopVideo, enableSound: self.enableSound, baseRate: self.baseRate, fetchAutomatically: self.fetchAutomatically, onlyFullSizeThumbnail: self.onlyFullSizeThumbnail, continuePlayingWithoutSoundOnLostAudioSession: self.continuePlayingWithoutSoundOnLostAudioSession, placeholderColor: self.placeholderColor, tempFilePath: self.tempFilePath)
return NativeVideoContentNode(postbox: postbox, audioSessionManager: audioSession, fileReference: self.fileReference, imageReference: self.imageReference, streamVideo: self.streamVideo, loopVideo: self.loopVideo, enableSound: self.enableSound, baseRate: self.baseRate, fetchAutomatically: self.fetchAutomatically, onlyFullSizeThumbnail: self.onlyFullSizeThumbnail, autoFetchFullSizeThumbnail: self.autoFetchFullSizeThumbnail, continuePlayingWithoutSoundOnLostAudioSession: self.continuePlayingWithoutSoundOnLostAudioSession, placeholderColor: self.placeholderColor, tempFilePath: self.tempFilePath)
}
public func isEqual(to other: UniversalVideoContent) -> Bool {
@ -126,7 +128,7 @@ private final class NativeVideoContentNode: ASDisplayNode, UniversalVideoContent
private var shouldPlay: Bool = false
init(postbox: Postbox, audioSessionManager: ManagedAudioSession, fileReference: FileMediaReference, imageReference: ImageMediaReference?, streamVideo: MediaPlayerStreaming, loopVideo: Bool, enableSound: Bool, baseRate: Double, fetchAutomatically: Bool, onlyFullSizeThumbnail: Bool, continuePlayingWithoutSoundOnLostAudioSession: Bool = false, placeholderColor: UIColor, tempFilePath: String?) {
init(postbox: Postbox, audioSessionManager: ManagedAudioSession, fileReference: FileMediaReference, imageReference: ImageMediaReference?, streamVideo: MediaPlayerStreaming, loopVideo: Bool, enableSound: Bool, baseRate: Double, fetchAutomatically: Bool, onlyFullSizeThumbnail: Bool, autoFetchFullSizeThumbnail: Bool, continuePlayingWithoutSoundOnLostAudioSession: Bool = false, placeholderColor: UIColor, tempFilePath: String?) {
self.postbox = postbox
self.fileReference = fileReference
self.placeholderColor = placeholderColor
@ -163,7 +165,7 @@ private final class NativeVideoContentNode: ASDisplayNode, UniversalVideoContent
self?.performActionAtEnd()
}
self.imageNode.setSignal(internalMediaGridMessageVideo(postbox: postbox, videoReference: fileReference, imageReference: imageReference, onlyFullSize: onlyFullSizeThumbnail, autoFetchFullSizeThumbnail: fileReference.media.isInstantVideo) |> map { [weak self] getSize, getData in
self.imageNode.setSignal(internalMediaGridMessageVideo(postbox: postbox, videoReference: fileReference, imageReference: imageReference, onlyFullSize: onlyFullSizeThumbnail, autoFetchFullSizeThumbnail: autoFetchFullSizeThumbnail || fileReference.media.isInstantVideo) |> map { [weak self] getSize, getData in
Queue.mainQueue().async {
if let strongSelf = self, strongSelf.dimensions == nil {
if let dimensions = getSize() {