URLSenderItem.m 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227
  1. // _____ _
  2. // |_ _| |_ _ _ ___ ___ _ __ __ _
  3. // | | | ' \| '_/ -_) -_) ' \/ _` |_
  4. // |_| |_||_|_| \___\___|_|_|_\__,_(_)
  5. //
  6. // Threema iOS Client
  7. // Copyright (c) 2015-2020 Threema GmbH
  8. //
  9. // This program is free software: you can redistribute it and/or modify
  10. // it under the terms of the GNU Affero General Public License, version 3,
  11. // as published by the Free Software Foundation.
  12. //
  13. // This program is distributed in the hope that it will be useful,
  14. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16. // GNU Affero General Public License for more details.
  17. //
  18. // You should have received a copy of the GNU Affero General Public License
  19. // along with this program. If not, see <https://www.gnu.org/licenses/>.
  20. #import "URLSenderItem.h"
  21. #import "UTIConverter.h"
  22. #import "UserSettings.h"
  23. #import "ContactUtil.h"
  24. #import "FLAnimatedImage.h"
  25. #import "MediaConverter.h"
  26. #ifdef DEBUG
  27. static const DDLogLevel ddLogLevel = DDLogLevelVerbose;
  28. #else
  29. static const DDLogLevel ddLogLevel = DDLogLevelWarning;
  30. #endif
  31. @interface URLSenderItem ()
  32. @property NSData *data;
  33. @property NSString *fileName;
  34. @end
  35. @implementation URLSenderItem
  36. +(instancetype)itemWithUrl:(NSURL *)url type:(NSString *)type renderType:(NSNumber *)renderType sendAsFile:(BOOL)sendAsFile {
  37. URLSenderItem *item = [[URLSenderItem alloc] initWithUrl:url type:type renderType:renderType sendAsFile:sendAsFile];
  38. return item;
  39. }
  40. +(instancetype)itemWithData:(NSData *)data fileName:(NSString *)fileName type:(NSString *)type renderType:(NSNumber *)renderType sendAsFile:(BOOL)sendAsFile {
  41. URLSenderItem *item = [[URLSenderItem alloc] initWithData:data fileName:fileName type:type renderType:renderType sendAsFile:sendAsFile];
  42. return item;
  43. }
  44. - (instancetype)initWithUrl:(NSURL *)url type:(NSString *)type renderType:(NSNumber *)renderType sendAsFile:(BOOL)sendAsFile {
  45. self = [super init];
  46. if (self) {
  47. _url = url;
  48. _type = type;
  49. _sendAsFile = sendAsFile;
  50. _renderType = renderType;
  51. }
  52. return self;
  53. }
  54. - (instancetype)initWithData:(NSData *)data fileName:(NSString *)fileName type:(NSString *)type renderType:(NSNumber *)renderType sendAsFile:(BOOL)sendAsFile {
  55. self = [super init];
  56. if (self) {
  57. _data = data;
  58. _type = type;
  59. _sendAsFile = sendAsFile;
  60. _renderType = renderType;
  61. _fileName = fileName;
  62. }
  63. return self;
  64. }
  65. - (NSData *)getData {
  66. if (_url) {
  67. return [[NSData alloc] initWithContentsOfURL:_url];
  68. } else {
  69. return _data;
  70. }
  71. }
  72. - (NSString *)getName {
  73. if (_url) {
  74. return [_url lastPathComponent];
  75. } else {
  76. if ([_type isEqualToString:UTTYPE_VCARD]) {
  77. return [self getContactFileName];
  78. }
  79. if (_fileName != nil) {
  80. return _fileName;
  81. }
  82. NSString *postfix = [UTIConverter preferedFileExtensionForMimeType:[self getMimeType]];
  83. return [NSString stringWithFormat:@"file.%@", postfix];
  84. }
  85. }
  86. - (NSString *)getMimeType {
  87. NSString *mimeType = [UTIConverter mimeTypeFromUTI:_type];
  88. if ([mimeType isEqualToString:@"application/octet-stream"] && _url != nil) {
  89. NSString *uti = [UTIConverter utiForFileURL:_url];
  90. mimeType = [UTIConverter mimeTypeFromUTI:uti];
  91. }
  92. // Hack for m4a files shared via UIDocumentInteractionController
  93. if ([mimeType isEqualToString:@"application/octet-stream"] && [_url.pathExtension isEqualToString:@"m4a"]) {
  94. mimeType = @"audio/mp4";
  95. }
  96. // Hack for public.image files shared via screen shots on iOS
  97. if ([mimeType isEqualToString:@"application/octet-stream"] && [_type isEqualToString:@"public.image"]) {
  98. mimeType = @"image/jpeg";
  99. }
  100. return mimeType;
  101. }
  102. - (UIImage *)getThumbnail {
  103. if ([UTIConverter isGifMimeType:[self getMimeType]]) {
  104. FLAnimatedImage *animImage = [FLAnimatedImage animatedImageWithGIFData:[self getData]];
  105. UIImage *thumbnail = [MediaConverter getThumbnailForImage:animImage.posterImage];
  106. return thumbnail;
  107. } else if ([self.type isEqualToString:@"image/gif"]) {
  108. FLAnimatedImage *animImage = [FLAnimatedImage animatedImageWithGIFData:[self getData]];
  109. UIImage *thumbnail = [MediaConverter getThumbnailForImage:animImage.posterImage];
  110. return thumbnail;
  111. }
  112. if ([UTIConverter isRenderingImageMimeType:[self getMimeType]]) {
  113. NSData *data = [self getData];
  114. if (data == nil) {
  115. return nil;
  116. }
  117. UIImage *originalImage = [UIImage imageWithData:data];
  118. UIImage *thumbnaiil = [MediaConverter getThumbnailForImage:originalImage];
  119. return thumbnaiil;
  120. }
  121. if ([UTIConverter isRenderingVideoMimeType:[self getMimeType]]) {
  122. if (self.url == nil) {
  123. NSString *tmpPath = [NSString stringWithFormat:@"%@video.mp4", NSTemporaryDirectory()];
  124. if ([[NSFileManager defaultManager] fileExistsAtPath:tmpPath]) {
  125. NSError *error;
  126. [[NSFileManager defaultManager] removeItemAtPath:tmpPath error:&error];
  127. if (error != nil) {
  128. DDLogError(@"Can't delete file at path %@", tmpPath);
  129. }
  130. }
  131. [[NSFileManager defaultManager] createFileAtPath:tmpPath contents:[self getData] attributes:nil];
  132. _url = [[NSURL alloc] initFileURLWithPath:tmpPath];
  133. }
  134. AVURLAsset *asset = [[AVURLAsset alloc] initWithURL:self.url options:nil];
  135. AVAssetImageGenerator *gen = [[AVAssetImageGenerator alloc] initWithAsset:asset];
  136. gen.appliesPreferredTrackTransform = YES;
  137. CMTime time = CMTimeMakeWithSeconds(0.0, 600);
  138. NSError *error = nil;
  139. CMTime actualTime;
  140. CGImageRef image = [gen copyCGImageAtTime:time actualTime:&actualTime error:&error];
  141. UIImage *thumbnail = [[UIImage alloc] initWithCGImage:image];
  142. CGImageRelease(image);
  143. thumbnail = [MediaConverter getThumbnailForImage:thumbnail];
  144. return thumbnail;
  145. }
  146. return nil;
  147. }
  148. - (NSString *)getContactFileName {
  149. NSString *name = [ContactUtil getNameFromVCardData:_data];
  150. if (name.length < 1) {
  151. return @"contact.vcf";
  152. }
  153. return [NSString stringWithFormat:@"%@.vcf", name];
  154. }
  155. - (CGFloat)getDuration {
  156. if ([UTIConverter isRenderingVideoMimeType:[self getMimeType]]) {
  157. /* Find duration and make thumbnail */
  158. AVURLAsset *asset = [[AVURLAsset alloc] initWithURL:self.url options:nil];
  159. return CMTimeGetSeconds(asset.duration);
  160. }
  161. if ([UTIConverter isRenderingAudioMimeType:[self getMimeType]]) {
  162. /* Find duration and make thumbnail */
  163. AVURLAsset *asset = [[AVURLAsset alloc] initWithURL:self.url options:nil];
  164. return CMTimeGetSeconds(asset.duration);
  165. }
  166. return 0.0;
  167. }
  168. - (CGFloat)getHeight {
  169. if ([UTIConverter isImageMimeType:[self getMimeType]]) {
  170. UIImage *originalImage = [UIImage imageWithData:[self getData]];
  171. return originalImage.size.height;
  172. }
  173. if ([UTIConverter isVideoMimeType:[self getMimeType]]) {
  174. AVURLAsset *asset = [[AVURLAsset alloc] initWithURL:self.url options:nil];
  175. AVAssetTrack *clipVideoTrack = [[asset tracksWithMediaType:AVMediaTypeVideo] objectAtIndex:0];
  176. return clipVideoTrack.naturalSize.height;
  177. }
  178. return 0.0;
  179. }
  180. - (CGFloat)getWidth {
  181. if ([UTIConverter isImageMimeType:[self getMimeType]]) {
  182. UIImage *originalImage = [UIImage imageWithData:[self getData]];
  183. return originalImage.size.width;
  184. }
  185. if ([UTIConverter isVideoMimeType:[self getMimeType]]) {
  186. AVURLAsset *asset = [[AVURLAsset alloc] initWithURL:self.url options:nil];
  187. AVAssetTrack *clipVideoTrack = [[asset tracksWithMediaType:AVMediaTypeVideo] objectAtIndex:0];
  188. return clipVideoTrack.naturalSize.width;
  189. }
  190. return 0.0;
  191. }
  192. @end