ShareItemController.m 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245
  1. /**
  2. * SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors
  3. * SPDX-License-Identifier: GPL-3.0-or-later
  4. */
  5. #import <MobileCoreServices/MobileCoreServices.h>
  6. #import "ShareItemController.h"
  7. #import "NextcloudTalk-Swift.h"
  8. //TODO: Should the quality be user-selectable?
  9. CGFloat const kShareItemControllerImageQuality = 0.7f;
  10. @interface ShareItemController ()
  11. @property (nonatomic, strong) NSString *tempDirectoryPath;
  12. @property (nonatomic, strong) NSURL *tempDirectoryURL;
  13. @property (nonatomic, strong) NSMutableArray *internalShareItems;
  14. @end
  15. @implementation ShareItemController
  16. - (instancetype)init
  17. {
  18. self = [super init];
  19. if (self) {
  20. self.internalShareItems = [[NSMutableArray alloc] init];
  21. [self initTempDirectory];
  22. }
  23. return self;
  24. }
  25. - (NSArray<ShareItem *> *)shareItems {
  26. return [self.internalShareItems copy];
  27. }
  28. - (void)initTempDirectory
  29. {
  30. NSFileManager *fileManager = [NSFileManager defaultManager];
  31. self.tempDirectoryPath = [NSTemporaryDirectory() stringByAppendingPathComponent:@"/upload/"];
  32. if (![fileManager fileExistsAtPath:self.tempDirectoryPath]) {
  33. // Make sure our upload directory exists
  34. [fileManager createDirectoryAtPath:self.tempDirectoryPath withIntermediateDirectories:YES attributes:nil error:nil];
  35. } else {
  36. // Clean up any temporary files from a previous upload
  37. NSArray *previousFiles = [fileManager contentsOfDirectoryAtPath:self.tempDirectoryPath error:nil];
  38. for (NSString *previousFile in previousFiles) {
  39. [fileManager removeItemAtPath:[self.tempDirectoryPath stringByAppendingPathComponent:previousFile] error:nil];
  40. }
  41. }
  42. self.tempDirectoryURL = [NSURL fileURLWithPath:self.tempDirectoryPath isDirectory:YES];
  43. }
  44. - (NSURL *)getFileLocalURL:(NSString *)fileName
  45. {
  46. NSURL *fileLocalURL = [self.tempDirectoryURL URLByAppendingPathComponent:fileName];
  47. if ([NSFileManager.defaultManager fileExistsAtPath:fileLocalURL.path]) {
  48. NSString *extension = [fileName pathExtension];
  49. NSString *nameWithoutExtension = [fileName stringByDeletingPathExtension];
  50. NSString *newFileName = [NSString stringWithFormat:@"%@%.f.%@", nameWithoutExtension, [[NSDate date] timeIntervalSince1970] * 1000, extension];
  51. fileLocalURL = [self.tempDirectoryURL URLByAppendingPathComponent:newFileName];
  52. }
  53. return fileLocalURL;
  54. }
  55. - (void)addItemWithURL:(NSURL *)fileURL
  56. {
  57. [self addItemWithURLAndName:fileURL withName:fileURL.lastPathComponent];
  58. }
  59. - (BOOL)prepareFileForUploadingAtURL:(NSURL *)fileURL toLocalURL:(NSURL *)fileLocalURL withCoordinatorOption:(NSFileCoordinatorReadingOptions)options
  60. {
  61. NSFileCoordinator *coordinator = [[NSFileCoordinator alloc] initWithFilePresenter:nil];
  62. __block NSError *error;
  63. // Make a local copy to prevent bug where file is removed after some time from inbox
  64. // See: https://stackoverflow.com/a/48007752/2512312
  65. [coordinator coordinateReadingItemAtURL:fileURL options:options error:&error byAccessor:^(NSURL *newURL) {
  66. if ([NSFileManager.defaultManager fileExistsAtPath:fileLocalURL.path]) {
  67. [NSFileManager.defaultManager removeItemAtPath:fileLocalURL.path error:nil];
  68. }
  69. [NSFileManager.defaultManager moveItemAtPath:newURL.path toPath:fileLocalURL.path error:nil];
  70. }];
  71. BOOL success = (error == nil);
  72. return success;
  73. }
  74. - (void)addItemWithURLAndName:(NSURL *)fileURL withName:(NSString *)fileName
  75. {
  76. NSURL *fileLocalURL = [self getFileLocalURL:fileName];
  77. // First try to prepare the file with NSFileCoordinatorReadingForUploading
  78. BOOL preparedSuccessfully = [self prepareFileForUploadingAtURL:fileURL toLocalURL:fileLocalURL withCoordinatorOption:NSFileCoordinatorReadingForUploading];
  79. if (!preparedSuccessfully) {
  80. // We failed to prepare the file with NSFileCoordinatorReadingForUploading, use NSFileCoordinatorReadingWithoutChanges as a fallback
  81. preparedSuccessfully = [self prepareFileForUploadingAtURL:fileURL toLocalURL:fileLocalURL withCoordinatorOption:NSFileCoordinatorReadingWithoutChanges];
  82. if (!preparedSuccessfully) {
  83. NSLog(@"Failed to prepare file for sharing");
  84. return;
  85. }
  86. }
  87. NSLog(@"Adding shareItem: %@ %@", fileName, fileLocalURL);
  88. // Try to determine if the item is an image file
  89. // This can happen when sharing an image from the native ios files app
  90. NSString *extension = fileLocalURL.pathExtension;
  91. BOOL fileIsImage = (extension && [NCUtils isImageWithFileExtension:extension]);
  92. ShareItem* item = [ShareItem initWithURL:fileLocalURL withName:fileName withPlaceholderImage:[self getPlaceholderImageForFileURL:fileLocalURL] isImage:fileIsImage];
  93. [self.internalShareItems addObject:item];
  94. [self.delegate shareItemControllerItemsChanged:self];
  95. }
  96. - (void)addItemWithImage:(UIImage *)image
  97. {
  98. NSString *imageName = [NSString stringWithFormat:@"IMG_%.f.jpg", [[NSDate date] timeIntervalSince1970] * 1000];
  99. [self addItemWithImageAndName:image withName:imageName];
  100. }
  101. - (void)addItemWithImageAndName:(UIImage *)image withName:(NSString *)imageName
  102. {
  103. NSURL *fileLocalURL = [self getFileLocalURL:imageName];
  104. NSData *jpegData = UIImageJPEGRepresentation(image, kShareItemControllerImageQuality);
  105. [jpegData writeToFile:fileLocalURL.path atomically:YES];
  106. NSLog(@"Adding shareItem with image: %@ %@", imageName, fileLocalURL);
  107. ShareItem* item = [ShareItem initWithURL:fileLocalURL withName:imageName withPlaceholderImage:[self getPlaceholderImageForFileURL:fileLocalURL] isImage:YES];
  108. [self.internalShareItems addObject:item];
  109. [self.delegate shareItemControllerItemsChanged:self];
  110. }
  111. - (UIImage *)getImageFromItem:(ShareItem *)item
  112. {
  113. if (!item || !item.fileURL) {
  114. return nil;
  115. }
  116. return [UIImage imageWithContentsOfFile:item.filePath];
  117. }
  118. - (void)addItemWithContactData:(NSData *)data
  119. {
  120. NSString *vCardFileName = [NSString stringWithFormat:@"Contact_%.f.vcf", [[NSDate date] timeIntervalSince1970] * 1000];
  121. [self addItemWithContactDataAndName:data withName:vCardFileName];
  122. }
  123. - (void)addItemWithContactDataAndName:(NSData *)data withName:(NSString *)vCardFileName
  124. {
  125. NSURL *fileLocalURL = [self getFileLocalURL:vCardFileName];
  126. NSString* vcString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
  127. [vcString writeToFile:fileLocalURL.path atomically:YES encoding:NSUTF8StringEncoding error:nil];
  128. NSLog(@"Adding shareItem with contact: %@ %@", vCardFileName, fileLocalURL);
  129. ShareItem* item = [ShareItem initWithURL:fileLocalURL withName:vCardFileName withPlaceholderImage:[self getPlaceholderImageForFileURL:fileLocalURL] isImage:YES];
  130. [self.internalShareItems addObject:item];
  131. [self.delegate shareItemControllerItemsChanged:self];
  132. }
  133. - (void)updateItem:(ShareItem *)item withURL:(NSURL *)fileURL
  134. {
  135. // This is called when an item was edited in quicklook and we want to use the edited image
  136. NSFileCoordinator *coordinator = [[NSFileCoordinator alloc] initWithFilePresenter:nil];
  137. __block NSError *error;
  138. [coordinator coordinateReadingItemAtURL:fileURL options:NSFileCoordinatorReadingForUploading error:&error byAccessor:^(NSURL *newURL) {
  139. if ([NSFileManager.defaultManager fileExistsAtPath:item.filePath]) {
  140. [NSFileManager.defaultManager removeItemAtPath:item.filePath error:nil];
  141. }
  142. [NSFileManager.defaultManager moveItemAtPath:newURL.path toPath:item.filePath error:nil];
  143. }];
  144. NSLog(@"Updating shareItem: %@ %@", item.fileName, item.fileURL);
  145. [self.delegate shareItemControllerItemsChanged:self];
  146. }
  147. - (void)updateItem:(ShareItem *)item withImage:(UIImage *)image
  148. {
  149. NSData *jpegData = UIImageJPEGRepresentation(image, kShareItemControllerImageQuality);
  150. [jpegData writeToFile:item.filePath atomically:YES];
  151. NSLog(@"Updating shareItem with Image: %@ %@", item.fileName, item.fileURL);
  152. [self.delegate shareItemControllerItemsChanged:self];
  153. }
  154. - (void)removeItem:(ShareItem *)item
  155. {
  156. [self removeItems:@[item]];
  157. }
  158. - (void)removeItems:(NSArray<ShareItem *> *)items
  159. {
  160. for (ShareItem *item in items) {
  161. [self cleanupItem:item];
  162. NSLog(@"Removing shareItem: %@ %@", item.fileName, item.fileURL);
  163. [self.internalShareItems removeObject:item];
  164. }
  165. [self.delegate shareItemControllerItemsChanged:self];
  166. }
  167. - (void)cleanupItem:(ShareItem *)item
  168. {
  169. if ([NSFileManager.defaultManager fileExistsAtPath:item.filePath]) {
  170. [NSFileManager.defaultManager removeItemAtPath:item.filePath error:nil];
  171. }
  172. }
  173. - (void)removeAllItems
  174. {
  175. for (ShareItem *item in self.internalShareItems) {
  176. [self cleanupItem:item];
  177. }
  178. [self.internalShareItems removeAllObjects];
  179. }
  180. - (UIImage *)getPlaceholderImageForFileURL:(NSURL *)fileURL
  181. {
  182. NSString *previewImage = [NCUtils previewImageForFileExtension:[fileURL pathExtension]];
  183. return [UIImage imageNamed:previewImage];
  184. }
  185. @end