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 22namespace picasa { 23 24namespace { 25 26void 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 40PicasaDataProvider::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 54PicasaDataProvider::~PicasaDataProvider() {} 55 56void 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 84scoped_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 91scoped_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 98scoped_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 116void 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 129void 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 135void 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 142void 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 172void 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 206void 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 228std::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 239void 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