Home | History | Annotate | Download | only in storage_monitor
      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 
      5 #import "components/storage_monitor/image_capture_device.h"
      6 
      7 #include "base/file_util.h"
      8 #include "content/public/browser/browser_thread.h"
      9 
     10 namespace storage_monitor {
     11 
     12 namespace {
     13 
     14 base::File::Error RenameFile(const base::FilePath& downloaded_filename,
     15                              const base::FilePath& desired_filename) {
     16   DCHECK(content::BrowserThread::CurrentlyOn(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;
     19 }
     20 
     21 void ReturnRenameResultToListener(
     22     base::WeakPtr<ImageCaptureDeviceListener> listener,
     23     const std::string& name,
     24     const base::File::Error& result) {
     25   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
     26   if (listener)
     27     listener->DownloadedFile(name, result);
     28 }
     29 
     30 base::Time NSDateToBaseTime(NSDate* date) {
     31   return base::Time::FromDoubleT([date timeIntervalSince1970]);
     32 }
     33 
     34 base::FilePath PathForCameraItem(ICCameraItem* item) {
     35   std::string name = base::SysNSStringToUTF8([item name]);
     36 
     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];
     42   }
     43   base::FilePath path;
     44   for (std::vector<std::string>::reverse_iterator i = components.rbegin();
     45        i != components.rend(); ++i) {
     46     path = path.Append(*i);
     47   }
     48   path = path.Append(name);
     49 
     50   return path;
     51 }
     52 
     53 }  // namespace
     54 
     55 }  // namespace storage_monitor
     56 
     57 @implementation ImageCaptureDevice
     58 
     59 - (id)initWithCameraDevice:(ICCameraDevice*)cameraDevice {
     60   if ((self = [super init])) {
     61     camera_.reset([cameraDevice retain]);
     62     [camera_ setDelegate:self];
     63     closing_ = false;
     64   }
     65   return self;
     66 }
     67 
     68 - (void)dealloc {
     69   // Make sure the session was closed and listener set to null
     70   // before destruction.
     71   DCHECK(![camera_ delegate]);
     72   DCHECK(!listener_);
     73   [super dealloc];
     74 }
     75 
     76 - (void)setListener:(base::WeakPtr<storage_monitor::ImageCaptureDeviceListener>)
     77         listener {
     78   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
     79   listener_ = listener;
     80 }
     81 
     82 - (void)open {
     83   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
     84   DCHECK(listener_);
     85   [camera_ requestOpenSession];
     86 }
     87 
     88 - (void)close {
     89   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
     90   closing_ = true;
     91   [camera_ cancelDownload];
     92   [camera_ requestCloseSession];
     93   [camera_ setDelegate:nil];
     94   listener_.reset();
     95 }
     96 
     97 - (void)eject {
     98   [camera_ requestEjectOrDisconnect];
     99 }
    100 
    101 - (void)downloadFile:(const std::string&)name
    102            localPath:(const base::FilePath&)localPath {
    103   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
    104 
    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());
    116 
    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];
    123 
    124       [camera_ requestDownloadFile:base::mac::ObjCCastStrict<ICCameraFile>(item)
    125                            options:options
    126                   downloadDelegate:self
    127                didDownloadSelector:
    128                    @selector(didDownloadFile:error:options:contextInfo:)
    129                        contextInfo:NULL];
    130       return;
    131     }
    132   }
    133 
    134   if (listener_)
    135     listener_->DownloadedFile(name, base::File::FILE_ERROR_NOT_FOUND);
    136 }
    137 
    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;
    142   else
    143     info.size = [base::mac::ObjCCastStrict<ICCameraFile>(item) fileSize];
    144 
    145   base::FilePath path = storage_monitor::PathForCameraItem(item);
    146 
    147   info.last_modified =
    148       storage_monitor::NSDateToBaseTime([item modificationDate]);
    149   info.creation_time = storage_monitor::NSDateToBaseTime([item creationDate]);
    150   info.last_accessed = info.last_modified;
    151 
    152   if (listener_)
    153     listener_->ItemAdded(path.value(), info);
    154 }
    155 
    156 - (void)cameraDevice:(ICCameraDevice*)camera didAddItems:(NSArray*)items {
    157   for (ICCameraItem* item in items)
    158     [self cameraDevice:camera didAddItem:item];
    159 }
    160 
    161 - (void)didRemoveDevice:(ICDevice*)device {
    162   device.delegate = NULL;
    163   if (listener_)
    164     listener_->DeviceRemoved();
    165 }
    166 
    167 // Notifies that a session was opened with the given device; potentially
    168 // with an error.
    169 - (void)device:(ICDevice*)device didOpenSessionWithError:(NSError*)error {
    170   if (error)
    171     [self didRemoveDevice:camera_];
    172 }
    173 
    174 - (void)device:(ICDevice*)device didEncounterError:(NSError*)error {
    175   if (error && listener_)
    176     listener_->DeviceRemoved();
    177 }
    178 
    179 // When this message is received, all media metadata is now loaded.
    180 - (void)deviceDidBecomeReadyWithCompleteContentCatalog:(ICDevice*)device {
    181   if (listener_)
    182     listener_->NoMoreItems();
    183 }
    184 
    185 - (void)didDownloadFile:(ICCameraFile*)file
    186                   error:(NSError*)error
    187                 options:(NSDictionary*)options
    188             contextInfo:(void*)contextInfo {
    189   if (closing_)
    190     return;
    191 
    192   std::string name = storage_monitor::PathForCameraItem(file).value();
    193 
    194   if (error) {
    195     DVLOG(1) << "error..."
    196              << base::SysNSStringToUTF8([error localizedDescription]);
    197     if (listener_)
    198       listener_->DownloadedFile(name, base::File::FILE_ERROR_FAILED);
    199     return;
    200   }
    201 
    202   std::string savedFilename =
    203       base::SysNSStringToUTF8([options objectForKey:ICSavedFilename]);
    204   std::string saveAsFilename =
    205       base::SysNSStringToUTF8([options objectForKey:ICSaveAsFilename]);
    206   if (savedFilename == saveAsFilename) {
    207     if (listener_)
    208       listener_->DownloadedFile(name, base::File::FILE_OK);
    209     return;
    210   }
    211 
    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,
    222       FROM_HERE,
    223       base::Bind(&storage_monitor::RenameFile, savedPath, saveAsPath),
    224       base::Bind(
    225           &storage_monitor::ReturnRenameResultToListener, listener_, name));
    226 }
    227 
    228 @end  // ImageCaptureDevice
    229