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.
5 #include "chrome/browser/media_galleries/fileapi/picasa_data_provider.h"
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"
27 std::vector
<PicasaDataProvider::ReadyCallback
>* ready_callbacks
,
29 for (std::vector
<PicasaDataProvider::ReadyCallback
>::const_iterator it
=
30 ready_callbacks
->begin();
31 it
!= ready_callbacks
->end();
35 ready_callbacks
->clear();
40 PicasaDataProvider::PicasaDataProvider(const base::FilePath
& database_path
)
41 : database_path_(database_path
),
42 state_(STALE_DATA_STATE
),
44 DCHECK(MediaFileSystemBackend::CurrentlyOnMediaTaskRunnerThread());
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()));
54 PicasaDataProvider::~PicasaDataProvider() {}
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.
62 if (state_
== INVALID_DATA_STATE
) {
63 ready_callback
.Run(false /* success */);
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 */);
73 album_list_ready_callbacks_
.push_back(ready_callback
);
75 if (state_
== ALBUMS_IMAGES_FRESH_STATE
) {
76 ready_callback
.Run(true /* success */);
79 albums_index_ready_callbacks_
.push_back(ready_callback
);
81 DoRefreshIfNecessary();
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_
));
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_
));
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
);
105 AlbumImagesMap::const_iterator it
= albums_images_
.find(key
);
107 if (it
== albums_images_
.end()) {
108 *error
= base::File::FILE_ERROR_NOT_FOUND
;
109 return scoped_ptr
<AlbumImages
>();
112 *error
= base::File::FILE_OK
;
113 return make_scoped_ptr(new AlbumImages(it
->second
));
116 void PicasaDataProvider::InvalidateData() {
117 DCHECK(MediaFileSystemBackend::CurrentlyOnMediaTaskRunnerThread());
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
;
126 DoRefreshIfNecessary();
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());
135 void PicasaDataProvider::OnTempDirChanged(const base::FilePath
& temp_dir_path
,
137 DCHECK(MediaFileSystemBackend::CurrentlyOnMediaTaskRunnerThread());
138 if (base::IsDirectoryEmpty(temp_dir_path
))
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()));
147 if (album_list_ready_callbacks_
.empty() &&
148 albums_index_ready_callbacks_
.empty()) {
152 if (state_
== STALE_DATA_STATE
) {
153 if (album_table_reader_
.get())
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_
));
162 DCHECK(state_
== LIST_OF_ALBUMS_AND_FOLDERS_FRESH_STATE
);
163 if (albums_indexer_
.get())
165 albums_indexer_
= new SafePicasaAlbumsIndexer(album_map_
, folder_map_
);
166 albums_indexer_
->Start(base::Bind(&PicasaDataProvider::OnAlbumsIndexerDone
,
167 weak_factory_
.GetWeakPtr(),
172 void PicasaDataProvider::OnAlbumTableReaderDone(
173 scoped_refptr
<SafePicasaAlbumTableReader
> reader
,
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())
181 album_table_reader_
= NULL
;
183 DCHECK(state_
== STALE_DATA_STATE
);
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 */);
196 UniquifyNames(albums
, &album_map_
);
197 UniquifyNames(folders
, &folder_map_
);
199 state_
= LIST_OF_ALBUMS_AND_FOLDERS_FRESH_STATE
;
200 RunAllCallbacks(&album_list_ready_callbacks_
, parse_success
);
202 // Chain from this process onto refreshing the albums images if necessary.
203 DoRefreshIfNecessary();
206 void PicasaDataProvider::OnAlbumsIndexerDone(
207 scoped_refptr
<SafePicasaAlbumsIndexer
> indexer
,
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())
214 albums_indexer_
= NULL
;
216 DCHECK(state_
== LIST_OF_ALBUMS_AND_FOLDERS_FRESH_STATE
);
219 state_
= ALBUMS_IMAGES_FRESH_STATE
;
221 albums_images_
= albums_images
;
224 RunAllCallbacks(&albums_index_ready_callbacks_
, success
);
228 std::string
PicasaDataProvider::DateToPathString(const base::Time
& time
) {
229 base::Time::Exploded exploded_time
;
230 time
.LocalExplode(&exploded_time
);
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
);
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
;
244 std::map
<std::string
, int> total_counts
;
245 std::map
<std::string
, int> current_counts
;
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
];
255 for (unsigned int i
= 0; i
< info_list
.size(); i
++) {
256 std::string name
= desired_names
[i
];
258 if (total_counts
[name
] != 1) {
259 name
= base::StringPrintf("%s (%d)", name
.c_str(),
260 ++current_counts
[name
]);
263 result_map
->insert(std::pair
<std::string
, AlbumInfo
>(name
, info_list
[i
]));
267 } // namespace picasa