Send live photos as gifs

This commit is contained in:
Ilya Laktyushin 2020-06-10 01:19:12 +03:00
parent 853febcb28
commit e085de4de5
25 changed files with 1250 additions and 1034 deletions

View file

@ -5538,3 +5538,5 @@ Any member of this group will be able to see messages in the channel.";
"Conversation.PrivateChannelTimeLimitedAlertTitle" = "Join Channel";
"Conversation.PrivateChannelTimeLimitedAlertText" = "This channel is private. Please join it to continue viewing its content.";
"Conversation.PrivateChannelTimeLimitedAlertJoin" = "Join";
"KeyCommand.SearchInChat" = "Search in Chat";

View file

@ -101,6 +101,7 @@ public func chatListItemStrings(strings: PresentationStrings, nameDisplayOrder:
case let .Audio(isVoice, _, title, performer, _):
if !message.text.isEmpty {
messageText = "🎤 \(messageText)"
processed = true
} else if isVoice {
if message.text.isEmpty {
messageText = strings.Message_Audio

View file

@ -417,6 +417,12 @@ open class GalleryControllerNode: ASDisplayNode, UIScrollViewDelegate, UIGesture
}
}
if velocity.y > 0.0 || distanceFromEquilibrium > 0.0, let centralItemNode = self.pager.centralItemNode() {
if centralItemNode.alternativeDismiss() {
return
}
}
if let centralItemNode = self.pager.centralItemNode(), let (transitionNodeForCentralItem, addToTransitionSurface) = self.transitionDataForCentralItem?(), let node = transitionNodeForCentralItem {
contentAnimationCompleted = false
centralItemNode.animateOut(to: node, addToTransitionSurface: addToTransitionSurface, completion: {

View file

@ -25,6 +25,7 @@ open class GalleryItemNode: ASDisplayNode {
public var beginCustomDismiss: () -> Void = { }
public var completeCustomDismiss: () -> Void = { }
public var baseNavigationController: () -> NavigationController? = { return nil }
public var alternativeDismiss: () -> Bool = { return false }
override public init() {
super.init()

View file

@ -401,6 +401,15 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode {
strongSelf.footerContentNode.setFramePreviewImage(image: nil)
}
})
self.alternativeDismiss = { [weak self] in
guard let strongSelf = self else {
return false
}
strongSelf.pictureInPictureButtonPressed()
return true
}
}
deinit {

View file

@ -0,0 +1,12 @@
{
"images" : [
{
"filename" : "ic_gif.pdf",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View file

@ -15,7 +15,7 @@
- (instancetype)initWithURL:(NSURL *)url;
- (instancetype)initWithAsset:(TGMediaAsset *)asset;
- (instancetype)initWithAsset:(TGMediaAsset *)asset livePhoto:(bool)livePhoto;
- (void)_cleanUp;

View file

@ -4,6 +4,7 @@
#import <AVFoundation/AVFoundation.h>
@class TGPhotoEntitiesContainerView;
@protocol TGMediaEditableItem;
@interface TGMediaPickerGalleryVideoItemView : TGModernGalleryItemView <TGModernGalleryEditableItemView>
@ -35,4 +36,6 @@
- (CGRect)editorTransitionViewRect;
- (TGPhotoEntitiesContainerView *)entitiesView;
- (id<TGMediaEditableItem>)editableMediaItem;
@end

View file

@ -27,6 +27,8 @@
+ (UIImage *)gifBackgroundImage;
+ (UIImage *)gifIcon;
+ (UIImage *)gifActiveIcon;
+ (UIImage *)muteIcon;
+ (UIImage *)muteActiveIcon;
+ (UIImage *)qualityIconForPreset:(TGMediaVideoConversionPreset)preset;
+ (UIImage *)timerIconForValue:(NSInteger)value;
+ (UIImage *)eraserIcon;

View file

@ -16,15 +16,22 @@
NSTimeInterval _cachedDuration;
AVURLAsset *_cachedAVAsset;
bool _livePhoto;
}
@end
@implementation TGCameraCapturedVideo
+ (NSURL *)videoURLForAsset:(TGMediaAsset *)asset {
NSURL *convertedGifsUrl = [NSURL fileURLWithPath:[[[LegacyComponentsGlobals provider] dataStoragePath] stringByAppendingPathComponent:@"convertedGifs"]];
[[NSFileManager defaultManager] createDirectoryAtPath:convertedGifsUrl.path withIntermediateDirectories:true attributes:nil error:nil];
return [convertedGifsUrl URLByAppendingPathComponent:[NSString stringWithFormat:@"%@.mp4", [TGStringUtils md5:asset.identifier]]];
if (asset.type == TGMediaAssetGifType) {
NSURL *convertedGifsUrl = [NSURL fileURLWithPath:[[[LegacyComponentsGlobals provider] dataStoragePath] stringByAppendingPathComponent:@"convertedGifs"]];
[[NSFileManager defaultManager] createDirectoryAtPath:convertedGifsUrl.path withIntermediateDirectories:true attributes:nil error:nil];
return [convertedGifsUrl URLByAppendingPathComponent:[NSString stringWithFormat:@"%@.mp4", [TGStringUtils md5:asset.identifier]]];
} else {
NSURL *convertedLivePhotosUrl = [NSURL fileURLWithPath:[[[LegacyComponentsGlobals provider] dataStoragePath] stringByAppendingPathComponent:@"convertedLivePhotos"]];
[[NSFileManager defaultManager] createDirectoryAtPath:convertedLivePhotosUrl.path withIntermediateDirectories:true attributes:nil error:nil];
return [convertedLivePhotosUrl URLByAppendingPathComponent:[NSString stringWithFormat:@"%@.mp4", [TGStringUtils md5:asset.identifier]]];
}
}
- (instancetype)initWithURL:(NSURL *)url
@ -39,12 +46,13 @@
return self;
}
- (instancetype)initWithAsset:(TGMediaAsset *)asset
- (instancetype)initWithAsset:(TGMediaAsset *)asset livePhoto:(bool)livePhoto
{
self = [super init];
if (self != nil)
{
_originalAsset = asset;
_livePhoto = livePhoto;
_cachedSize = CGSizeZero;
_cachedDuration = 0.0;
@ -71,30 +79,46 @@
- (AVAsset *)immediateAVAsset {
return _cachedAVAsset;
}
- (SSignal *)avAsset {
if (_originalAsset != nil) {
if (_cachedAVAsset != nil) {
return [SSignal single:_cachedAVAsset];
} else {
NSURL *videoUrl = [TGCameraCapturedVideo videoURLForAsset:_originalAsset];
return [[TGMediaAssetImageSignals imageDataForAsset:_originalAsset allowNetworkAccess:false] mapToSignal:^SSignal *(TGMediaAssetImageData *assetData) {
NSData *data = assetData.imageData;
const char *gif87Header = "GIF87";
const char *gif89Header = "GIF89";
if (data.length >= 5 && (!memcmp(data.bytes, gif87Header, 5) || !memcmp(data.bytes, gif89Header, 5)))
{
return [[TGGifConverter convertGifToMp4:data] map:^id(NSDictionary *result)
{
NSString *filePath = result[@"path"];
[[NSFileManager defaultManager] moveItemAtPath:filePath toPath:videoUrl.path error:nil];
if ([[NSFileManager defaultManager] fileExistsAtPath:videoUrl.path isDirectory:nil]) {
_cachedAVAsset = [AVURLAsset assetWithURL:videoUrl];
return [SSignal single:_cachedAVAsset];
} else {
if (_originalAsset.type == TGMediaAssetGifType) {
return [[[TGMediaAssetImageSignals imageDataForAsset:_originalAsset allowNetworkAccess:false] mapToSignal:^SSignal *(TGMediaAssetImageData *assetData) {
NSData *data = assetData.imageData;
return [AVURLAsset assetWithURL:videoUrl];
const char *gif87Header = "GIF87";
const char *gif89Header = "GIF89";
if (data.length >= 5 && (!memcmp(data.bytes, gif87Header, 5) || !memcmp(data.bytes, gif89Header, 5)))
{
return [[TGGifConverter convertGifToMp4:data] map:^id(NSDictionary *result)
{
NSString *filePath = result[@"path"];
[[NSFileManager defaultManager] moveItemAtPath:filePath toPath:videoUrl.path error:nil];
return [AVURLAsset assetWithURL:videoUrl];
}];
} else {
return [SSignal complete];
}
}] onNext:^(id next) {
_cachedAVAsset = next;
}];
} else {
return [SSignal complete];
return [[[TGMediaAssetImageSignals avAssetForVideoAsset:_originalAsset allowNetworkAccess:false] mapToSignal:^SSignal *(AVURLAsset *asset) {
return [SSignal single:asset];
}] onNext:^(id next) {
_cachedAVAsset = next;
}];
}
}];
}
}
} else {
return [SSignal single:_cachedAVAsset];

View file

@ -2,6 +2,7 @@
#import "LegacyComponentsInternal.h"
#import "TGImageUtils.h"
#import "TGStringUtils.h"
#import <Photos/Photos.h>
#import <LegacyComponents/UIImage+TG.h>
@ -770,21 +771,27 @@
if (videoResource != nil)
{
NSString *filePath = [NSTemporaryDirectory() stringByAppendingPathComponent:[NSString stringWithFormat:@"%@.mov", [NSString stringWithFormat:@"%.0f", [[NSDate date] timeIntervalSince1970]]]];
NSURL *fileUrl = [NSURL fileURLWithPath:filePath];
NSURL *convertedLivePhotosUrl = [NSURL fileURLWithPath:[[[LegacyComponentsGlobals provider] dataStoragePath] stringByAppendingPathComponent:@"convertedLivePhotos"]];
[[NSFileManager defaultManager] createDirectoryAtPath:convertedLivePhotosUrl.path withIntermediateDirectories:true attributes:nil error:nil];
NSURL *fileUrl = [convertedLivePhotosUrl URLByAppendingPathComponent:[NSString stringWithFormat:@"%@.mp4", [TGStringUtils md5:asset.identifier]]];
[[PHAssetResourceManager defaultManager] writeDataForAssetResource:videoResource toFile:fileUrl options:nil completionHandler:^(NSError * _Nullable error)
{
if (error == nil)
{
[subscriber putNext:[[AVPlayerItem alloc] initWithURL:fileUrl]];
[subscriber putCompletion];
}
else
{
[subscriber putError:nil];
}
}];
if ([[NSFileManager defaultManager] fileExistsAtPath:fileUrl.path isDirectory:nil]) {
[subscriber putNext:[[AVPlayerItem alloc] initWithURL:fileUrl]];
[subscriber putCompletion];
} else {
[[PHAssetResourceManager defaultManager] writeDataForAssetResource:videoResource toFile:fileUrl options:nil completionHandler:^(NSError * _Nullable error)
{
if (error == nil)
{
[subscriber putNext:[[AVPlayerItem alloc] initWithURL:fileUrl]];
[subscriber putCompletion];
}
else
{
[subscriber putError:nil];
}
}];
}
}
else
{
@ -850,9 +857,11 @@
if (processLive)
{
NSString *filePath = [NSTemporaryDirectory() stringByAppendingPathComponent:[NSString stringWithFormat:@"%@.mov", asset.identifier]];
NSURL *fileUrl = [NSURL fileURLWithPath:filePath];
if ([[NSFileManager defaultManager] fileExistsAtPath:filePath])
NSURL *convertedLivePhotosUrl = [NSURL fileURLWithPath:[[[LegacyComponentsGlobals provider] dataStoragePath] stringByAppendingPathComponent:@"convertedLivePhotos"]];
[[NSFileManager defaultManager] createDirectoryAtPath:convertedLivePhotosUrl.path withIntermediateDirectories:true attributes:nil error:nil];
NSURL *fileUrl = [convertedLivePhotosUrl URLByAppendingPathComponent:[NSString stringWithFormat:@"%@.mp4", [TGStringUtils md5:asset.identifier]]];
if ([[NSFileManager defaultManager] fileExistsAtPath:fileUrl.path])
{
[subscriber putNext:[[AVURLAsset alloc] initWithURL:fileUrl options:nil]];
[subscriber putCompletion];

View file

@ -773,25 +773,77 @@
SSignal *imageSignal = assetSignal;
if (adjustments.sendAsGif)
{
[signals addObject:[inlineThumbnailSignal(asset) map:^NSDictionary *(UIImage *image)
NSTimeInterval trimStartValue = 0.0;
if ([adjustments isKindOfClass:[TGVideoEditAdjustments class]]) {
TGVideoEditAdjustments *videoAdjustments = (TGVideoEditAdjustments *)adjustments;
trimStartValue = videoAdjustments.trimStartValue;
}
UIImage *(^cropVideoThumbnail)(UIImage *, CGSize, CGSize, bool) = ^UIImage *(UIImage *image, CGSize targetSize, CGSize sourceSize, bool resize)
{
NSMutableDictionary *dict = [[NSMutableDictionary alloc] init];
dict[@"type"] = @"cloudPhoto";
dict[@"previewImage"] = image;
dict[@"livePhoto"] = @true;
dict[@"asset"] = asset;
dict[@"adjustments"] = adjustments;
if ([adjustments cropAppliedForAvatar:false] || adjustments.hasPainting || adjustments.toolsApplied)
{
CGRect scaledCropRect = CGRectMake(adjustments.cropRect.origin.x * image.size.width / adjustments.originalSize.width, adjustments.cropRect.origin.y * image.size.height / adjustments.originalSize.height, adjustments.cropRect.size.width * image.size.width / adjustments.originalSize.width, adjustments.cropRect.size.height * image.size.height / adjustments.originalSize.height);
UIImage *paintingImage = adjustments.paintingData.stillImage;
if (paintingImage == nil) {
paintingImage = adjustments.paintingData.image;
}
if (adjustments.toolsApplied) {
image = [PGPhotoEditor resultImageForImage:image adjustments:adjustments];
}
return TGPhotoEditorCrop(image, paintingImage, adjustments.cropOrientation, 0, scaledCropRect, adjustments.cropMirrored, targetSize, sourceSize, resize);
}
if (adjustments.paintingData.stickers.count > 0)
dict[@"stickers"] = adjustments.paintingData.stickers;
if (timer != nil)
dict[@"timer"] = timer;
else if (groupedId != nil && !hasAnyTimers)
dict[@"groupedId"] = groupedId;
id generatedItem = descriptionGenerator(dict, caption, entities, nil);
return generatedItem;
return image;
};
SSignal *trimmedVideoThumbnailSignal = [[TGMediaAssetImageSignals avAssetForVideoAsset:asset allowNetworkAccess:false] mapToSignal:^SSignal *(AVAsset *avAsset)
{
CGSize imageSize = TGFillSize(asset.dimensions, CGSizeMake(512, 512));
return [[TGMediaAssetImageSignals videoThumbnailForAVAsset:avAsset size:imageSize timestamp:CMTimeMakeWithSeconds(trimStartValue, NSEC_PER_SEC)] map:^UIImage *(UIImage *image)
{
return cropVideoThumbnail(image, TGScaleToFill(asset.dimensions, CGSizeMake(512, 512)), asset.dimensions, true);
}];
}];
SSignal *videoThumbnailSignal = [inlineThumbnailSignal(asset) map:^UIImage *(UIImage *image)
{
return cropVideoThumbnail(image, image.size, image.size, false);
}];
SSignal *thumbnailSignal = trimStartValue > FLT_EPSILON ? trimmedVideoThumbnailSignal : videoThumbnailSignal;
TGMediaVideoConversionPreset preset = [TGMediaVideoConverter presetFromAdjustments:adjustments];
CGSize dimensions = [TGMediaVideoConverter dimensionsFor:asset.originalSize adjustments:adjustments preset:preset];
TGCameraCapturedVideo *videoAsset = [[TGCameraCapturedVideo alloc] initWithAsset:asset livePhoto:true];
[signals addObject:[thumbnailSignal mapToSignal:^SSignal *(UIImage *image)
{
return [videoAsset.avAsset map:^id(AVURLAsset *avAsset) {
NSTimeInterval duration = CMTimeGetSeconds(avAsset.duration);
if ([adjustments isKindOfClass:[TGVideoEditAdjustments class]]) {
TGVideoEditAdjustments *videoAdjustments = (TGVideoEditAdjustments *)adjustments;
duration = videoAdjustments.trimApplied ? (videoAdjustments.trimEndValue - videoAdjustments.trimStartValue) : duration;
}
NSMutableDictionary *dict = [[NSMutableDictionary alloc] init];
dict[@"type"] = @"cameraVideo";
dict[@"url"] = avAsset.URL;
dict[@"previewImage"] = image;
dict[@"duration"] = @(duration);
dict[@"dimensions"] = [NSValue valueWithCGSize:dimensions];
dict[@"adjustments"] = adjustments;
if (adjustments.paintingData.stickers.count > 0)
dict[@"stickers"] = adjustments.paintingData.stickers;
if (timer != nil)
dict[@"timer"] = timer;
else if (groupedId != nil && !hasAnyTimers)
dict[@"groupedId"] = groupedId;
id generatedItem = descriptionGenerator(dict, caption, entities, nil);
return generatedItem;
}];
}]];
}
else

View file

@ -159,9 +159,9 @@
_muteButton.hidden = true;
_muteButton.adjustsImageWhenHighlighted = false;
[_muteButton setBackgroundImage:[TGPhotoEditorInterfaceAssets gifBackgroundImage] forState:UIControlStateNormal];
[_muteButton setImage:[TGPhotoEditorInterfaceAssets gifIcon] forState:UIControlStateNormal];
[_muteButton setImage:[TGPhotoEditorInterfaceAssets gifActiveIcon] forState:UIControlStateSelected];
[_muteButton setImage:[TGPhotoEditorInterfaceAssets gifActiveIcon] forState:UIControlStateSelected | UIControlStateHighlighted];
[_muteButton setImage:[TGPhotoEditorInterfaceAssets muteIcon] forState:UIControlStateNormal];
[_muteButton setImage:[TGPhotoEditorInterfaceAssets muteActiveIcon] forState:UIControlStateSelected];
[_muteButton setImage:[TGPhotoEditorInterfaceAssets muteActiveIcon] forState:UIControlStateSelected | UIControlStateHighlighted];
[_muteButton addTarget:self action:@selector(toggleSendAsGif) forControlEvents:UIControlEventTouchUpInside];
[_wrapperView addSubview:_muteButton];
@ -574,8 +574,6 @@
[strongSelf->_portraitToolbarView setEditButtonsEnabled:available animated:true];
[strongSelf->_landscapeToolbarView setEditButtonsEnabled:available animated:true];
bool sendableAsGif = !strongSelf->_inhibitMute && [strongItemView isKindOfClass:[TGMediaPickerGalleryVideoItemView class]];
if ([strongSelf->_currentItem isKindOfClass:[TGMediaPickerGalleryVideoItem class]]) {
TGMediaPickerGalleryVideoItem *item = (TGMediaPickerGalleryVideoItem *)strongSelf->_currentItem;
@ -586,6 +584,22 @@
strongSelf->_muteButton.hidden = !sendableAsGif;
}
}]];
UIImage *muteIcon = [TGPhotoEditorInterfaceAssets muteIcon];
UIImage *muteActiveIcon = [TGPhotoEditorInterfaceAssets muteActiveIcon];
if ([item isKindOfClass:[TGMediaPickerGalleryVideoItem class]]) {
TGMediaPickerGalleryVideoItem *videoGalleryItem = (TGMediaPickerGalleryVideoItem *)item;
if ([videoGalleryItem.editableMediaItem isKindOfClass:[TGMediaAsset class]]) {
TGMediaAsset *asset = (TGMediaAsset *)videoGalleryItem.editableMediaItem;
if (asset.type == TGMediaAssetPhotoType) {
muteIcon = [TGPhotoEditorInterfaceAssets gifIcon];
muteActiveIcon = [TGPhotoEditorInterfaceAssets gifActiveIcon];
}
}
}
[_muteButton setImage:muteIcon forState:UIControlStateNormal];
[_muteButton setImage:muteActiveIcon forState:UIControlStateSelected];
[_muteButton setImage:muteActiveIcon forState:UIControlStateSelected | UIControlStateHighlighted];
}
- (TGPhotoEditorTab)currentTabs
@ -1264,7 +1278,7 @@
return;
TGModernGalleryItemView *currentItemView = _currentItemView;
bool sendableAsGif = [currentItemView isKindOfClass:[TGMediaPickerGalleryVideoItemView class]]; // || ([currentItemView.item isKindOfClass:[TGMediaPickerGalleryPhotoItem class]]; && ((TGMediaPickerGalleryPhotoItem *)currentItemView.item).asset.subtypes & TGMediaAssetSubtypePhotoLive);
bool sendableAsGif = [currentItemView isKindOfClass:[TGMediaPickerGalleryVideoItemView class]];
if (sendableAsGif)
[(TGMediaPickerGalleryVideoItemView *)currentItemView toggleSendAsGif];
}

View file

@ -352,7 +352,7 @@
_itemBeingEdited = item;
PGPhotoEditorValues *editorValues = (PGPhotoEditorValues *)[item.editingContext adjustmentsForItem:item.editableMediaItem];
id<TGMediaEditAdjustments> adjustments = [item.editingContext adjustmentsForItem:item.editableMediaItem];
NSString *caption = [item.editingContext captionForItem:item.editableMediaItem];
@ -365,6 +365,8 @@
TGPhotoEntitiesContainerView *entitiesView = nil;
id<TGMediaEditableItem> editableMediaItem = item.editableMediaItem;
bool isVideo = false;
if ([editorReferenceView isKindOfClass:[UIImageView class]])
{
@ -389,13 +391,15 @@
entitiesView = [videoItemView entitiesView];
isVideo = true;
editableMediaItem = videoItemView.editableMediaItem;
}
if (self.useGalleryImageAsEditableItemImage && self.storeOriginalImageForItem != nil)
self.storeOriginalImageForItem(item.editableMediaItem, screenImage);
TGPhotoEditorControllerIntent intent = isVideo ? TGPhotoEditorControllerVideoIntent : TGPhotoEditorControllerGenericIntent;
TGPhotoEditorController *controller = [[TGPhotoEditorController alloc] initWithContext:_context item:item.editableMediaItem intent:intent adjustments:editorValues caption:caption screenImage:screenImage availableTabs:_interfaceView.currentTabs selectedTab:tab];
TGPhotoEditorController *controller = [[TGPhotoEditorController alloc] initWithContext:_context item:editableMediaItem intent:intent adjustments:adjustments caption:caption screenImage:screenImage availableTabs:_interfaceView.currentTabs selectedTab:tab];
controller.entitiesView = entitiesView;
controller.editingContext = _editingContext;
controller.stickersContext = _stickersContext;
@ -429,7 +433,7 @@
if (hasChanges)
{
if (didFinishEditingItem != nil) {
didFinishEditingItem(item.editableMediaItem, adjustments, resultImage, thumbnailImage);
didFinishEditingItem(editableMediaItem, adjustments, resultImage, thumbnailImage);
}
}
@ -448,7 +452,7 @@
return;
if (strongSelf.didFinishRenderingFullSizeImage != nil)
strongSelf.didFinishRenderingFullSizeImage(item.editableMediaItem, image);
strongSelf.didFinishRenderingFullSizeImage(editableMediaItem, image);
};
controller.captionSet = ^(NSString *caption, NSArray *entities)

View file

@ -20,6 +20,8 @@
#import "TGMediaPickerGalleryItem.h"
#import "TGMediaPickerGalleryVideoItem.h"
#import "TGCameraCapturedVideo.h"
#import <LegacyComponents/TGVideoEditAdjustments.h>
#import <LegacyComponents/TGPaintingData.h>
@ -96,6 +98,8 @@
SMetaDisposable *_downloadDisposable;
SMetaDisposable *_currentAudioSession;
SVariable *_editableItemVariable;
UIEdgeInsets _safeAreaInset;
bool _requestingThumbnails;
@ -127,6 +131,8 @@
_videoDurationDisposable = [[SMetaDisposable alloc] init];
_adjustmentsDisposable = [[SMetaDisposable alloc] init];
_editableItemVariable = [[SVariable alloc] init];
_containerView = [[UIView alloc] initWithFrame:self.bounds];
_containerView.clipsToBounds = true;
@ -434,23 +440,27 @@
[self.imageView setSignal:imageSignal];
[_editableItemVariable set:[SSignal single:[self editableMediaItem]]];
if (item.editingContext != nil)
{
SSignal *adjustmentsSignal = [item.editingContext adjustmentsSignalForItem:item.editableMediaItem];
[_adjustmentsDisposable setDisposable:[[adjustmentsSignal deliverOn:[SQueue mainQueue]] startWithNext:^(__unused id next)
SSignal *adjustmentsSignal = [[self editableItemSignal] mapToSignal:^SSignal *(id<TGMediaEditableItem> editableItem) {
return [item.editingContext adjustmentsSignalForItem:editableItem];
}];
[_adjustmentsDisposable setDisposable:[[adjustmentsSignal deliverOn:[SQueue mainQueue]] startWithNext:^(id<TGMediaEditAdjustments> adjustments)
{
__strong TGMediaPickerGalleryVideoItemView *strongSelf = weakSelf;
if (strongSelf == nil)
return;
[strongSelf _layoutPlayerView];
TGVideoEditAdjustments *adjustments = (TGVideoEditAdjustments *)[strongSelf.item.editingContext adjustmentsForItem:strongSelf.item.editableMediaItem];
strongSelf->_paintingImageView.image = adjustments.paintingData.image;
strongSelf->_sendAsGif = adjustments.sendAsGif;
[strongSelf _mutePlayer:adjustments.sendAsGif];
id<TGMediaEditAdjustments> baseAdjustments = [strongSelf.item.editingContext adjustmentsForItem:strongSelf.item.editableMediaItem];
strongSelf->_sendAsGif = baseAdjustments.sendAsGif;
[strongSelf _mutePlayer:baseAdjustments.sendAsGif];
if (adjustments.sendAsGif || ([strongSelf itemIsLivePhoto]))
if (baseAdjustments.sendAsGif || ([strongSelf itemIsLivePhoto]))
[strongSelf setPlayButtonHidden:true animated:false];
[strongSelf->_entitiesContainerView setupWithPaintingData:adjustments.paintingData];
@ -1370,6 +1380,23 @@
#pragma mark - Edit Adjustments
- (SSignal *)editableItemSignal {
return [_editableItemVariable signal];
}
- (id<TGMediaEditableItem>)editableMediaItem {
TGVideoEditAdjustments *adjustments = (TGVideoEditAdjustments *)[self.item.editingContext adjustmentsForItem:self.item.editableMediaItem];
if ([self itemIsLivePhoto]) {
if (adjustments.sendAsGif) {
return [[TGCameraCapturedVideo alloc] initWithAsset:self.item.editableMediaItem livePhoto:true];
} else {
return self.item.editableMediaItem;
}
} else {
return self.item.editableMediaItem;
}
}
- (void)toggleSendAsGif
{
TGVideoEditAdjustments *adjustments = (TGVideoEditAdjustments *)[self.item.editingContext adjustmentsForItem:self.item.editableMediaItem];
@ -1388,43 +1415,45 @@
trimEndValue = adjustments.trimEndValue;
}
}
NSTimeInterval trimDuration = trimEndValue - trimStartValue;
// NSTimeInterval trimDuration = trimEndValue - trimStartValue;
bool sendAsGif = !adjustments.sendAsGif;
if (sendAsGif && _scrubberView.allowsTrimming)
{
if (trimDuration > TGVideoEditMaximumGifDuration)
{
trimEndValue = trimStartValue + TGVideoEditMaximumGifDuration;
if (_scrubberView.value > trimEndValue)
{
[self stop];
[_scrubberView setValue:_scrubberView.trimStartValue resetPosition:true];
[self _seekToPosition:_scrubberView.value manual:true];
}
_scrubberView.trimStartValue = trimStartValue;
_scrubberView.trimEndValue = trimEndValue;
[_scrubberView setTrimApplied:true];
[self updatePlayerRange:trimEndValue];
}
}
else if (_shouldResetScrubber)
{
trimStartValue = 0.0;
trimEndValue = _videoDuration;
_scrubberView.trimStartValue = trimStartValue;
_scrubberView.trimEndValue = trimEndValue;
[_scrubberView setTrimApplied:false];
[self updatePlayerRange:trimEndValue];
}
// if (sendAsGif && _scrubberView.allowsTrimming)
// {
// if (trimDuration > TGVideoEditMaximumGifDuration)
// {
// trimEndValue = trimStartValue + TGVideoEditMaximumGifDuration;
//
// if (_scrubberView.value > trimEndValue)
// {
// [self stop];
// [_scrubberView setValue:_scrubberView.trimStartValue resetPosition:true];
// [self _seekToPosition:_scrubberView.value manual:true];
// }
//
// _scrubberView.trimStartValue = trimStartValue;
// _scrubberView.trimEndValue = trimEndValue;
// [_scrubberView setTrimApplied:true];
// [self updatePlayerRange:trimEndValue];
// }
// }
// else if (_shouldResetScrubber)
// {
// trimStartValue = 0.0;
// trimEndValue = _videoDuration;
//
// _scrubberView.trimStartValue = trimStartValue;
// _scrubberView.trimEndValue = trimEndValue;
//
// [_scrubberView setTrimApplied:false];
// [self updatePlayerRange:trimEndValue];
// }
TGVideoEditAdjustments *updatedAdjustments = [TGVideoEditAdjustments editAdjustmentsWithOriginalSize:_videoDimensions cropRect:cropRect cropOrientation:adjustments.cropOrientation cropRotation:adjustments.cropRotation cropLockedAspectRatio:adjustments.cropLockedAspectRatio cropMirrored:adjustments.cropMirrored trimStartValue:trimStartValue trimEndValue:trimEndValue toolValues:adjustments.toolValues paintingData:adjustments.paintingData sendAsGif:sendAsGif preset:adjustments.preset];
[self.item.editingContext setAdjustments:updatedAdjustments forItem:self.item.editableMediaItem];
[_editableItemVariable set:[SSignal single:[self editableMediaItem]]];
if (sendAsGif)
{
if (UIInterfaceOrientationIsPortrait([[LegacyComponentsGlobals provider] applicationStatusBarOrientation]))
@ -1552,7 +1581,7 @@
SSignal *avAsset = self.item.avAsset ?: [SSignal single:_player.currentItem.asset];
TGMediaEditingContext *editingContext = self.item.editingContext;
id<TGMediaEditableItem> editableItem = self.item.editableMediaItem;
id<TGMediaEditableItem> editableItem = self.editableMediaItem;
SSignal *thumbnailsSignal = nil;
if ([self.item.asset isKindOfClass:[TGMediaAsset class]] && ![self itemIsLivePhoto])

View file

@ -370,17 +370,17 @@
case TGMediaAssetGifType:
{
// TGCameraCapturedVideo *convertedAsset = [[TGCameraCapturedVideo alloc] initWithAsset:asset];
// galleryItem = [[TGMediaPickerGalleryVideoItem alloc] initWithAsset:convertedAsset];
galleryItem = [[TGMediaPickerGalleryGifItem alloc] initWithAsset:asset];
TGCameraCapturedVideo *convertedAsset = [[TGCameraCapturedVideo alloc] initWithAsset:asset livePhoto:false];
galleryItem = [[TGMediaPickerGalleryVideoItem alloc] initWithAsset:convertedAsset];
// galleryItem = [[TGMediaPickerGalleryGifItem alloc] initWithAsset:asset];
}
break;
default:
{
// if (asset.subtypes & TGMediaAssetSubtypePhotoLive)
// galleryItem = [[TGMediaPickerGalleryVideoItem alloc] initWithAsset:asset];
// else
if (asset.subtypes & TGMediaAssetSubtypePhotoLive)
galleryItem = [[TGMediaPickerGalleryVideoItem alloc] initWithAsset:asset];
else
galleryItem = [[TGMediaPickerGalleryPhotoItem alloc] initWithAsset:asset];
}
break;

View file

@ -135,16 +135,26 @@
return muteBackground;
}
+ (UIImage *)gifIcon
+ (UIImage *)muteIcon
{
return TGComponentsImageNamed(@"PhotoEditorMute.png");
}
+ (UIImage *)gifActiveIcon
+ (UIImage *)muteActiveIcon
{
return TGTintedImage([self gifIcon], [self accentColor]);
}
+ (UIImage *)gifIcon
{
return TGTintedImage([UIImage imageNamed:@"Editor/Gif"], [self toolbarIconColor]);
}
+ (UIImage *)gifActiveIcon
{
return TGTintedImage([UIImage imageNamed:@"Editor/Gif"], [self accentColor]);
}
+ (UIImage *)groupIcon
{
return TGTintedImage(TGComponentsImageNamed(@"PhotoEditorGroupPhotosIcon.png"), UIColorRGB(0x4cb4ff));

View file

@ -503,7 +503,7 @@ open class TelegramBaseController: ViewController, KeyShortcutResponder {
}
let mediaAccessoryPanel = MediaNavigationAccessoryPanel(context: self.context)
mediaAccessoryPanel.containerNode.headerNode.displayScrubber = type != .voice
mediaAccessoryPanel.containerNode.headerNode.displayScrubber = item.playbackData?.type != .instantVideo
mediaAccessoryPanel.close = { [weak self] in
if let strongSelf = self, let (_, _, _, _, type, _) = strongSelf.playlistStateAndType {
strongSelf.context.sharedContext.mediaManager.setPlaylist(nil, type: type, control: SharedMediaPlayerControlAction.playback(.pause))

View file

@ -155,7 +155,7 @@ public final class PeersNearbyContext {
var selfPeer: PeerNearby?
for entry in updatedEntries {
switch entry {
case let .selfPeer:
case .selfPeer:
if let index = existingSelfPeer {
entries[index] = entry
} else {

View file

@ -9069,7 +9069,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
})
}
}),
KeyShortcut(input: "F", modifiers: [.command], action: { [weak self] in
KeyShortcut(title: strings.KeyCommand_SearchInChat, input: "F", modifiers: [.command], action: { [weak self] in
if let strongSelf = self {
strongSelf.beginMessageSearch("")
}

View file

@ -213,7 +213,7 @@ final class PeerInfoListPaneNode: ASDisplayNode, PeerInfoPaneNode {
}
let mediaAccessoryPanel = MediaNavigationAccessoryPanel(context: self.context)
mediaAccessoryPanel.containerNode.headerNode.displayScrubber = type != .voice
mediaAccessoryPanel.containerNode.headerNode.displayScrubber = item.playbackData?.type != .instantVideo
mediaAccessoryPanel.close = { [weak self] in
if let strongSelf = self, let (_, _, _, _, type, _) = strongSelf.playlistStateAndType {
strongSelf.context.sharedContext.mediaManager.setPlaylist(nil, type: type, control: SharedMediaPlayerControlAction.playback(.pause))

View file

@ -421,14 +421,51 @@ public func legacyEnqueueWebSearchMessages(_ selectionState: TGMediaSelectionCon
var signals: [Any] = []
for result in results {
let editableItem = LegacyWebSearchItem(result: result)
if editingState.adjustments(for: editableItem) != nil {
if let adjustments = editingState.adjustments(for: editableItem) {
var animated = false
if let entities = adjustments.paintingData?.entities {
for entity in entities {
if let paintEntity = entity as? TGPhotoPaintEntity, paintEntity.animated {
animated = true
break
}
}
}
if let imageSignal = editingState.imageSignal(for: editableItem) {
let signal = imageSignal.map { image -> Any in
if let image = image as? UIImage {
let dict: [AnyHashable: Any] = [
var dict: [AnyHashable: Any] = [
"type": "editedPhoto",
"image": image
]
if animated {
dict["isAnimation"] = true
if let photoEditorValues = adjustments as? PGPhotoEditorValues {
dict["adjustments"] = TGVideoEditAdjustments(photoEditorValues: photoEditorValues)
}
let filePath = NSTemporaryDirectory().appending("/gifvideo_\(arc4random()).jpg")
let data = image.jpegData(compressionQuality: 0.8)
if let data = data {
let _ = try? data.write(to: URL(fileURLWithPath: filePath), options: [])
}
dict["url"] = NSURL(fileURLWithPath: filePath)
if adjustments.cropApplied(forAvatar: false) || adjustments.hasPainting() || adjustments.toolsApplied() {
var paintingImage: UIImage? = adjustments.paintingData?.stillImage
if paintingImage == nil {
paintingImage = adjustments.paintingData?.image
}
let thumbnailImage = TGPhotoEditorVideoExtCrop(image, paintingImage, adjustments.cropOrientation, adjustments.cropRotation, adjustments.cropRect, adjustments.cropMirrored, TGScaleToFill(image.size, CGSize(width: 512.0, height: 512.0)), adjustments.originalSize, true, true, true)
if let thumbnailImage = thumbnailImage {
dict["previewImage"] = thumbnailImage
}
}
}
return legacyAssetPickerItemGenerator()(dict, nil, nil, nil) as Any
} else {
return SSignal.complete()