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