image_capture_device.mm 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298
  1. // Copyright 2014 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #import "components/storage_monitor/image_capture_device.h"
  5. #include "base/bind.h"
  6. #include "base/containers/adapters.h"
  7. #include "base/files/file_util.h"
  8. #include "base/task/task_traits.h"
  9. #include "base/task/thread_pool.h"
  10. #include "content/public/browser/browser_thread.h"
  11. namespace storage_monitor {
  12. namespace {
  13. base::File::Error RenameFile(const base::FilePath& downloaded_filename,
  14. const base::FilePath& desired_filename) {
  15. bool success = base::ReplaceFile(downloaded_filename, desired_filename, NULL);
  16. return success ? base::File::FILE_OK : base::File::FILE_ERROR_NOT_FOUND;
  17. }
  18. void ReturnRenameResultToListener(
  19. base::WeakPtr<ImageCaptureDeviceListener> listener,
  20. const std::string& name,
  21. const base::File::Error& result) {
  22. DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
  23. if (listener)
  24. listener->DownloadedFile(name, result);
  25. }
  26. base::Time NSDateToBaseTime(NSDate* date) {
  27. return base::Time::FromDoubleT([date timeIntervalSince1970]);
  28. }
  29. base::FilePath PathForCameraItem(ICCameraItem* item) {
  30. std::string name = base::SysNSStringToUTF8([item name]);
  31. std::vector<std::string> components;
  32. ICCameraFolder* folder = [item parentFolder];
  33. while (folder != nil) {
  34. components.push_back(base::SysNSStringToUTF8([folder name]));
  35. folder = [folder parentFolder];
  36. }
  37. base::FilePath path;
  38. for (const std::string& component : base::Reversed(components)) {
  39. path = path.Append(component);
  40. }
  41. path = path.Append(name);
  42. return path;
  43. }
  44. } // namespace
  45. } // namespace storage_monitor
  46. @implementation ImageCaptureDevice
  47. - (instancetype)initWithCameraDevice:(ICCameraDevice*)cameraDevice {
  48. if ((self = [super init])) {
  49. _camera.reset([cameraDevice retain]);
  50. [_camera setDelegate:self];
  51. _closing = false;
  52. }
  53. return self;
  54. }
  55. - (void)dealloc {
  56. // Make sure the session was closed and listener set to null
  57. // before destruction.
  58. DCHECK(![_camera delegate]);
  59. DCHECK(!_listener);
  60. [super dealloc];
  61. }
  62. - (void)setListener:(base::WeakPtr<storage_monitor::ImageCaptureDeviceListener>)
  63. listener {
  64. DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
  65. _listener = listener;
  66. }
  67. - (void)open {
  68. DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
  69. DCHECK(_listener);
  70. [_camera requestOpenSession];
  71. }
  72. - (void)close {
  73. DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
  74. _closing = true;
  75. [_camera cancelDownload];
  76. [_camera requestCloseSession];
  77. [_camera setDelegate:nil];
  78. _listener.reset();
  79. }
  80. - (void)eject {
  81. [_camera requestEjectOrDisconnect];
  82. }
  83. - (void)downloadFile:(const std::string&)name
  84. localPath:(const base::FilePath&)localPath {
  85. DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
  86. // Find the file with that name and start download.
  87. for (ICCameraItem* item in [_camera mediaFiles]) {
  88. std::string itemName = storage_monitor::PathForCameraItem(item).value();
  89. if (itemName == name) {
  90. // To create save options for ImageCapture, we need to
  91. // split the target filename into directory/name
  92. // and encode the directory as a URL.
  93. NSString* saveDirectory =
  94. base::mac::FilePathToNSString(localPath.DirName());
  95. NSString* saveFilename =
  96. base::mac::FilePathToNSString(localPath.BaseName());
  97. NSMutableDictionary* options =
  98. [NSMutableDictionary dictionaryWithCapacity:3];
  99. options[ICDownloadsDirectoryURL] =
  100. [NSURL fileURLWithPath:saveDirectory isDirectory:YES];
  101. options[ICSaveAsFilename] = saveFilename;
  102. options[ICOverwrite] = @YES;
  103. [_camera requestDownloadFile:base::mac::ObjCCastStrict<ICCameraFile>(item)
  104. options:options
  105. downloadDelegate:self
  106. didDownloadSelector:
  107. @selector(didDownloadFile:error:options:contextInfo:)
  108. contextInfo:NULL];
  109. return;
  110. }
  111. }
  112. if (_listener)
  113. _listener->DownloadedFile(name, base::File::FILE_ERROR_NOT_FOUND);
  114. }
  115. - (void)cameraDevice:(ICCameraDevice*)camera didAddItem:(ICCameraItem*)item {
  116. base::File::Info info;
  117. if ([[item UTI] isEqualToString:base::mac::CFToNSCast(kUTTypeFolder)])
  118. info.is_directory = true;
  119. else
  120. info.size = [base::mac::ObjCCastStrict<ICCameraFile>(item) fileSize];
  121. base::FilePath path = storage_monitor::PathForCameraItem(item);
  122. info.last_modified =
  123. storage_monitor::NSDateToBaseTime([item modificationDate]);
  124. info.creation_time = storage_monitor::NSDateToBaseTime([item creationDate]);
  125. info.last_accessed = info.last_modified;
  126. if (_listener)
  127. _listener->ItemAdded(path.value(), info);
  128. }
  129. - (void)cameraDevice:(ICCameraDevice*)camera didAddItems:(NSArray*)items {
  130. for (ICCameraItem* item in items)
  131. [self cameraDevice:camera didAddItem:item];
  132. }
  133. - (void)didRemoveDevice:(ICDevice*)device {
  134. device.delegate = NULL;
  135. if (_listener)
  136. _listener->DeviceRemoved();
  137. }
  138. // Notifies that a session was opened with the given device; potentially
  139. // with an error.
  140. - (void)device:(ICDevice*)device didOpenSessionWithError:(NSError*)error {
  141. if (error)
  142. [self didRemoveDevice:_camera];
  143. }
  144. - (void)device:(ICDevice*)device didEncounterError:(NSError*)error {
  145. if (error && _listener)
  146. _listener->DeviceRemoved();
  147. }
  148. // When this message is received, all media metadata is now loaded.
  149. - (void)deviceDidBecomeReadyWithCompleteContentCatalog:(ICDevice*)device {
  150. if (_listener)
  151. _listener->NoMoreItems();
  152. }
  153. - (void)didDownloadFile:(ICCameraFile*)file
  154. error:(NSError*)error
  155. options:(NSDictionary*)options
  156. contextInfo:(void*)contextInfo {
  157. if (_closing)
  158. return;
  159. std::string name = storage_monitor::PathForCameraItem(file).value();
  160. if (error) {
  161. DVLOG(1) << "error..."
  162. << base::SysNSStringToUTF8([error localizedDescription]);
  163. if (_listener)
  164. _listener->DownloadedFile(name, base::File::FILE_ERROR_FAILED);
  165. return;
  166. }
  167. std::string savedFilename = base::SysNSStringToUTF8(options[ICSavedFilename]);
  168. std::string saveAsFilename =
  169. base::SysNSStringToUTF8(options[ICSaveAsFilename]);
  170. if (savedFilename == saveAsFilename) {
  171. if (_listener)
  172. _listener->DownloadedFile(name, base::File::FILE_OK);
  173. return;
  174. }
  175. // ImageCapture did not save the file into the name we gave it in the
  176. // options. It picks a new name according to its best lights, so we need
  177. // to rename the file.
  178. base::FilePath saveDir(
  179. base::SysNSStringToUTF8([options[ICDownloadsDirectoryURL] path]));
  180. base::FilePath saveAsPath = saveDir.Append(saveAsFilename);
  181. base::FilePath savedPath = saveDir.Append(savedFilename);
  182. // Shared result value from file-copy closure to tell-listener closure.
  183. // This is worth blocking shutdown, as otherwise a file that has been
  184. // downloaded will be incorrectly named.
  185. base::ThreadPool::PostTaskAndReplyWithResult(
  186. FROM_HERE,
  187. {base::MayBlock(), base::TaskPriority::BEST_EFFORT,
  188. base::TaskShutdownBehavior::BLOCK_SHUTDOWN},
  189. base::BindOnce(&storage_monitor::RenameFile, savedPath, saveAsPath),
  190. base::BindOnce(&storage_monitor::ReturnRenameResultToListener, _listener,
  191. name));
  192. }
  193. // MacOS 10.14 SDK methods, not yet implemented (https://crbug.com/849689)
  194. - (void)cameraDevice:(ICCameraDevice*)camera
  195. didRenameItems:(NSArray<ICCameraItem*>*)items {
  196. NOTIMPLEMENTED();
  197. }
  198. - (void)cameraDevice:(ICCameraDevice*)camera didRemoveItem:(ICCameraItem*)item {
  199. NOTIMPLEMENTED();
  200. }
  201. - (void)cameraDevice:(ICCameraDevice*)camera
  202. didCompleteDeleteFilesWithError:(NSError*)error {
  203. NOTIMPLEMENTED();
  204. }
  205. - (void)cameraDeviceDidChangeCapability:(ICCameraDevice*)camera {
  206. NOTIMPLEMENTED();
  207. }
  208. - (void)cameraDevice:(ICCameraDevice*)camera
  209. didReceiveThumbnailForItem:(ICCameraItem*)item {
  210. NOTIMPLEMENTED();
  211. }
  212. - (void)cameraDevice:(ICCameraDevice*)camera
  213. didReceiveMetadataForItem:(ICCameraItem*)item {
  214. NOTIMPLEMENTED();
  215. }
  216. - (void)cameraDevice:(ICCameraDevice*)camera
  217. didReceivePTPEvent:(NSData*)eventData {
  218. NOTIMPLEMENTED();
  219. }
  220. // Mac 10.15 SDK methods, not yet implemented (https://crbug.com/849689)
  221. - (void)cameraDevice:(ICCameraDevice*)camera didRemoveItems:(NSArray*)items {
  222. NOTIMPLEMENTED();
  223. }
  224. - (void)cameraDevice:(ICCameraDevice*)camera
  225. didReceiveThumbnail:(CGImageRef)thumbnail
  226. forItem:(ICCameraItem*)item
  227. error:(NSError*)error {
  228. NOTIMPLEMENTED();
  229. }
  230. - (void)cameraDevice:(ICCameraDevice*)camera
  231. didReceiveMetadata:(NSDictionary*)metadata
  232. forItem:(ICCameraItem*)item
  233. error:(NSError*)error {
  234. NOTIMPLEMENTED();
  235. }
  236. - (void)cameraDeviceDidEnableAccessRestriction:(ICDevice*)device {
  237. NOTIMPLEMENTED();
  238. }
  239. - (void)cameraDeviceDidRemoveAccessRestriction:(ICDevice*)device {
  240. NOTIMPLEMENTED();
  241. }
  242. - (void)device:(ICDevice*)device didCloseSessionWithError:(NSError*)error {
  243. NOTIMPLEMENTED();
  244. }
  245. @end // ImageCaptureDevice