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.
5 #import "components/storage_monitor/image_capture_device.h"
7 #include "base/files/file_util.h"
8 #include "content/public/browser/browser_thread.h"
10 namespace storage_monitor {
14 base::File::Error RenameFile(const base::FilePath& downloaded_filename,
15 const base::FilePath& desired_filename) {
16 DCHECK_CURRENTLY_ON(content::BrowserThread::FILE);
17 bool success = base::ReplaceFile(downloaded_filename, desired_filename, NULL);
18 return success ? base::File::FILE_OK : base::File::FILE_ERROR_NOT_FOUND;
21 void ReturnRenameResultToListener(
22 base::WeakPtr<ImageCaptureDeviceListener> listener,
23 const std::string& name,
24 const base::File::Error& result) {
25 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
27 listener->DownloadedFile(name, result);
30 base::Time NSDateToBaseTime(NSDate* date) {
31 return base::Time::FromDoubleT([date timeIntervalSince1970]);
34 base::FilePath PathForCameraItem(ICCameraItem* item) {
35 std::string name = base::SysNSStringToUTF8([item name]);
37 std::vector<std::string> components;
38 ICCameraFolder* folder = [item parentFolder];
39 while (folder != nil) {
40 components.push_back(base::SysNSStringToUTF8([folder name]));
41 folder = [folder parentFolder];
44 for (std::vector<std::string>::reverse_iterator i = components.rbegin();
45 i != components.rend(); ++i) {
46 path = path.Append(*i);
48 path = path.Append(name);
55 } // namespace storage_monitor
57 @implementation ImageCaptureDevice
59 - (id)initWithCameraDevice:(ICCameraDevice*)cameraDevice {
60 if ((self = [super init])) {
61 camera_.reset([cameraDevice retain]);
62 [camera_ setDelegate:self];
69 // Make sure the session was closed and listener set to null
70 // before destruction.
71 DCHECK(![camera_ delegate]);
76 - (void)setListener:(base::WeakPtr<storage_monitor::ImageCaptureDeviceListener>)
78 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
83 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
85 [camera_ requestOpenSession];
89 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
91 [camera_ cancelDownload];
92 [camera_ requestCloseSession];
93 [camera_ setDelegate:nil];
98 [camera_ requestEjectOrDisconnect];
101 - (void)downloadFile:(const std::string&)name
102 localPath:(const base::FilePath&)localPath {
103 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
105 // Find the file with that name and start download.
106 for (ICCameraItem* item in [camera_ mediaFiles]) {
107 std::string itemName = storage_monitor::PathForCameraItem(item).value();
108 if (itemName == name) {
109 // To create save options for ImageCapture, we need to
110 // split the target filename into directory/name
111 // and encode the directory as a URL.
112 NSString* saveDirectory =
113 base::mac::FilePathToNSString(localPath.DirName());
114 NSString* saveFilename =
115 base::mac::FilePathToNSString(localPath.BaseName());
117 NSMutableDictionary* options =
118 [NSMutableDictionary dictionaryWithCapacity:3];
119 [options setObject:[NSURL fileURLWithPath:saveDirectory isDirectory:YES]
120 forKey:ICDownloadsDirectoryURL];
121 [options setObject:saveFilename forKey:ICSaveAsFilename];
122 [options setObject:[NSNumber numberWithBool:YES] forKey:ICOverwrite];
124 [camera_ requestDownloadFile:base::mac::ObjCCastStrict<ICCameraFile>(item)
126 downloadDelegate:self
128 @selector(didDownloadFile:error:options:contextInfo:)
135 listener_->DownloadedFile(name, base::File::FILE_ERROR_NOT_FOUND);
138 - (void)cameraDevice:(ICCameraDevice*)camera didAddItem:(ICCameraItem*)item {
139 base::File::Info info;
140 if ([[item UTI] isEqualToString:base::mac::CFToNSCast(kUTTypeFolder)])
141 info.is_directory = true;
143 info.size = [base::mac::ObjCCastStrict<ICCameraFile>(item) fileSize];
145 base::FilePath path = storage_monitor::PathForCameraItem(item);
148 storage_monitor::NSDateToBaseTime([item modificationDate]);
149 info.creation_time = storage_monitor::NSDateToBaseTime([item creationDate]);
150 info.last_accessed = info.last_modified;
153 listener_->ItemAdded(path.value(), info);
156 - (void)cameraDevice:(ICCameraDevice*)camera didAddItems:(NSArray*)items {
157 for (ICCameraItem* item in items)
158 [self cameraDevice:camera didAddItem:item];
161 - (void)didRemoveDevice:(ICDevice*)device {
162 device.delegate = NULL;
164 listener_->DeviceRemoved();
167 // Notifies that a session was opened with the given device; potentially
169 - (void)device:(ICDevice*)device didOpenSessionWithError:(NSError*)error {
171 [self didRemoveDevice:camera_];
174 - (void)device:(ICDevice*)device didEncounterError:(NSError*)error {
175 if (error && listener_)
176 listener_->DeviceRemoved();
179 // When this message is received, all media metadata is now loaded.
180 - (void)deviceDidBecomeReadyWithCompleteContentCatalog:(ICDevice*)device {
182 listener_->NoMoreItems();
185 - (void)didDownloadFile:(ICCameraFile*)file
186 error:(NSError*)error
187 options:(NSDictionary*)options
188 contextInfo:(void*)contextInfo {
192 std::string name = storage_monitor::PathForCameraItem(file).value();
195 DVLOG(1) << "error..."
196 << base::SysNSStringToUTF8([error localizedDescription]);
198 listener_->DownloadedFile(name, base::File::FILE_ERROR_FAILED);
202 std::string savedFilename =
203 base::SysNSStringToUTF8([options objectForKey:ICSavedFilename]);
204 std::string saveAsFilename =
205 base::SysNSStringToUTF8([options objectForKey:ICSaveAsFilename]);
206 if (savedFilename == saveAsFilename) {
208 listener_->DownloadedFile(name, base::File::FILE_OK);
212 // ImageCapture did not save the file into the name we gave it in the
213 // options. It picks a new name according to its best lights, so we need
214 // to rename the file.
215 base::FilePath saveDir(base::SysNSStringToUTF8(
216 [[options objectForKey:ICDownloadsDirectoryURL] path]));
217 base::FilePath saveAsPath = saveDir.Append(saveAsFilename);
218 base::FilePath savedPath = saveDir.Append(savedFilename);
219 // Shared result value from file-copy closure to tell-listener closure.
220 content::BrowserThread::PostTaskAndReplyWithResult(
221 content::BrowserThread::FILE,
223 base::Bind(&storage_monitor::RenameFile, savedPath, saveAsPath),
225 &storage_monitor::ReturnRenameResultToListener, listener_, name));
228 @end // ImageCaptureDevice