Disable view source for Developer Tools.
[chromium-blink-merge.git] / chrome / browser / media_galleries / media_galleries_preferences.cc
blob5575cc6aabc36f79a7b78f9947d2fbe1af90e8be
1 // Copyright (c) 2012 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/media_galleries_preferences.h"
7 #include "base/base_paths_posix.h"
8 #include "base/callback.h"
9 #include "base/i18n/time_formatting.h"
10 #include "base/path_service.h"
11 #include "base/prefs/pref_service.h"
12 #include "base/prefs/scoped_user_pref_update.h"
13 #include "base/stl_util.h"
14 #include "base/strings/string16.h"
15 #include "base/strings/string_number_conversions.h"
16 #include "base/strings/utf_string_conversions.h"
17 #include "base/values.h"
18 #include "chrome/browser/browser_process.h"
19 #include "chrome/browser/extensions/api/media_galleries_private/media_galleries_private_api.h"
20 #include "chrome/browser/extensions/extension_service.h"
21 #include "chrome/browser/extensions/extension_system.h"
22 #include "chrome/browser/media_galleries/fileapi/iapps_finder.h"
23 #include "chrome/browser/media_galleries/fileapi/picasa_finder.h"
24 #include "chrome/browser/media_galleries/media_file_system_registry.h"
25 #include "chrome/browser/media_galleries/media_galleries_histograms.h"
26 #include "chrome/browser/profiles/profile.h"
27 #include "chrome/browser/storage_monitor/media_storage_util.h"
28 #include "chrome/browser/storage_monitor/storage_monitor.h"
29 #include "chrome/common/chrome_paths.h"
30 #include "chrome/common/extensions/permissions/media_galleries_permission.h"
31 #include "chrome/common/pref_names.h"
32 #include "components/user_prefs/pref_registry_syncable.h"
33 #include "content/public/browser/browser_thread.h"
34 #include "extensions/browser/pref_names.h"
35 #include "extensions/common/extension.h"
36 #include "extensions/common/extension_set.h"
37 #include "extensions/common/permissions/api_permission.h"
38 #include "extensions/common/permissions/permissions_data.h"
39 #include "grit/generated_resources.h"
40 #include "ui/base/l10n/l10n_util.h"
41 #include "ui/base/text/bytes_formatting.h"
43 using base::DictionaryValue;
44 using base::ListValue;
45 using extensions::ExtensionPrefs;
47 namespace {
49 // Pref key for the list of media gallery permissions.
50 const char kMediaGalleriesPermissions[] = "media_galleries_permissions";
51 // Pref key for Media Gallery ID.
52 const char kMediaGalleryIdKey[] = "id";
53 // Pref key for Media Gallery Permission Value.
54 const char kMediaGalleryHasPermissionKey[] = "has_permission";
56 const char kMediaGalleriesDeviceIdKey[] = "deviceId";
57 const char kMediaGalleriesDisplayNameKey[] = "displayName";
58 const char kMediaGalleriesPathKey[] = "path";
59 const char kMediaGalleriesPrefIdKey[] = "prefId";
60 const char kMediaGalleriesTypeKey[] = "type";
61 const char kMediaGalleriesVolumeLabelKey[] = "volumeLabel";
62 const char kMediaGalleriesVendorNameKey[] = "vendorName";
63 const char kMediaGalleriesModelNameKey[] = "modelName";
64 const char kMediaGalleriesSizeKey[] = "totalSize";
65 const char kMediaGalleriesLastAttachTimeKey[] = "lastAttachTime";
66 const char kMediaGalleriesScanImageCountKey[] = "imageCount";
67 const char kMediaGalleriesScanMusicCountKey[] = "musicCount";
68 const char kMediaGalleriesScanVideoCountKey[] = "videoCount";
69 const char kMediaGalleriesPrefsVersionKey[] = "preferencesVersion";
71 const char kMediaGalleriesTypeAutoDetectedValue[] = "autoDetected";
72 const char kMediaGalleriesTypeBlackListedValue[] = "blackListed";
73 const char kMediaGalleriesTypeRemovedScanValue[] = "removedScan";
74 const char kMediaGalleriesTypeScanResultValue[] = "scanResult";
75 const char kMediaGalleriesTypeUserAddedValue[] = "userAdded";
77 const char kIPhotoGalleryName[] = "iPhoto";
78 const char kITunesGalleryName[] = "iTunes";
79 const char kPicasaGalleryName[] = "Picasa";
81 const int kCurrentPrefsVersion = 2;
83 int NumberExtensionsUsingMediaGalleries(Profile* profile) {
84 int count = 0;
85 if (!profile)
86 return count;
87 ExtensionService* extension_service =
88 extensions::ExtensionSystem::Get(profile)->extension_service();
89 if (!extension_service)
90 return count;
92 const extensions::ExtensionSet* extensions = extension_service->extensions();
93 for (extensions::ExtensionSet::const_iterator i = extensions->begin();
94 i != extensions->end(); ++i) {
95 if (extensions::PermissionsData::HasAPIPermission(
96 *i, extensions::APIPermission::kMediaGalleries) ||
97 extensions::PermissionsData::HasAPIPermission(
98 *i, extensions::APIPermission::kMediaGalleriesPrivate)) {
99 count++;
102 return count;
105 bool GetPrefId(const base::DictionaryValue& dict, MediaGalleryPrefId* value) {
106 std::string string_id;
107 if (!dict.GetString(kMediaGalleriesPrefIdKey, &string_id) ||
108 !base::StringToUint64(string_id, value)) {
109 return false;
112 return true;
115 bool GetType(const base::DictionaryValue& dict,
116 MediaGalleryPrefInfo::Type* type) {
117 std::string string_type;
118 if (!dict.GetString(kMediaGalleriesTypeKey, &string_type))
119 return false;
121 if (string_type == kMediaGalleriesTypeUserAddedValue) {
122 *type = MediaGalleryPrefInfo::kUserAdded;
123 return true;
125 if (string_type == kMediaGalleriesTypeAutoDetectedValue) {
126 *type = MediaGalleryPrefInfo::kAutoDetected;
127 return true;
129 if (string_type == kMediaGalleriesTypeBlackListedValue) {
130 *type = MediaGalleryPrefInfo::kBlackListed;
131 return true;
133 if (string_type == kMediaGalleriesTypeScanResultValue) {
134 *type = MediaGalleryPrefInfo::kScanResult;
135 return true;
137 if (string_type == kMediaGalleriesTypeRemovedScanValue) {
138 *type = MediaGalleryPrefInfo::kRemovedScan;
139 return true;
142 return false;
145 const char* TypeToStringValue(MediaGalleryPrefInfo::Type type) {
146 const char* result = NULL;
147 switch (type) {
148 case MediaGalleryPrefInfo::kUserAdded:
149 result = kMediaGalleriesTypeUserAddedValue;
150 break;
151 case MediaGalleryPrefInfo::kAutoDetected:
152 result = kMediaGalleriesTypeAutoDetectedValue;
153 break;
154 case MediaGalleryPrefInfo::kBlackListed:
155 result = kMediaGalleriesTypeBlackListedValue;
156 break;
157 case MediaGalleryPrefInfo::kScanResult:
158 result = kMediaGalleriesTypeScanResultValue;
159 break;
160 case MediaGalleryPrefInfo::kRemovedScan:
161 result = kMediaGalleriesTypeRemovedScanValue;
162 break;
163 default:
164 NOTREACHED();
165 break;
167 return result;
170 bool PopulateGalleryPrefInfoFromDictionary(
171 const base::DictionaryValue& dict, MediaGalleryPrefInfo* out_gallery_info) {
172 MediaGalleryPrefId pref_id;
173 base::string16 display_name;
174 std::string device_id;
175 base::FilePath::StringType path;
176 MediaGalleryPrefInfo::Type type = MediaGalleryPrefInfo::kInvalidType;
177 base::string16 volume_label;
178 base::string16 vendor_name;
179 base::string16 model_name;
180 double total_size_in_bytes = 0.0;
181 double last_attach_time = 0.0;
182 bool volume_metadata_valid = false;
183 int image_count = 0;
184 int music_count = 0;
185 int video_count = 0;
186 int prefs_version = 0;
188 if (!GetPrefId(dict, &pref_id) ||
189 !dict.GetString(kMediaGalleriesDeviceIdKey, &device_id) ||
190 !dict.GetString(kMediaGalleriesPathKey, &path) ||
191 !GetType(dict, &type)) {
192 return false;
195 dict.GetString(kMediaGalleriesDisplayNameKey, &display_name);
196 dict.GetInteger(kMediaGalleriesPrefsVersionKey, &prefs_version);
198 if (dict.GetString(kMediaGalleriesVolumeLabelKey, &volume_label) &&
199 dict.GetString(kMediaGalleriesVendorNameKey, &vendor_name) &&
200 dict.GetString(kMediaGalleriesModelNameKey, &model_name) &&
201 dict.GetDouble(kMediaGalleriesSizeKey, &total_size_in_bytes) &&
202 dict.GetDouble(kMediaGalleriesLastAttachTimeKey, &last_attach_time)) {
203 volume_metadata_valid = true;
206 if (type == MediaGalleryPrefInfo::kScanResult &&
207 dict.GetInteger(kMediaGalleriesScanImageCountKey, &image_count) &&
208 dict.GetInteger(kMediaGalleriesScanMusicCountKey, &music_count) &&
209 dict.GetInteger(kMediaGalleriesScanVideoCountKey, &video_count)) {
210 out_gallery_info->image_count = image_count;
211 out_gallery_info->music_count = music_count;
212 out_gallery_info->video_count = video_count;
215 out_gallery_info->pref_id = pref_id;
216 out_gallery_info->display_name = display_name;
217 out_gallery_info->device_id = device_id;
218 out_gallery_info->path = base::FilePath(path);
219 out_gallery_info->type = type;
220 out_gallery_info->volume_label = volume_label;
221 out_gallery_info->vendor_name = vendor_name;
222 out_gallery_info->model_name = model_name;
223 out_gallery_info->total_size_in_bytes = total_size_in_bytes;
224 out_gallery_info->last_attach_time =
225 base::Time::FromInternalValue(last_attach_time);
226 out_gallery_info->volume_metadata_valid = volume_metadata_valid;
227 out_gallery_info->prefs_version = prefs_version;
229 return true;
232 base::DictionaryValue* CreateGalleryPrefInfoDictionary(
233 const MediaGalleryPrefInfo& gallery) {
234 base::DictionaryValue* dict = new base::DictionaryValue();
235 dict->SetString(kMediaGalleriesPrefIdKey,
236 base::Uint64ToString(gallery.pref_id));
237 dict->SetString(kMediaGalleriesDeviceIdKey, gallery.device_id);
238 dict->SetString(kMediaGalleriesPathKey, gallery.path.value());
239 dict->SetString(kMediaGalleriesTypeKey, TypeToStringValue(gallery.type));
241 if (gallery.volume_metadata_valid) {
242 dict->SetString(kMediaGalleriesVolumeLabelKey, gallery.volume_label);
243 dict->SetString(kMediaGalleriesVendorNameKey, gallery.vendor_name);
244 dict->SetString(kMediaGalleriesModelNameKey, gallery.model_name);
245 dict->SetDouble(kMediaGalleriesSizeKey, gallery.total_size_in_bytes);
246 dict->SetDouble(kMediaGalleriesLastAttachTimeKey,
247 gallery.last_attach_time.ToInternalValue());
248 } else {
249 dict->SetString(kMediaGalleriesDisplayNameKey, gallery.display_name);
252 if (gallery.type == MediaGalleryPrefInfo::kScanResult) {
253 dict->SetInteger(kMediaGalleriesScanImageCountKey, gallery.image_count);
254 dict->SetInteger(kMediaGalleriesScanMusicCountKey, gallery.music_count);
255 dict->SetInteger(kMediaGalleriesScanVideoCountKey, gallery.video_count);
258 // Version 0 of the prefs format was that the display_name was always
259 // used to show the user-visible name of the gallery. Version 1 means
260 // that there is an optional display_name, and when it is present, it
261 // overrides the name that would be built from the volume metadata, path,
262 // or whatever other data. So if we see a display_name with version 0, it
263 // means it may be overwritten simply by getting new volume metadata.
264 // A display_name with version 1 should not be overwritten.
265 dict->SetInteger(kMediaGalleriesPrefsVersionKey, gallery.prefs_version);
267 return dict;
270 bool HasAutoDetectedGalleryPermission(const extensions::Extension& extension) {
271 extensions::MediaGalleriesPermission::CheckParam param(
272 extensions::MediaGalleriesPermission::kAllAutoDetectedPermission);
273 return extensions::PermissionsData::CheckAPIPermissionWithParam(
274 &extension, extensions::APIPermission::kMediaGalleries, &param);
277 // Retrieves the MediaGalleryPermission from the given dictionary; DCHECKs on
278 // failure.
279 bool GetMediaGalleryPermissionFromDictionary(
280 const base::DictionaryValue* dict,
281 MediaGalleryPermission* out_permission) {
282 std::string string_id;
283 if (dict->GetString(kMediaGalleryIdKey, &string_id) &&
284 base::StringToUint64(string_id, &out_permission->pref_id) &&
285 dict->GetBoolean(kMediaGalleryHasPermissionKey,
286 &out_permission->has_permission)) {
287 return true;
289 NOTREACHED();
290 return false;
293 base::string16 GetDisplayNameForDevice(uint64 storage_size_in_bytes,
294 const base::string16& name) {
295 DCHECK(!name.empty());
296 return (storage_size_in_bytes == 0) ?
297 name :
298 ui::FormatBytes(storage_size_in_bytes) + base::ASCIIToUTF16(" ") + name;
301 // For a device with |device_name| and a relative path |sub_folder|, construct
302 // a display name. If |sub_folder| is empty, then just return |device_name|.
303 base::string16 GetDisplayNameForSubFolder(const base::string16& device_name,
304 const base::FilePath& sub_folder) {
305 if (sub_folder.empty())
306 return device_name;
307 return (sub_folder.BaseName().LossyDisplayName() +
308 base::ASCIIToUTF16(" - ") +
309 device_name);
312 base::string16 GetFullProductName(const base::string16& vendor_name,
313 const base::string16& model_name) {
314 if (vendor_name.empty() && model_name.empty())
315 return base::string16();
317 base::string16 product_name;
318 if (vendor_name.empty())
319 product_name = model_name;
320 else if (model_name.empty())
321 product_name = vendor_name;
322 else if (!vendor_name.empty() && !model_name.empty())
323 product_name = vendor_name + base::UTF8ToUTF16(", ") + model_name;
325 return product_name;
328 } // namespace
330 MediaGalleryPrefInfo::MediaGalleryPrefInfo()
331 : pref_id(kInvalidMediaGalleryPrefId),
332 type(kInvalidType),
333 total_size_in_bytes(0),
334 volume_metadata_valid(false),
335 image_count(0),
336 music_count(0),
337 video_count(0),
338 prefs_version(0) {
341 MediaGalleryPrefInfo::~MediaGalleryPrefInfo() {}
343 base::FilePath MediaGalleryPrefInfo::AbsolutePath() const {
344 base::FilePath base_path = MediaStorageUtil::FindDevicePathById(device_id);
345 DCHECK(!path.IsAbsolute());
346 return base_path.empty() ? base_path : base_path.Append(path);
349 bool MediaGalleryPrefInfo::IsBlackListedType() const {
350 return type == kBlackListed || type == kRemovedScan;
353 base::string16 MediaGalleryPrefInfo::GetGalleryDisplayName() const {
354 if (!StorageInfo::IsRemovableDevice(device_id)) {
355 // For fixed storage, the default name is the fully qualified directory
356 // name, or in the case of a root directory, the root directory name.
357 // Exception: ChromeOS -- the full pathname isn't visible there, so only
358 // the directory name is used.
359 base::FilePath path = AbsolutePath();
360 if (!display_name.empty())
361 return display_name;
363 #if defined(OS_CHROMEOS)
364 // See chrome/browser/chromeos/fileapi/file_system_backend.cc
365 base::FilePath home_path;
366 if (PathService::Get(base::DIR_HOME, &home_path)) {
367 home_path = home_path.AppendASCII("Downloads");
368 base::FilePath relative;
369 if (home_path.AppendRelativePath(path, &relative))
370 return relative.LossyDisplayName();
372 return path.BaseName().LossyDisplayName();
373 #else
374 return path.LossyDisplayName();
375 #endif
378 base::string16 name = display_name;
379 if (name.empty())
380 name = volume_label;
381 if (name.empty())
382 name = GetFullProductName(vendor_name, model_name);
383 if (name.empty())
384 name = l10n_util::GetStringUTF16(IDS_MEDIA_GALLERIES_UNLABELED_DEVICE);
386 name = GetDisplayNameForDevice(total_size_in_bytes, name);
388 if (!path.empty())
389 name = GetDisplayNameForSubFolder(name, path);
391 return name;
394 base::string16 MediaGalleryPrefInfo::GetGalleryTooltip() const {
395 return AbsolutePath().LossyDisplayName();
398 base::string16 MediaGalleryPrefInfo::GetGalleryAdditionalDetails() const {
399 base::string16 attached;
400 if (StorageInfo::IsRemovableDevice(device_id)) {
401 if (MediaStorageUtil::IsRemovableStorageAttached(device_id)) {
402 attached = l10n_util::GetStringUTF16(
403 IDS_MEDIA_GALLERIES_DIALOG_DEVICE_ATTACHED);
404 } else if (!last_attach_time.is_null()) {
405 attached = l10n_util::GetStringFUTF16(
406 IDS_MEDIA_GALLERIES_LAST_ATTACHED,
407 base::TimeFormatShortDateNumeric(last_attach_time));
408 } else {
409 attached = l10n_util::GetStringUTF16(
410 IDS_MEDIA_GALLERIES_DIALOG_DEVICE_NOT_ATTACHED);
414 return attached;
417 bool MediaGalleryPrefInfo::IsGalleryAvailable() const {
418 return !StorageInfo::IsRemovableDevice(device_id) ||
419 MediaStorageUtil::IsRemovableStorageAttached(device_id);
422 MediaGalleriesPreferences::GalleryChangeObserver::~GalleryChangeObserver() {}
424 MediaGalleriesPreferences::MediaGalleriesPreferences(Profile* profile)
425 : initialized_(false),
426 pre_initialization_callbacks_waiting_(0),
427 profile_(profile),
428 extension_prefs_for_testing_(NULL),
429 weak_factory_(this) {
432 MediaGalleriesPreferences::~MediaGalleriesPreferences() {
433 if (StorageMonitor::GetInstance())
434 StorageMonitor::GetInstance()->RemoveObserver(this);
437 void MediaGalleriesPreferences::EnsureInitialized(base::Closure callback) {
438 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
440 if (IsInitialized()) {
441 if (!callback.is_null())
442 callback.Run();
443 return;
446 on_initialize_callbacks_.push_back(callback);
447 if (on_initialize_callbacks_.size() > 1)
448 return;
450 // This counter must match the number of async methods dispatched below.
451 // It cannot be incremented inline with each callback, as some may return
452 // synchronously, decrement the counter to 0, and prematurely trigger
453 // FinishInitialization.
454 pre_initialization_callbacks_waiting_ = 4;
456 // Check whether we should be initializing -- are there any extensions that
457 // are using media galleries?
458 media_galleries::UsageCount(media_galleries::PREFS_INITIALIZED);
459 if (NumberExtensionsUsingMediaGalleries(profile_) == 0) {
460 media_galleries::UsageCount(media_galleries::PREFS_INITIALIZED_ERROR);
463 // We determine the freshness of the profile here, before any of the finders
464 // return and add media galleries to it.
465 StorageMonitor::GetInstance()->EnsureInitialized(
466 base::Bind(&MediaGalleriesPreferences::OnStorageMonitorInit,
467 weak_factory_.GetWeakPtr(),
468 !APIHasBeenUsed(profile_) /* add_default_galleries */));
470 // Look for optional default galleries every time.
471 iapps::FindITunesLibrary(
472 base::Bind(&MediaGalleriesPreferences::OnFinderDeviceID,
473 weak_factory_.GetWeakPtr()));
475 picasa::FindPicasaDatabase(
476 base::Bind(&MediaGalleriesPreferences::OnFinderDeviceID,
477 weak_factory_.GetWeakPtr()));
479 iapps::FindIPhotoLibrary(
480 base::Bind(&MediaGalleriesPreferences::OnFinderDeviceID,
481 weak_factory_.GetWeakPtr()));
484 bool MediaGalleriesPreferences::IsInitialized() const { return initialized_; }
486 Profile* MediaGalleriesPreferences::profile() { return profile_; }
488 void MediaGalleriesPreferences::OnInitializationCallbackReturned() {
489 DCHECK(!IsInitialized());
490 DCHECK(pre_initialization_callbacks_waiting_ > 0);
491 if (--pre_initialization_callbacks_waiting_ == 0)
492 FinishInitialization();
495 void MediaGalleriesPreferences::FinishInitialization() {
496 DCHECK(!IsInitialized());
498 initialized_ = true;
500 StorageMonitor* monitor = StorageMonitor::GetInstance();
501 DCHECK(monitor->IsInitialized());
503 InitFromPrefs();
505 StorageMonitor::GetInstance()->AddObserver(this);
507 std::vector<StorageInfo> existing_devices =
508 monitor->GetAllAvailableStorages();
509 for (size_t i = 0; i < existing_devices.size(); i++) {
510 if (!(StorageInfo::IsMediaDevice(existing_devices[i].device_id()) &&
511 StorageInfo::IsRemovableDevice(existing_devices[i].device_id())))
512 continue;
513 AddGallery(existing_devices[i].device_id(),
514 base::FilePath(),
515 MediaGalleryPrefInfo::kAutoDetected,
516 existing_devices[i].storage_label(),
517 existing_devices[i].vendor_name(),
518 existing_devices[i].model_name(),
519 existing_devices[i].total_size_in_bytes(),
520 base::Time::Now(), 0, 0, 0);
523 for (std::vector<base::Closure>::iterator iter =
524 on_initialize_callbacks_.begin();
525 iter != on_initialize_callbacks_.end();
526 ++iter) {
527 iter->Run();
529 on_initialize_callbacks_.clear();
532 void MediaGalleriesPreferences::AddDefaultGalleries() {
533 const int kDirectoryKeys[] = {
534 chrome::DIR_USER_MUSIC,
535 chrome::DIR_USER_PICTURES,
536 chrome::DIR_USER_VIDEOS,
539 for (size_t i = 0; i < arraysize(kDirectoryKeys); ++i) {
540 base::FilePath path;
541 if (!PathService::Get(kDirectoryKeys[i], &path))
542 continue;
544 base::FilePath relative_path;
545 StorageInfo info;
546 if (MediaStorageUtil::GetDeviceInfoFromPath(path, &info, &relative_path)) {
547 AddGalleryInternal(info.device_id(), info.name(), relative_path,
548 MediaGalleryPrefInfo::kAutoDetected,
549 info.storage_label(), info.vendor_name(),
550 info.model_name(), info.total_size_in_bytes(),
551 base::Time(), true, 0, 0, 0, kCurrentPrefsVersion);
556 bool MediaGalleriesPreferences::UpdateDeviceIDForSingletonType(
557 const std::string& device_id) {
558 StorageInfo::Type singleton_type;
559 if (!StorageInfo::CrackDeviceId(device_id, &singleton_type, NULL))
560 return false;
562 PrefService* prefs = profile_->GetPrefs();
563 scoped_ptr<ListPrefUpdate> update(new ListPrefUpdate(
564 prefs, prefs::kMediaGalleriesRememberedGalleries));
565 base::ListValue* list = update->Get();
566 for (base::ListValue::iterator iter = list->begin();
567 iter != list->end(); ++iter) {
568 // All of these calls should succeed, but preferences file can be corrupt.
569 base::DictionaryValue* dict;
570 if (!(*iter)->GetAsDictionary(&dict))
571 continue;
572 std::string this_device_id;
573 if (!dict->GetString(kMediaGalleriesDeviceIdKey, &this_device_id))
574 continue;
575 if (this_device_id == device_id)
576 return true; // No update is necessary.
577 StorageInfo::Type device_type;
578 if (!StorageInfo::CrackDeviceId(this_device_id, &device_type, NULL))
579 continue;
581 if (device_type == singleton_type) {
582 dict->SetString(kMediaGalleriesDeviceIdKey, device_id);
583 update.reset(); // commits the update.
584 InitFromPrefs();
585 MediaGalleryPrefId pref_id;
586 if (GetPrefId(*dict, &pref_id)) {
587 FOR_EACH_OBSERVER(GalleryChangeObserver,
588 gallery_change_observers_,
589 OnGalleryInfoUpdated(this, pref_id));
591 return true;
594 return false;
597 void MediaGalleriesPreferences::OnStorageMonitorInit(
598 bool add_default_galleries) {
599 if (add_default_galleries)
600 AddDefaultGalleries();
601 OnInitializationCallbackReturned();
604 void MediaGalleriesPreferences::OnFinderDeviceID(const std::string& device_id) {
605 if (!device_id.empty() && !UpdateDeviceIDForSingletonType(device_id)) {
606 std::string gallery_name;
607 if (StorageInfo::IsIPhotoDevice(device_id))
608 gallery_name = kIPhotoGalleryName;
609 else if (StorageInfo::IsITunesDevice(device_id))
610 gallery_name = kITunesGalleryName;
611 else if (StorageInfo::IsPicasaDevice(device_id))
612 gallery_name = kPicasaGalleryName;
613 else
614 NOTREACHED();
616 AddGalleryInternal(device_id, base::ASCIIToUTF16(gallery_name),
617 base::FilePath(), MediaGalleryPrefInfo::kAutoDetected,
618 base::string16(), base::string16(), base::string16(), 0,
619 base::Time(), false, 0, 0, 0, kCurrentPrefsVersion);
622 OnInitializationCallbackReturned();
625 void MediaGalleriesPreferences::InitFromPrefs() {
626 known_galleries_.clear();
627 device_map_.clear();
629 PrefService* prefs = profile_->GetPrefs();
630 const base::ListValue* list = prefs->GetList(
631 prefs::kMediaGalleriesRememberedGalleries);
632 if (list) {
633 for (base::ListValue::const_iterator it = list->begin();
634 it != list->end(); ++it) {
635 const base::DictionaryValue* dict = NULL;
636 if (!(*it)->GetAsDictionary(&dict))
637 continue;
639 MediaGalleryPrefInfo gallery_info;
640 if (!PopulateGalleryPrefInfoFromDictionary(*dict, &gallery_info))
641 continue;
643 known_galleries_[gallery_info.pref_id] = gallery_info;
644 device_map_[gallery_info.device_id].insert(gallery_info.pref_id);
649 void MediaGalleriesPreferences::AddGalleryChangeObserver(
650 GalleryChangeObserver* observer) {
651 DCHECK(IsInitialized());
652 gallery_change_observers_.AddObserver(observer);
655 void MediaGalleriesPreferences::RemoveGalleryChangeObserver(
656 GalleryChangeObserver* observer) {
657 DCHECK(IsInitialized());
658 gallery_change_observers_.RemoveObserver(observer);
661 void MediaGalleriesPreferences::OnRemovableStorageAttached(
662 const StorageInfo& info) {
663 DCHECK(IsInitialized());
664 if (!StorageInfo::IsMediaDevice(info.device_id()))
665 return;
667 AddGallery(info.device_id(), base::FilePath(),
668 MediaGalleryPrefInfo::kAutoDetected, info.storage_label(),
669 info.vendor_name(), info.model_name(), info.total_size_in_bytes(),
670 base::Time::Now(), 0, 0, 0);
673 bool MediaGalleriesPreferences::LookUpGalleryByPath(
674 const base::FilePath& path,
675 MediaGalleryPrefInfo* gallery_info) const {
676 DCHECK(IsInitialized());
677 StorageInfo info;
678 base::FilePath relative_path;
679 if (!MediaStorageUtil::GetDeviceInfoFromPath(path, &info, &relative_path)) {
680 if (gallery_info)
681 *gallery_info = MediaGalleryPrefInfo();
682 return false;
685 relative_path = relative_path.NormalizePathSeparators();
686 MediaGalleryPrefIdSet galleries_on_device =
687 LookUpGalleriesByDeviceId(info.device_id());
688 for (MediaGalleryPrefIdSet::const_iterator it = galleries_on_device.begin();
689 it != galleries_on_device.end();
690 ++it) {
691 const MediaGalleryPrefInfo& gallery = known_galleries_.find(*it)->second;
692 if (gallery.path != relative_path)
693 continue;
695 if (gallery_info)
696 *gallery_info = gallery;
697 return true;
700 // This method is called by controller::FilesSelected when the user
701 // adds a new gallery. Control reaches here when the selected gallery is
702 // on a volume we know about, but have no gallery already for. Returns
703 // hypothetical data to the caller about what the prefs will look like
704 // if the gallery is added.
705 // TODO(gbillock): split this out into another function so it doesn't
706 // conflate LookUp.
707 if (gallery_info) {
708 gallery_info->pref_id = kInvalidMediaGalleryPrefId;
709 gallery_info->device_id = info.device_id();
710 gallery_info->path = relative_path;
711 gallery_info->type = MediaGalleryPrefInfo::kInvalidType;
712 gallery_info->volume_label = info.storage_label();
713 gallery_info->vendor_name = info.vendor_name();
714 gallery_info->model_name = info.model_name();
715 gallery_info->total_size_in_bytes = info.total_size_in_bytes();
716 gallery_info->last_attach_time = base::Time::Now();
717 gallery_info->volume_metadata_valid = true;
718 gallery_info->prefs_version = kCurrentPrefsVersion;
720 return false;
723 MediaGalleryPrefIdSet MediaGalleriesPreferences::LookUpGalleriesByDeviceId(
724 const std::string& device_id) const {
725 DeviceIdPrefIdsMap::const_iterator found = device_map_.find(device_id);
726 if (found == device_map_.end())
727 return MediaGalleryPrefIdSet();
728 return found->second;
731 base::FilePath MediaGalleriesPreferences::LookUpGalleryPathForExtension(
732 MediaGalleryPrefId gallery_id,
733 const extensions::Extension* extension,
734 bool include_unpermitted_galleries) {
735 DCHECK(IsInitialized());
736 DCHECK(extension);
737 if (!include_unpermitted_galleries &&
738 !ContainsKey(GalleriesForExtension(*extension), gallery_id))
739 return base::FilePath();
741 MediaGalleriesPrefInfoMap::const_iterator it =
742 known_galleries_.find(gallery_id);
743 if (it == known_galleries_.end())
744 return base::FilePath();
745 return MediaStorageUtil::FindDevicePathById(it->second.device_id);
748 MediaGalleryPrefId MediaGalleriesPreferences::AddGallery(
749 const std::string& device_id,
750 const base::FilePath& relative_path,
751 MediaGalleryPrefInfo::Type type,
752 const base::string16& volume_label,
753 const base::string16& vendor_name,
754 const base::string16& model_name,
755 uint64 total_size_in_bytes,
756 base::Time last_attach_time,
757 int image_count,
758 int music_count,
759 int video_count) {
760 DCHECK(IsInitialized());
761 return AddGalleryInternal(device_id, base::string16(), relative_path,
762 type, volume_label, vendor_name, model_name,
763 total_size_in_bytes, last_attach_time, true,
764 image_count, music_count, video_count,
765 kCurrentPrefsVersion);
768 MediaGalleryPrefId MediaGalleriesPreferences::AddGalleryInternal(
769 const std::string& device_id, const base::string16& display_name,
770 const base::FilePath& relative_path, MediaGalleryPrefInfo::Type type,
771 const base::string16& volume_label, const base::string16& vendor_name,
772 const base::string16& model_name, uint64 total_size_in_bytes,
773 base::Time last_attach_time, bool volume_metadata_valid,
774 int image_count, int music_count, int video_count, int prefs_version) {
775 DCHECK(type == MediaGalleryPrefInfo::kUserAdded ||
776 type == MediaGalleryPrefInfo::kAutoDetected ||
777 type == MediaGalleryPrefInfo::kScanResult);
778 base::FilePath normalized_relative_path =
779 relative_path.NormalizePathSeparators();
780 MediaGalleryPrefIdSet galleries_on_device =
781 LookUpGalleriesByDeviceId(device_id);
782 for (MediaGalleryPrefIdSet::const_iterator pref_id_it =
783 galleries_on_device.begin();
784 pref_id_it != galleries_on_device.end();
785 ++pref_id_it) {
786 const MediaGalleryPrefInfo& existing =
787 known_galleries_.find(*pref_id_it)->second;
788 if (existing.path != normalized_relative_path)
789 continue;
791 bool update_gallery_type = false;
792 MediaGalleryPrefInfo::Type new_type = existing.type;
793 if (type == MediaGalleryPrefInfo::kUserAdded) {
794 if (existing.type == MediaGalleryPrefInfo::kBlackListed) {
795 new_type = MediaGalleryPrefInfo::kAutoDetected;
796 update_gallery_type = true;
798 if (existing.type == MediaGalleryPrefInfo::kRemovedScan) {
799 new_type = MediaGalleryPrefInfo::kUserAdded;
800 update_gallery_type = true;
804 // Status quo: In M27 and M28, galleries added manually use version 0,
805 // and galleries added automatically (including default galleries) use
806 // version 1. The name override is used by default galleries as well
807 // as all device attach events.
808 // We want to upgrade the name if the existing version is < 2. Leave it
809 // alone if the existing display name is set with version == 2 and the
810 // proposed new name is empty.
811 bool update_gallery_name = existing.display_name != display_name;
812 if (existing.prefs_version == 2 && !existing.display_name.empty() &&
813 display_name.empty()) {
814 update_gallery_name = false;
816 bool update_gallery_metadata = volume_metadata_valid &&
817 ((existing.volume_label != volume_label) ||
818 (existing.vendor_name != vendor_name) ||
819 (existing.model_name != model_name) ||
820 (existing.total_size_in_bytes != total_size_in_bytes) ||
821 (existing.last_attach_time != last_attach_time));
823 bool update_scan_counts =
824 (new_type == MediaGalleryPrefInfo::kScanResult) &&
825 (image_count > 0 || music_count > 0 || video_count > 0);
827 if (!update_gallery_name && !update_gallery_type &&
828 !update_gallery_metadata && !update_scan_counts)
829 return *pref_id_it;
831 PrefService* prefs = profile_->GetPrefs();
832 scoped_ptr<ListPrefUpdate> update(
833 new ListPrefUpdate(prefs, prefs::kMediaGalleriesRememberedGalleries));
834 base::ListValue* list = update->Get();
836 for (base::ListValue::const_iterator list_iter = list->begin();
837 list_iter != list->end();
838 ++list_iter) {
839 base::DictionaryValue* dict;
840 MediaGalleryPrefId iter_id;
841 if ((*list_iter)->GetAsDictionary(&dict) &&
842 GetPrefId(*dict, &iter_id) &&
843 *pref_id_it == iter_id) {
844 if (update_gallery_type)
845 dict->SetString(kMediaGalleriesTypeKey, TypeToStringValue(new_type));
846 if (update_gallery_name)
847 dict->SetString(kMediaGalleriesDisplayNameKey, display_name);
848 if (update_gallery_metadata) {
849 dict->SetString(kMediaGalleriesVolumeLabelKey, volume_label);
850 dict->SetString(kMediaGalleriesVendorNameKey, vendor_name);
851 dict->SetString(kMediaGalleriesModelNameKey, model_name);
852 dict->SetDouble(kMediaGalleriesSizeKey, total_size_in_bytes);
853 dict->SetDouble(kMediaGalleriesLastAttachTimeKey,
854 last_attach_time.ToInternalValue());
856 if (update_scan_counts) {
857 dict->SetInteger(kMediaGalleriesScanImageCountKey, image_count);
858 dict->SetInteger(kMediaGalleriesScanMusicCountKey, music_count);
859 dict->SetInteger(kMediaGalleriesScanVideoCountKey, video_count);
861 dict->SetInteger(kMediaGalleriesPrefsVersionKey, prefs_version);
862 break;
866 // Commits the prefs update.
867 update.reset();
869 InitFromPrefs();
870 FOR_EACH_OBSERVER(GalleryChangeObserver, gallery_change_observers_,
871 OnGalleryInfoUpdated(this, *pref_id_it));
872 return *pref_id_it;
875 PrefService* prefs = profile_->GetPrefs();
877 MediaGalleryPrefInfo gallery_info;
878 gallery_info.pref_id = prefs->GetUint64(prefs::kMediaGalleriesUniqueId);
879 prefs->SetUint64(prefs::kMediaGalleriesUniqueId, gallery_info.pref_id + 1);
880 gallery_info.display_name = display_name;
881 gallery_info.device_id = device_id;
882 gallery_info.path = normalized_relative_path;
883 gallery_info.type = type;
884 gallery_info.volume_label = volume_label;
885 gallery_info.vendor_name = vendor_name;
886 gallery_info.model_name = model_name;
887 gallery_info.total_size_in_bytes = total_size_in_bytes;
888 gallery_info.last_attach_time = last_attach_time;
889 gallery_info.volume_metadata_valid = volume_metadata_valid;
890 gallery_info.image_count = image_count;
891 gallery_info.music_count = music_count;
892 gallery_info.video_count = video_count;
893 gallery_info.prefs_version = prefs_version;
896 ListPrefUpdate update(prefs, prefs::kMediaGalleriesRememberedGalleries);
897 base::ListValue* list = update.Get();
898 list->Append(CreateGalleryPrefInfoDictionary(gallery_info));
900 InitFromPrefs();
901 FOR_EACH_OBSERVER(GalleryChangeObserver,
902 gallery_change_observers_,
903 OnGalleryAdded(this, gallery_info.pref_id));
905 return gallery_info.pref_id;
908 MediaGalleryPrefId MediaGalleriesPreferences::AddGalleryByPath(
909 const base::FilePath& path, MediaGalleryPrefInfo::Type type) {
910 DCHECK(IsInitialized());
911 MediaGalleryPrefInfo gallery_info;
912 if (LookUpGalleryByPath(path, &gallery_info) &&
913 !gallery_info.IsBlackListedType()) {
914 return gallery_info.pref_id;
916 return AddGalleryInternal(gallery_info.device_id,
917 gallery_info.display_name,
918 gallery_info.path,
919 type,
920 gallery_info.volume_label,
921 gallery_info.vendor_name,
922 gallery_info.model_name,
923 gallery_info.total_size_in_bytes,
924 gallery_info.last_attach_time,
925 gallery_info.volume_metadata_valid,
926 0, 0, 0,
927 kCurrentPrefsVersion);
930 void MediaGalleriesPreferences::ForgetGalleryById(MediaGalleryPrefId pref_id) {
931 DCHECK(IsInitialized());
932 PrefService* prefs = profile_->GetPrefs();
933 scoped_ptr<ListPrefUpdate> update(new ListPrefUpdate(
934 prefs, prefs::kMediaGalleriesRememberedGalleries));
935 base::ListValue* list = update->Get();
937 if (!ContainsKey(known_galleries_, pref_id))
938 return;
940 for (base::ListValue::iterator iter = list->begin();
941 iter != list->end(); ++iter) {
942 base::DictionaryValue* dict;
943 MediaGalleryPrefId iter_id;
944 if ((*iter)->GetAsDictionary(&dict) && GetPrefId(*dict, &iter_id) &&
945 pref_id == iter_id) {
946 RemoveGalleryPermissionsFromPrefs(pref_id);
947 MediaGalleryPrefInfo::Type type;
948 if (GetType(*dict, &type) &&
949 (type == MediaGalleryPrefInfo::kAutoDetected ||
950 type == MediaGalleryPrefInfo::kScanResult)) {
951 if (type == MediaGalleryPrefInfo::kAutoDetected) {
952 dict->SetString(kMediaGalleriesTypeKey,
953 kMediaGalleriesTypeBlackListedValue);
954 } else {
955 dict->SetString(kMediaGalleriesTypeKey,
956 kMediaGalleriesTypeRemovedScanValue);
957 dict->SetInteger(kMediaGalleriesScanImageCountKey, 0);
958 dict->SetInteger(kMediaGalleriesScanMusicCountKey, 0);
959 dict->SetInteger(kMediaGalleriesScanVideoCountKey, 0);
961 } else {
962 list->Erase(iter, NULL);
964 update.reset(NULL); // commits the update.
966 InitFromPrefs();
967 FOR_EACH_OBSERVER(GalleryChangeObserver,
968 gallery_change_observers_,
969 OnGalleryRemoved(this, pref_id));
970 return;
975 MediaGalleryPrefIdSet MediaGalleriesPreferences::GalleriesForExtension(
976 const extensions::Extension& extension) const {
977 DCHECK(IsInitialized());
978 MediaGalleryPrefIdSet result;
980 if (HasAutoDetectedGalleryPermission(extension)) {
981 for (MediaGalleriesPrefInfoMap::const_iterator it =
982 known_galleries_.begin(); it != known_galleries_.end(); ++it) {
983 if (it->second.type == MediaGalleryPrefInfo::kAutoDetected)
984 result.insert(it->second.pref_id);
988 std::vector<MediaGalleryPermission> stored_permissions =
989 GetGalleryPermissionsFromPrefs(extension.id());
990 for (std::vector<MediaGalleryPermission>::const_iterator it =
991 stored_permissions.begin(); it != stored_permissions.end(); ++it) {
992 if (!it->has_permission) {
993 result.erase(it->pref_id);
994 } else {
995 MediaGalleriesPrefInfoMap::const_iterator gallery =
996 known_galleries_.find(it->pref_id);
997 DCHECK(gallery != known_galleries_.end());
998 if (!gallery->second.IsBlackListedType()) {
999 result.insert(it->pref_id);
1000 } else {
1001 NOTREACHED() << gallery->second.device_id;
1005 return result;
1008 bool MediaGalleriesPreferences::SetGalleryPermissionForExtension(
1009 const extensions::Extension& extension,
1010 MediaGalleryPrefId pref_id,
1011 bool has_permission) {
1012 DCHECK(IsInitialized());
1013 // The gallery may not exist anymore if the user opened a second config
1014 // surface concurrently and removed it. Drop the permission update if so.
1015 MediaGalleriesPrefInfoMap::const_iterator gallery_info =
1016 known_galleries_.find(pref_id);
1017 if (gallery_info == known_galleries_.end())
1018 return false;
1020 bool default_permission = false;
1021 if (gallery_info->second.type == MediaGalleryPrefInfo::kAutoDetected)
1022 default_permission = HasAutoDetectedGalleryPermission(extension);
1023 // When the permission matches the default, we don't need to remember it.
1024 if (has_permission == default_permission) {
1025 if (!UnsetGalleryPermissionInPrefs(extension.id(), pref_id))
1026 // If permission wasn't set, assume nothing has changed.
1027 return false;
1028 } else {
1029 if (!SetGalleryPermissionInPrefs(extension.id(), pref_id, has_permission))
1030 return false;
1032 if (has_permission)
1033 FOR_EACH_OBSERVER(GalleryChangeObserver,
1034 gallery_change_observers_,
1035 OnPermissionAdded(this, extension.id(), pref_id));
1036 else
1037 FOR_EACH_OBSERVER(GalleryChangeObserver,
1038 gallery_change_observers_,
1039 OnPermissionRemoved(this, extension.id(), pref_id));
1040 return true;
1043 const MediaGalleriesPrefInfoMap& MediaGalleriesPreferences::known_galleries()
1044 const {
1045 DCHECK(IsInitialized());
1046 return known_galleries_;
1049 void MediaGalleriesPreferences::Shutdown() {
1050 weak_factory_.InvalidateWeakPtrs();
1051 profile_ = NULL;
1054 // static
1055 bool MediaGalleriesPreferences::APIHasBeenUsed(Profile* profile) {
1056 MediaGalleryPrefId current_id =
1057 profile->GetPrefs()->GetUint64(prefs::kMediaGalleriesUniqueId);
1058 return current_id != kInvalidMediaGalleryPrefId + 1;
1061 // static
1062 void MediaGalleriesPreferences::RegisterProfilePrefs(
1063 user_prefs::PrefRegistrySyncable* registry) {
1064 registry->RegisterListPref(prefs::kMediaGalleriesRememberedGalleries,
1065 user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
1066 registry->RegisterUint64Pref(
1067 prefs::kMediaGalleriesUniqueId,
1068 kInvalidMediaGalleryPrefId + 1,
1069 user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
1072 bool MediaGalleriesPreferences::SetGalleryPermissionInPrefs(
1073 const std::string& extension_id,
1074 MediaGalleryPrefId gallery_id,
1075 bool has_access) {
1076 DCHECK(IsInitialized());
1077 ExtensionPrefs::ScopedListUpdate update(GetExtensionPrefs(),
1078 extension_id,
1079 kMediaGalleriesPermissions);
1080 base::ListValue* permissions = update.Get();
1081 if (!permissions) {
1082 permissions = update.Create();
1083 } else {
1084 // If the gallery is already in the list, update the permission...
1085 for (base::ListValue::iterator iter = permissions->begin();
1086 iter != permissions->end(); ++iter) {
1087 base::DictionaryValue* dict = NULL;
1088 if (!(*iter)->GetAsDictionary(&dict))
1089 continue;
1090 MediaGalleryPermission perm;
1091 if (!GetMediaGalleryPermissionFromDictionary(dict, &perm))
1092 continue;
1093 if (perm.pref_id == gallery_id) {
1094 if (has_access != perm.has_permission) {
1095 dict->SetBoolean(kMediaGalleryHasPermissionKey, has_access);
1096 return true;
1097 } else {
1098 return false;
1103 // ...Otherwise, add a new entry for the gallery.
1104 base::DictionaryValue* dict = new base::DictionaryValue;
1105 dict->SetString(kMediaGalleryIdKey, base::Uint64ToString(gallery_id));
1106 dict->SetBoolean(kMediaGalleryHasPermissionKey, has_access);
1107 permissions->Append(dict);
1108 return true;
1111 bool MediaGalleriesPreferences::UnsetGalleryPermissionInPrefs(
1112 const std::string& extension_id,
1113 MediaGalleryPrefId gallery_id) {
1114 DCHECK(IsInitialized());
1115 ExtensionPrefs::ScopedListUpdate update(GetExtensionPrefs(),
1116 extension_id,
1117 kMediaGalleriesPermissions);
1118 base::ListValue* permissions = update.Get();
1119 if (!permissions)
1120 return false;
1122 for (base::ListValue::iterator iter = permissions->begin();
1123 iter != permissions->end(); ++iter) {
1124 const base::DictionaryValue* dict = NULL;
1125 if (!(*iter)->GetAsDictionary(&dict))
1126 continue;
1127 MediaGalleryPermission perm;
1128 if (!GetMediaGalleryPermissionFromDictionary(dict, &perm))
1129 continue;
1130 if (perm.pref_id == gallery_id) {
1131 permissions->Erase(iter, NULL);
1132 return true;
1135 return false;
1138 std::vector<MediaGalleryPermission>
1139 MediaGalleriesPreferences::GetGalleryPermissionsFromPrefs(
1140 const std::string& extension_id) const {
1141 DCHECK(IsInitialized());
1142 std::vector<MediaGalleryPermission> result;
1143 const base::ListValue* permissions;
1144 if (!GetExtensionPrefs()->ReadPrefAsList(extension_id,
1145 kMediaGalleriesPermissions,
1146 &permissions)) {
1147 return result;
1150 for (base::ListValue::const_iterator iter = permissions->begin();
1151 iter != permissions->end(); ++iter) {
1152 base::DictionaryValue* dict = NULL;
1153 if (!(*iter)->GetAsDictionary(&dict))
1154 continue;
1155 MediaGalleryPermission perm;
1156 if (!GetMediaGalleryPermissionFromDictionary(dict, &perm))
1157 continue;
1158 result.push_back(perm);
1161 return result;
1164 void MediaGalleriesPreferences::RemoveGalleryPermissionsFromPrefs(
1165 MediaGalleryPrefId gallery_id) {
1166 DCHECK(IsInitialized());
1167 ExtensionPrefs* prefs = GetExtensionPrefs();
1168 const base::DictionaryValue* extensions =
1169 prefs->pref_service()->GetDictionary(extensions::pref_names::kExtensions);
1170 if (!extensions)
1171 return;
1173 for (base::DictionaryValue::Iterator iter(*extensions); !iter.IsAtEnd();
1174 iter.Advance()) {
1175 if (!extensions::Extension::IdIsValid(iter.key())) {
1176 NOTREACHED();
1177 continue;
1179 UnsetGalleryPermissionInPrefs(iter.key(), gallery_id);
1183 ExtensionPrefs* MediaGalleriesPreferences::GetExtensionPrefs() const {
1184 DCHECK(IsInitialized());
1185 if (extension_prefs_for_testing_)
1186 return extension_prefs_for_testing_;
1187 return extensions::ExtensionPrefs::Get(profile_);
1190 void MediaGalleriesPreferences::SetExtensionPrefsForTesting(
1191 extensions::ExtensionPrefs* extension_prefs) {
1192 DCHECK(IsInitialized());
1193 extension_prefs_for_testing_ = extension_prefs;