// Copyright 2014 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #import "components/storage_monitor/image_capture_device.h" #include "base/files/file_util.h" #include "content/public/browser/browser_thread.h" namespace storage_monitor { namespace { base::File::Error RenameFile(const base::FilePath& downloaded_filename, const base::FilePath& desired_filename) { DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::FILE)); bool success = base::ReplaceFile(downloaded_filename, desired_filename, NULL); return success ? base::File::FILE_OK : base::File::FILE_ERROR_NOT_FOUND; } void ReturnRenameResultToListener( base::WeakPtr listener, const std::string& name, const base::File::Error& result) { DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); if (listener) listener->DownloadedFile(name, result); } base::Time NSDateToBaseTime(NSDate* date) { return base::Time::FromDoubleT([date timeIntervalSince1970]); } base::FilePath PathForCameraItem(ICCameraItem* item) { std::string name = base::SysNSStringToUTF8([item name]); std::vector components; ICCameraFolder* folder = [item parentFolder]; while (folder != nil) { components.push_back(base::SysNSStringToUTF8([folder name])); folder = [folder parentFolder]; } base::FilePath path; for (std::vector::reverse_iterator i = components.rbegin(); i != components.rend(); ++i) { path = path.Append(*i); } path = path.Append(name); return path; } } // namespace } // namespace storage_monitor @implementation ImageCaptureDevice - (id)initWithCameraDevice:(ICCameraDevice*)cameraDevice { if ((self = [super init])) { camera_.reset([cameraDevice retain]); [camera_ setDelegate:self]; closing_ = false; } return self; } - (void)dealloc { // Make sure the session was closed and listener set to null // before destruction. DCHECK(![camera_ delegate]); DCHECK(!listener_); [super dealloc]; } - (void)setListener:(base::WeakPtr) listener { DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); listener_ = listener; } - (void)open { DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); DCHECK(listener_); [camera_ requestOpenSession]; } - (void)close { DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); closing_ = true; [camera_ cancelDownload]; [camera_ requestCloseSession]; [camera_ setDelegate:nil]; listener_.reset(); } - (void)eject { [camera_ requestEjectOrDisconnect]; } - (void)downloadFile:(const std::string&)name localPath:(const base::FilePath&)localPath { DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); // Find the file with that name and start download. for (ICCameraItem* item in [camera_ mediaFiles]) { std::string itemName = storage_monitor::PathForCameraItem(item).value(); if (itemName == name) { // To create save options for ImageCapture, we need to // split the target filename into directory/name // and encode the directory as a URL. NSString* saveDirectory = base::mac::FilePathToNSString(localPath.DirName()); NSString* saveFilename = base::mac::FilePathToNSString(localPath.BaseName()); NSMutableDictionary* options = [NSMutableDictionary dictionaryWithCapacity:3]; [options setObject:[NSURL fileURLWithPath:saveDirectory isDirectory:YES] forKey:ICDownloadsDirectoryURL]; [options setObject:saveFilename forKey:ICSaveAsFilename]; [options setObject:[NSNumber numberWithBool:YES] forKey:ICOverwrite]; [camera_ requestDownloadFile:base::mac::ObjCCastStrict(item) options:options downloadDelegate:self didDownloadSelector: @selector(didDownloadFile:error:options:contextInfo:) contextInfo:NULL]; return; } } if (listener_) listener_->DownloadedFile(name, base::File::FILE_ERROR_NOT_FOUND); } - (void)cameraDevice:(ICCameraDevice*)camera didAddItem:(ICCameraItem*)item { base::File::Info info; if ([[item UTI] isEqualToString:base::mac::CFToNSCast(kUTTypeFolder)]) info.is_directory = true; else info.size = [base::mac::ObjCCastStrict(item) fileSize]; base::FilePath path = storage_monitor::PathForCameraItem(item); info.last_modified = storage_monitor::NSDateToBaseTime([item modificationDate]); info.creation_time = storage_monitor::NSDateToBaseTime([item creationDate]); info.last_accessed = info.last_modified; if (listener_) listener_->ItemAdded(path.value(), info); } - (void)cameraDevice:(ICCameraDevice*)camera didAddItems:(NSArray*)items { for (ICCameraItem* item in items) [self cameraDevice:camera didAddItem:item]; } - (void)didRemoveDevice:(ICDevice*)device { device.delegate = NULL; if (listener_) listener_->DeviceRemoved(); } // Notifies that a session was opened with the given device; potentially // with an error. - (void)device:(ICDevice*)device didOpenSessionWithError:(NSError*)error { if (error) [self didRemoveDevice:camera_]; } - (void)device:(ICDevice*)device didEncounterError:(NSError*)error { if (error && listener_) listener_->DeviceRemoved(); } // When this message is received, all media metadata is now loaded. - (void)deviceDidBecomeReadyWithCompleteContentCatalog:(ICDevice*)device { if (listener_) listener_->NoMoreItems(); } - (void)didDownloadFile:(ICCameraFile*)file error:(NSError*)error options:(NSDictionary*)options contextInfo:(void*)contextInfo { if (closing_) return; std::string name = storage_monitor::PathForCameraItem(file).value(); if (error) { DVLOG(1) << "error..." << base::SysNSStringToUTF8([error localizedDescription]); if (listener_) listener_->DownloadedFile(name, base::File::FILE_ERROR_FAILED); return; } std::string savedFilename = base::SysNSStringToUTF8([options objectForKey:ICSavedFilename]); std::string saveAsFilename = base::SysNSStringToUTF8([options objectForKey:ICSaveAsFilename]); if (savedFilename == saveAsFilename) { if (listener_) listener_->DownloadedFile(name, base::File::FILE_OK); return; } // ImageCapture did not save the file into the name we gave it in the // options. It picks a new name according to its best lights, so we need // to rename the file. base::FilePath saveDir(base::SysNSStringToUTF8( [[options objectForKey:ICDownloadsDirectoryURL] path])); base::FilePath saveAsPath = saveDir.Append(saveAsFilename); base::FilePath savedPath = saveDir.Append(savedFilename); // Shared result value from file-copy closure to tell-listener closure. content::BrowserThread::PostTaskAndReplyWithResult( content::BrowserThread::FILE, FROM_HERE, base::Bind(&storage_monitor::RenameFile, savedPath, saveAsPath), base::Bind( &storage_monitor::ReturnRenameResultToListener, listener_, name)); } @end // ImageCaptureDevice