mirror of
https://github.com/danog/Telegram.git
synced 2024-12-02 09:27:55 +01:00
482 lines
21 KiB
Objective-C
482 lines
21 KiB
Objective-C
#import "TGLegacyCameraController.h"
|
|
|
|
#import "TGHacks.h"
|
|
|
|
#import <AVFoundation/AVFoundation.h>
|
|
#import <MobileCoreServices/MobileCoreServices.h>
|
|
#import <CommonCrypto/CommonDigest.h>
|
|
|
|
#import "TGImageDownloadActor.h"
|
|
|
|
#import "TGImageUtils.h"
|
|
#import "TGProgressWindow.h"
|
|
|
|
#import "TGLegacyMediaPickerTipView.h"
|
|
|
|
#import "TGImagePickerController.h"
|
|
|
|
#import "TGAppDelegate.h"
|
|
|
|
@interface TGLegacyCameraController () <UINavigationControllerDelegate, UIImagePickerControllerDelegate>
|
|
{
|
|
TGProgressWindow *_progressWindow;
|
|
bool _didShowTip;
|
|
}
|
|
|
|
@end
|
|
|
|
@implementation TGLegacyCameraController
|
|
|
|
- (void)setAvatarMode:(bool)avatarMode
|
|
{
|
|
_avatarMode = avatarMode;
|
|
self.allowsEditing = _avatarMode;
|
|
}
|
|
|
|
- (void)loadView
|
|
{
|
|
[super loadView];
|
|
|
|
self.delegate = self;
|
|
}
|
|
|
|
- (UIStatusBarStyle)preferredStatusBarStyle
|
|
{
|
|
return UIStatusBarStyleDefault;
|
|
}
|
|
|
|
- (BOOL)prefersStatusBarHidden
|
|
{
|
|
return false;
|
|
}
|
|
|
|
- (BOOL)shouldAutorotate
|
|
{
|
|
return false;
|
|
}
|
|
|
|
- (void)viewDidLayoutSubviews
|
|
{
|
|
[super viewDidLayoutSubviews];
|
|
}
|
|
|
|
- (void)viewWillAppear:(BOOL)animated
|
|
{
|
|
if (!_didShowTip && _isInDocumentMode)
|
|
{
|
|
if (![[[NSUserDefaults standardUserDefaults] objectForKey:@"didShowDocumentPickerTip"] boolValue])
|
|
{
|
|
[[NSUserDefaults standardUserDefaults] setObject:@true forKey:@"didShowDocumentPickerTip"];
|
|
|
|
_didShowTip = true;
|
|
TGLegacyMediaPickerTipView *tipView = [[TGLegacyMediaPickerTipView alloc] initWithFrame:CGRectMake(0.0f, 0.0f, self.view.bounds.size.width, self.view.bounds.size.height)];
|
|
tipView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
|
|
[self.view addSubview:tipView];
|
|
}
|
|
}
|
|
|
|
if (iosMajorVersion() >= 7 && !_isInDocumentMode)
|
|
{
|
|
if (animated)
|
|
{
|
|
[UIView animateWithDuration:0.3 animations:^
|
|
{
|
|
[TGHacks setApplicationStatusBarAlpha:0.0f];
|
|
}];
|
|
}
|
|
else
|
|
[TGHacks setApplicationStatusBarAlpha:0.0f];
|
|
}
|
|
|
|
[super viewWillAppear:animated];
|
|
}
|
|
|
|
- (void)viewWillDisappear:(BOOL)animated
|
|
{
|
|
if (iosMajorVersion() >= 7 && !_isInDocumentMode)
|
|
{
|
|
if (animated)
|
|
{
|
|
[UIView animateWithDuration:0.3 animations:^
|
|
{
|
|
[TGHacks setApplicationStatusBarAlpha:1.0f];
|
|
}];
|
|
}
|
|
else
|
|
[TGHacks setApplicationStatusBarAlpha:1.0f];
|
|
}
|
|
|
|
[super viewWillDisappear:animated];
|
|
}
|
|
|
|
- (void)dealloc
|
|
{
|
|
if (_progressWindow != nil)
|
|
{
|
|
[_progressWindow dismiss:true];
|
|
_progressWindow = nil;
|
|
}
|
|
}
|
|
|
|
- (void)imagePickerControllerDidCancel:(UIImagePickerController *)__unused picker
|
|
{
|
|
id<TGLegacyCameraControllerDelegate> delegate = _completionDelegate;
|
|
[delegate legacyCameraControllerCompletedWithNoResult];
|
|
}
|
|
|
|
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info
|
|
{
|
|
NSString *mediaType = [info objectForKey:UIImagePickerControllerMediaType];
|
|
NSURL *referenceUrl = [info objectForKey:UIImagePickerControllerReferenceURL];
|
|
|
|
if ([mediaType isEqualToString:(__bridge NSString *)kUTTypeImage])
|
|
{
|
|
//if (picker.sourceType == UIImagePickerControllerSourceTypeCamera)
|
|
// defaultFlashMode = picker.cameraFlashMode;
|
|
|
|
|
|
if (_avatarMode)
|
|
{
|
|
CGRect cropRect = [[info objectForKey:UIImagePickerControllerCropRect] CGRectValue];
|
|
if (ABS(cropRect.size.width - cropRect.size.height) > FLT_EPSILON)
|
|
{
|
|
if (cropRect.size.width < cropRect.size.height)
|
|
{
|
|
cropRect.origin.x -= (cropRect.size.height - cropRect.size.width) / 2;
|
|
cropRect.size.width = cropRect.size.height;
|
|
}
|
|
else
|
|
{
|
|
cropRect.origin.y -= (cropRect.size.width - cropRect.size.height) / 2;
|
|
cropRect.size.height = cropRect.size.width;
|
|
}
|
|
}
|
|
|
|
id<TGLegacyCameraControllerDelegate> delegate = _completionDelegate;
|
|
UIImage *image = TGFixOrientationAndCrop([info objectForKey:UIImagePickerControllerOriginalImage], cropRect, CGSizeMake(600, 600));
|
|
if (image != nil)
|
|
[(id<TGImagePickerControllerDelegate>)delegate imagePickerController:nil didFinishPickingWithAssets:@[image]];
|
|
|
|
return;
|
|
}
|
|
|
|
id<TGLegacyCameraControllerDelegate> delegate = _completionDelegate;
|
|
if ([delegate conformsToProtocol:@protocol(TGImagePickerControllerDelegate)])
|
|
{
|
|
if (_isInDocumentMode)
|
|
{
|
|
NSURL *referenceUrl = info[UIImagePickerControllerReferenceURL];
|
|
if (referenceUrl != nil)
|
|
{
|
|
self.view.userInteractionEnabled = false;
|
|
|
|
id libraryToken = [TGImagePickerController preloadLibrary];
|
|
[TGImagePickerController loadAssetWithUrl:referenceUrl completion:^(ALAsset *asset)
|
|
{
|
|
if (asset != nil)
|
|
{
|
|
int64_t randomId = 0;
|
|
arc4random_buf(&randomId, sizeof(randomId));
|
|
NSString *tempFileName = [NSTemporaryDirectory() stringByAppendingPathComponent:[[NSString alloc] initWithFormat:@"%" PRIx64 ".bin", randomId]];
|
|
NSOutputStream *os = [[NSOutputStream alloc] initToFileAtPath:tempFileName append:false];
|
|
[os open];
|
|
|
|
ALAssetRepresentation *representation = asset.defaultRepresentation;
|
|
long long size = representation.size;
|
|
|
|
uint8_t buf[128 * 1024];
|
|
for (long long offset = 0; offset < size; offset += 128 * 1024)
|
|
{
|
|
long long batchSize = MIN(128 * 1024, size - offset);
|
|
NSUInteger readBytes = [representation getBytes:buf fromOffset:offset length:(NSUInteger)batchSize error:nil];
|
|
[os write:buf maxLength:readBytes];
|
|
}
|
|
|
|
[os close];
|
|
|
|
NSString *mimeType = (__bridge_transfer NSString*)UTTypeCopyPreferredTagWithClass((__bridge CFStringRef)[representation UTI], kUTTagClassMIMEType);
|
|
|
|
TGDispatchOnMainThread(^
|
|
{
|
|
[delegate legacyCameraControllerCompletedWithDocument:[NSURL fileURLWithPath:tempFileName] fileName:[representation filename] mimeType:mimeType];
|
|
});
|
|
}
|
|
else
|
|
{
|
|
TGDispatchOnMainThread(^
|
|
{
|
|
self.view.userInteractionEnabled = true;
|
|
});
|
|
}
|
|
|
|
[libraryToken class];
|
|
}];
|
|
}
|
|
}
|
|
else
|
|
{
|
|
UIImage *image = [info objectForKey:UIImagePickerControllerOriginalImage];
|
|
|
|
if (picker.sourceType == UIImagePickerControllerSourceTypeCamera && _storeCapturedAssets)
|
|
{
|
|
@autoreleasepool
|
|
{
|
|
UIImageWriteToSavedPhotosAlbum(image, nil, nil, NULL);
|
|
}
|
|
}
|
|
|
|
[(id<TGImagePickerControllerDelegate>)delegate imagePickerController:nil didFinishPickingWithAssets:@[image]];
|
|
}
|
|
}
|
|
}
|
|
else if ([mediaType isEqualToString:(__bridge NSString *)kUTTypeMovie])
|
|
{
|
|
id<TGLegacyCameraControllerDelegate> delegate = _completionDelegate;
|
|
if ([delegate conformsToProtocol:@protocol(TGImagePickerControllerDelegate)])
|
|
{
|
|
if (_isInDocumentMode)
|
|
{
|
|
NSURL *referenceUrl = info[UIImagePickerControllerReferenceURL];
|
|
if (referenceUrl != nil)
|
|
{
|
|
self.view.userInteractionEnabled = false;
|
|
|
|
id libraryToken = [TGImagePickerController preloadLibrary];
|
|
[TGImagePickerController loadAssetWithUrl:referenceUrl completion:^(ALAsset *asset)
|
|
{
|
|
if (asset != nil)
|
|
{
|
|
int64_t randomId = 0;
|
|
arc4random_buf(&randomId, sizeof(randomId));
|
|
NSString *tempFileName = [NSTemporaryDirectory() stringByAppendingPathComponent:[[NSString alloc] initWithFormat:@"%" PRIx64 ".bin", randomId]];
|
|
NSOutputStream *os = [[NSOutputStream alloc] initToFileAtPath:tempFileName append:false];
|
|
[os open];
|
|
|
|
ALAssetRepresentation *representation = asset.defaultRepresentation;
|
|
long long size = representation.size;
|
|
|
|
uint8_t buf[128 * 1024];
|
|
for (long long offset = 0; offset < size; offset += 128 * 1024)
|
|
{
|
|
long long batchSize = MIN(128 * 1024, size - offset);
|
|
NSUInteger readBytes = [representation getBytes:buf fromOffset:offset length:(NSUInteger)batchSize error:nil];
|
|
[os write:buf maxLength:readBytes];
|
|
}
|
|
|
|
[os close];
|
|
|
|
NSString *mimeType = (__bridge_transfer NSString*)UTTypeCopyPreferredTagWithClass((__bridge CFStringRef)[representation UTI], kUTTagClassMIMEType);
|
|
|
|
TGDispatchOnMainThread(^
|
|
{
|
|
[delegate legacyCameraControllerCompletedWithDocument:[NSURL fileURLWithPath:tempFileName] fileName:[representation filename] mimeType:mimeType];
|
|
});
|
|
}
|
|
else
|
|
{
|
|
TGDispatchOnMainThread(^
|
|
{
|
|
self.view.userInteractionEnabled = true;
|
|
});
|
|
}
|
|
|
|
[libraryToken class];
|
|
}];
|
|
}
|
|
}
|
|
else
|
|
{
|
|
_progressWindow = [[TGProgressWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
|
|
[_progressWindow show:true];
|
|
|
|
NSURL *mediaUrl = [info objectForKey:UIImagePickerControllerMediaURL];
|
|
|
|
NSString *assetHash = nil;
|
|
if (_storeCapturedAssets && [referenceUrl absoluteString].length != 0)
|
|
{
|
|
assetHash = [[NSString alloc] initWithFormat:@"%@", [referenceUrl absoluteString]];
|
|
TGLog(@"Video hash: %@", assetHash);
|
|
}
|
|
|
|
bool deleteFile = true;
|
|
|
|
if (picker.sourceType == UIImagePickerControllerSourceTypeCamera && _storeCapturedAssets)
|
|
{
|
|
UISaveVideoAtPathToSavedPhotosAlbum(mediaUrl.path, [self class], @selector(video:didFinishSavingWithError:contextInfo:), NULL);
|
|
deleteFile = false;
|
|
}
|
|
|
|
NSString *videosPath = [[TGAppDelegate documentsPath] stringByAppendingPathComponent:@"video"];
|
|
NSFileManager *fileManager = [NSFileManager defaultManager];
|
|
NSError *error = nil;
|
|
[fileManager createDirectoryAtPath:videosPath withIntermediateDirectories:true attributes:nil error:&error];
|
|
|
|
NSString *tmpPath = NSTemporaryDirectory();
|
|
|
|
int64_t fileId = 0;
|
|
arc4random_buf(&fileId, sizeof(fileId));
|
|
NSString *videoMp4FilePath = [tmpPath stringByAppendingPathComponent:[NSString stringWithFormat:@"%" PRId64 ".mp4", fileId]];
|
|
|
|
[ActionStageInstance() dispatchOnStageQueue:^
|
|
{
|
|
NSDictionary *existingData = [TGImageDownloadActor serverMediaDataForAssetUrl:assetHash];
|
|
if (existingData != nil)
|
|
{
|
|
TGDispatchOnMainThread(^
|
|
{
|
|
[_progressWindow dismiss:true];
|
|
_progressWindow = nil;
|
|
|
|
id<TGLegacyCameraControllerDelegate> delegate = _completionDelegate;
|
|
[delegate legacyCameraControllerCompletedWithExistingMedia:existingData[@"videoAttachment"]];
|
|
});
|
|
}
|
|
else
|
|
{
|
|
AVAsset *avAsset = [[AVURLAsset alloc] initWithURL:mediaUrl options:nil];
|
|
|
|
AVAssetExportSession *exportSession = [[AVAssetExportSession alloc] initWithAsset:avAsset presetName:AVAssetExportPresetPassthrough];
|
|
|
|
exportSession.outputURL = [NSURL fileURLWithPath:videoMp4FilePath];
|
|
exportSession.outputFileType = AVFileTypeMPEG4;
|
|
|
|
[exportSession exportAsynchronouslyWithCompletionHandler:^
|
|
{
|
|
bool endProcessing = false;
|
|
bool success = false;
|
|
|
|
switch ([exportSession status])
|
|
{
|
|
case AVAssetExportSessionStatusFailed:
|
|
NSLog(@"Export failed: %@", [[exportSession error] localizedDescription]);
|
|
endProcessing = true;
|
|
break;
|
|
case AVAssetExportSessionStatusCancelled:
|
|
endProcessing = true;
|
|
NSLog(@"Export canceled");
|
|
break;
|
|
case AVAssetExportSessionStatusCompleted:
|
|
{
|
|
TGLog(@"Export mp4 completed");
|
|
endProcessing = true;
|
|
success = true;
|
|
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (endProcessing)
|
|
{
|
|
if (deleteFile)
|
|
[fileManager removeItemAtURL:mediaUrl error:nil];
|
|
|
|
if (success)
|
|
{
|
|
AVAsset *mp4Asset = [AVAsset assetWithURL:[NSURL fileURLWithPath:videoMp4FilePath]];
|
|
|
|
AVAssetImageGenerator *imageGenerator = [[AVAssetImageGenerator alloc] initWithAsset:mp4Asset];
|
|
imageGenerator.maximumSize = CGSizeMake(800, 800);
|
|
imageGenerator.appliesPreferredTrackTransform = true;
|
|
NSError *imageError = nil;
|
|
CGImageRef imageRef = [imageGenerator copyCGImageAtTime:CMTimeMake(0, mp4Asset.duration.timescale) actualTime:NULL error:&imageError];
|
|
UIImage *previewImage = [[UIImage alloc] initWithCGImage:imageRef];
|
|
if (imageRef != NULL)
|
|
CGImageRelease(imageRef);
|
|
|
|
if (error == nil && [[mp4Asset tracksWithMediaType:AVMediaTypeVideo] count] > 0)
|
|
{
|
|
AVAssetTrack *track = [[mp4Asset tracksWithMediaType:AVMediaTypeVideo] objectAtIndex:0];
|
|
|
|
CGSize trackNaturalSize = track.naturalSize;
|
|
CGSize naturalSize = CGRectApplyAffineTransform(CGRectMake(0, 0, trackNaturalSize.width, trackNaturalSize.height), track.preferredTransform).size;
|
|
|
|
NSTimeInterval duration = CMTimeGetSeconds(mp4Asset.duration);
|
|
|
|
NSDictionary *finalFileAttributes = [[NSFileManager defaultManager] attributesOfItemAtPath:videoMp4FilePath error:nil];
|
|
int32_t fileSize = (int32_t)[[finalFileAttributes objectForKey:NSFileSize] intValue];
|
|
|
|
TGDispatchOnMainThread(^
|
|
{
|
|
[_progressWindow dismiss:true];
|
|
_progressWindow = nil;
|
|
|
|
id<TGLegacyCameraControllerDelegate> delegate = _completionDelegate;
|
|
[delegate legacyCameraControllerCapturedVideoWithTempFilePath:videoMp4FilePath fileSize:fileSize previewImage:previewImage duration:duration dimensions:naturalSize assetUrl:assetHash];
|
|
});
|
|
}
|
|
}
|
|
else
|
|
{
|
|
TGDispatchOnMainThread(^
|
|
{
|
|
[_progressWindow dismiss:true];
|
|
_progressWindow = nil;
|
|
|
|
id<TGLegacyCameraControllerDelegate> delegate = _completionDelegate;
|
|
[delegate legacyCameraControllerCompletedWithNoResult];
|
|
});
|
|
}
|
|
}
|
|
}];
|
|
}
|
|
}];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
- (NSString *)_dictionaryString:(NSDictionary *)dict
|
|
{
|
|
NSMutableString *string = [[NSMutableString alloc] init];
|
|
|
|
[dict enumerateKeysAndObjectsUsingBlock:^(id key, id value, __unused BOOL *stop)
|
|
{
|
|
if ([key isKindOfClass:[NSString class]])
|
|
[string appendString:key];
|
|
else if ([key isKindOfClass:[NSNumber class]])
|
|
[string appendString:[key description]];
|
|
[string appendString:@":"];
|
|
|
|
if ([value isKindOfClass:[NSString class]])
|
|
[string appendString:value];
|
|
else if ([value isKindOfClass:[NSNumber class]])
|
|
[string appendString:[value description]];
|
|
else if ([value isKindOfClass:[NSDictionary class]])
|
|
{
|
|
[string appendString:@"{"];
|
|
[string appendString:[self _dictionaryString:value]];
|
|
[string appendString:@"}"];
|
|
}
|
|
|
|
[string appendString:@";"];
|
|
}];
|
|
|
|
return string;
|
|
}
|
|
|
|
+ (void)video:(NSString *)videoPath didFinishSavingWithError:(NSError *)error contextInfo:(void *)__unused contextInfo
|
|
{
|
|
[[NSFileManager defaultManager] removeItemAtPath:videoPath error:nil];
|
|
if (error != nil)
|
|
TGLog(@"Video saving error: %@", error);
|
|
}
|
|
|
|
- (void)actionStageActionRequested:(NSString *)action options:(id)options
|
|
{
|
|
if ([action isEqualToString:@"imageCropResult"])
|
|
{
|
|
UIImage *image = options;
|
|
|
|
if ([options isKindOfClass:[UIImage class]])
|
|
{
|
|
id<TGLegacyCameraControllerDelegate> delegate = _completionDelegate;
|
|
if ([delegate conformsToProtocol:@protocol(TGImagePickerControllerDelegate)])
|
|
[(id<TGImagePickerControllerDelegate>)delegate imagePickerController:nil didFinishPickingWithAssets:@[image]];
|
|
}
|
|
}
|
|
}
|
|
|
|
@end
|