Home | History | Annotate | Download | only in fileapi
      1 // Copyright 2013 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 #include "chrome/browser/media_galleries/fileapi/picasa_data_provider.h"
      6 
      7 #include <utility>
      8 
      9 #include "base/basictypes.h"
     10 #include "base/bind_helpers.h"
     11 #include "base/callback.h"
     12 #include "base/files/file_util.h"
     13 #include "base/strings/stringprintf.h"
     14 #include "chrome/browser/media_galleries/fileapi/file_path_watcher_util.h"
     15 #include "chrome/browser/media_galleries/fileapi/media_file_system_backend.h"
     16 #include "chrome/browser/media_galleries/fileapi/safe_picasa_album_table_reader.h"
     17 #include "chrome/browser/media_galleries/fileapi/safe_picasa_albums_indexer.h"
     18 #include "chrome/browser/media_galleries/imported_media_gallery_registry.h"
     19 #include "storage/browser/fileapi/file_system_operation_context.h"
     20 #include "storage/browser/fileapi/file_system_url.h"
     21 
     22 namespace picasa {
     23 
     24 namespace {
     25 
     26 void RunAllCallbacks(
     27     std::vector<PicasaDataProvider::ReadyCallback>* ready_callbacks,
     28     bool success) {
     29   for (std::vector<PicasaDataProvider::ReadyCallback>::const_iterator it =
     30            ready_callbacks->begin();
     31        it != ready_callbacks->end();
     32        ++it) {
     33     it->Run(success);
     34   }
     35   ready_callbacks->clear();
     36 }
     37 
     38 }  // namespace
     39 
     40 PicasaDataProvider::PicasaDataProvider(const base::FilePath& database_path)
     41     : database_path_(database_path),
     42       state_(STALE_DATA_STATE),
     43       weak_factory_(this) {
     44   DCHECK(MediaFileSystemBackend::CurrentlyOnMediaTaskRunnerThread());
     45 
     46   StartFilePathWatchOnMediaTaskRunner(
     47       database_path_.DirName().AppendASCII(kPicasaTempDirName),
     48       base::Bind(&PicasaDataProvider::OnTempDirWatchStarted,
     49                  weak_factory_.GetWeakPtr()),
     50       base::Bind(&PicasaDataProvider::OnTempDirChanged,
     51                  weak_factory_.GetWeakPtr()));
     52 }
     53 
     54 PicasaDataProvider::~PicasaDataProvider() {}
     55 
     56 void PicasaDataProvider::RefreshData(DataType needed_data,
     57                                      const ReadyCallback& ready_callback) {
     58   DCHECK(MediaFileSystemBackend::CurrentlyOnMediaTaskRunnerThread());
     59   // TODO(tommycli): Need to watch the database_path_ folder and handle
     60   // rereading the data when it changes.
     61 
     62   if (state_ == INVALID_DATA_STATE) {
     63     ready_callback.Run(false /* success */);
     64     return;
     65   }
     66 
     67   if (needed_data == LIST_OF_ALBUMS_AND_FOLDERS_DATA) {
     68     if (state_ == LIST_OF_ALBUMS_AND_FOLDERS_FRESH_STATE ||
     69         state_ == ALBUMS_IMAGES_FRESH_STATE) {
     70       ready_callback.Run(true /* success */);
     71       return;
     72     }
     73     album_list_ready_callbacks_.push_back(ready_callback);
     74   } else {
     75     if (state_ == ALBUMS_IMAGES_FRESH_STATE) {
     76       ready_callback.Run(true /* success */);
     77       return;
     78     }
     79     albums_index_ready_callbacks_.push_back(ready_callback);
     80   }
     81   DoRefreshIfNecessary();
     82 }
     83 
     84 scoped_ptr<AlbumMap> PicasaDataProvider::GetFolders() {
     85   DCHECK(MediaFileSystemBackend::CurrentlyOnMediaTaskRunnerThread());
     86   DCHECK(state_ == LIST_OF_ALBUMS_AND_FOLDERS_FRESH_STATE ||
     87          state_ == ALBUMS_IMAGES_FRESH_STATE);
     88   return make_scoped_ptr(new AlbumMap(folder_map_));
     89 }
     90 
     91 scoped_ptr<AlbumMap> PicasaDataProvider::GetAlbums() {
     92   DCHECK(MediaFileSystemBackend::CurrentlyOnMediaTaskRunnerThread());
     93   DCHECK(state_ == LIST_OF_ALBUMS_AND_FOLDERS_FRESH_STATE ||
     94          state_ == ALBUMS_IMAGES_FRESH_STATE);
     95   return make_scoped_ptr(new AlbumMap(album_map_));
     96 }
     97 
     98 scoped_ptr<AlbumImages> PicasaDataProvider::FindAlbumImages(
     99     const std::string& key,
    100     base::File::Error* error) {
    101   DCHECK(MediaFileSystemBackend::CurrentlyOnMediaTaskRunnerThread());
    102   DCHECK(state_ == ALBUMS_IMAGES_FRESH_STATE);
    103   DCHECK(error);
    104 
    105   AlbumImagesMap::const_iterator it = albums_images_.find(key);
    106 
    107   if (it == albums_images_.end()) {
    108     *error = base::File::FILE_ERROR_NOT_FOUND;
    109     return scoped_ptr<AlbumImages>();
    110   }
    111 
    112   *error = base::File::FILE_OK;
    113   return make_scoped_ptr(new AlbumImages(it->second));
    114 }
    115 
    116 void PicasaDataProvider::InvalidateData() {
    117   DCHECK(MediaFileSystemBackend::CurrentlyOnMediaTaskRunnerThread());
    118 
    119   // Set data state to stale and ignore responses from any in-flight processes.
    120   // TODO(tommycli): Implement and call Cancel function for these
    121   // UtilityProcessHostClients to actually kill the in-flight processes.
    122   state_ = STALE_DATA_STATE;
    123   album_table_reader_ = NULL;
    124   albums_indexer_ = NULL;
    125 
    126   DoRefreshIfNecessary();
    127 }
    128 
    129 void PicasaDataProvider::OnTempDirWatchStarted(
    130     scoped_ptr<base::FilePathWatcher> temp_dir_watcher) {
    131   DCHECK(MediaFileSystemBackend::CurrentlyOnMediaTaskRunnerThread());
    132   temp_dir_watcher_.reset(temp_dir_watcher.release());
    133 }
    134 
    135 void PicasaDataProvider::OnTempDirChanged(const base::FilePath& temp_dir_path,
    136                                           bool error) {
    137   DCHECK(MediaFileSystemBackend::CurrentlyOnMediaTaskRunnerThread());
    138   if (base::IsDirectoryEmpty(temp_dir_path))
    139     InvalidateData();
    140 }
    141 
    142 void PicasaDataProvider::DoRefreshIfNecessary() {
    143   DCHECK(state_ != INVALID_DATA_STATE);
    144   DCHECK(state_ != ALBUMS_IMAGES_FRESH_STATE);
    145   DCHECK(!(album_table_reader_.get() && albums_indexer_.get()));
    146 
    147   if (album_list_ready_callbacks_.empty() &&
    148       albums_index_ready_callbacks_.empty()) {
    149     return;
    150   }
    151 
    152   if (state_ == STALE_DATA_STATE) {
    153     if (album_table_reader_.get())
    154       return;
    155     album_table_reader_ =
    156         new SafePicasaAlbumTableReader(AlbumTableFiles(database_path_));
    157     album_table_reader_->Start(
    158         base::Bind(&PicasaDataProvider::OnAlbumTableReaderDone,
    159                    weak_factory_.GetWeakPtr(),
    160                    album_table_reader_));
    161   } else {
    162     DCHECK(state_ == LIST_OF_ALBUMS_AND_FOLDERS_FRESH_STATE);
    163     if (albums_indexer_.get())
    164       return;
    165     albums_indexer_ = new SafePicasaAlbumsIndexer(album_map_, folder_map_);
    166     albums_indexer_->Start(base::Bind(&PicasaDataProvider::OnAlbumsIndexerDone,
    167                                       weak_factory_.GetWeakPtr(),
    168                                       albums_indexer_));
    169   }
    170 }
    171 
    172 void PicasaDataProvider::OnAlbumTableReaderDone(
    173     scoped_refptr<SafePicasaAlbumTableReader> reader,
    174     bool parse_success,
    175     const std::vector<AlbumInfo>& albums,
    176     const std::vector<AlbumInfo>& folders) {
    177   DCHECK(MediaFileSystemBackend::CurrentlyOnMediaTaskRunnerThread());
    178   // If the reader has already been deemed stale, ignore the result.
    179   if (reader.get() != album_table_reader_.get())
    180     return;
    181   album_table_reader_ = NULL;
    182 
    183   DCHECK(state_ == STALE_DATA_STATE);
    184 
    185   if (!parse_success) {
    186     // If we didn't get the list successfully, fail all those waiting for
    187     // the albums indexer also.
    188     state_ = INVALID_DATA_STATE;
    189     RunAllCallbacks(&album_list_ready_callbacks_, false /* success */);
    190     RunAllCallbacks(&albums_index_ready_callbacks_, false /* success */);
    191     return;
    192   }
    193 
    194   album_map_.clear();
    195   folder_map_.clear();
    196   UniquifyNames(albums, &album_map_);
    197   UniquifyNames(folders, &folder_map_);
    198 
    199   state_ = LIST_OF_ALBUMS_AND_FOLDERS_FRESH_STATE;
    200   RunAllCallbacks(&album_list_ready_callbacks_, parse_success);
    201 
    202   // Chain from this process onto refreshing the albums images if necessary.
    203   DoRefreshIfNecessary();
    204 }
    205 
    206 void PicasaDataProvider::OnAlbumsIndexerDone(
    207     scoped_refptr<SafePicasaAlbumsIndexer> indexer,
    208     bool success,
    209     const picasa::AlbumImagesMap& albums_images) {
    210   DCHECK(MediaFileSystemBackend::CurrentlyOnMediaTaskRunnerThread());
    211   // If the indexer has already been deemed stale, ignore the result.
    212   if (indexer.get() != albums_indexer_.get())
    213     return;
    214   albums_indexer_ = NULL;
    215 
    216   DCHECK(state_ == LIST_OF_ALBUMS_AND_FOLDERS_FRESH_STATE);
    217 
    218   if (success) {
    219     state_ = ALBUMS_IMAGES_FRESH_STATE;
    220 
    221     albums_images_ = albums_images;
    222   }
    223 
    224   RunAllCallbacks(&albums_index_ready_callbacks_, success);
    225 }
    226 
    227 // static
    228 std::string PicasaDataProvider::DateToPathString(const base::Time& time) {
    229   base::Time::Exploded exploded_time;
    230   time.LocalExplode(&exploded_time);
    231 
    232   // TODO(tommycli): Investigate better localization and persisting which locale
    233   // we use to generate these unique names.
    234   return base::StringPrintf("%04d-%02d-%02d", exploded_time.year,
    235                             exploded_time.month, exploded_time.day_of_month);
    236 }
    237 
    238 // static
    239 void PicasaDataProvider::UniquifyNames(const std::vector<AlbumInfo>& info_list,
    240                                        AlbumMap* result_map) {
    241   // TODO(tommycli): We should persist the uniquified names.
    242   std::vector<std::string> desired_names;
    243 
    244   std::map<std::string, int> total_counts;
    245   std::map<std::string, int> current_counts;
    246 
    247   for (std::vector<AlbumInfo>::const_iterator it = info_list.begin();
    248        it != info_list.end(); ++it) {
    249     std::string desired_name =
    250         it->name + " " + DateToPathString(it->timestamp);
    251     desired_names.push_back(desired_name);
    252     ++total_counts[desired_name];
    253   }
    254 
    255   for (unsigned int i = 0; i < info_list.size(); i++) {
    256     std::string name = desired_names[i];
    257 
    258     if (total_counts[name] != 1) {
    259       name = base::StringPrintf("%s (%d)", name.c_str(),
    260                                 ++current_counts[name]);
    261     }
    262 
    263     result_map->insert(std::pair<std::string, AlbumInfo>(name, info_list[i]));
    264   }
    265 }
    266 
    267 }  // namespace picasa
    268