mirror of
https://github.com/TelegramMessenger/Telegram-iOS.git
synced 2026-07-06 03:33:41 +02:00
Send live photos as gifs
This commit is contained in:
parent
853febcb28
commit
e085de4de5
25 changed files with 1250 additions and 1034 deletions
|
|
@ -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";
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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: {
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
12
submodules/LegacyComponents/LegacyImages.xcassets/Editor/Gif.imageset/Contents.json
vendored
Normal file
12
submodules/LegacyComponents/LegacyImages.xcassets/Editor/Gif.imageset/Contents.json
vendored
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "ic_gif.pdf",
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
BIN
submodules/LegacyComponents/LegacyImages.xcassets/Editor/Gif.imageset/ic_gif.pdf
vendored
Normal file
BIN
submodules/LegacyComponents/LegacyImages.xcassets/Editor/Gif.imageset/ic_gif.pdf
vendored
Normal file
Binary file not shown.
|
|
@ -15,7 +15,7 @@
|
|||
|
||||
|
||||
- (instancetype)initWithURL:(NSURL *)url;
|
||||
- (instancetype)initWithAsset:(TGMediaAsset *)asset;
|
||||
- (instancetype)initWithAsset:(TGMediaAsset *)asset livePhoto:(bool)livePhoto;
|
||||
|
||||
- (void)_cleanUp;
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -27,6 +27,8 @@
|
|||
+ (UIImage *)gifBackgroundImage;
|
||||
+ (UIImage *)gifIcon;
|
||||
+ (UIImage *)gifActiveIcon;
|
||||
+ (UIImage *)muteIcon;
|
||||
+ (UIImage *)muteActiveIcon;
|
||||
+ (UIImage *)qualityIconForPreset:(TGMediaVideoConversionPreset)preset;
|
||||
+ (UIImage *)timerIconForValue:(NSInteger)value;
|
||||
+ (UIImage *)eraserIcon;
|
||||
|
|
|
|||
|
|
@ -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];
|
||||
|
|
|
|||
|
|
@ -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];
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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];
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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])
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
Binary file not shown.
|
|
@ -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("")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue