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/media_galleries/fileapi/iapps_finder.h"
22 #include "chrome/browser/media_galleries/fileapi/picasa_finder.h"
23 #include "chrome/browser/media_galleries/imported_media_gallery_registry.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/common/chrome_paths.h"
28 #include "chrome/common/pref_names.h"
29 #include "components/storage_monitor/media_storage_util.h"
30 #include "components/storage_monitor/storage_monitor.h"
31 #include "components/user_prefs/pref_registry_syncable.h"
32 #include "content/public/browser/browser_thread.h"
33 #include "extensions/browser/extension_system.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/media_galleries_permission.h"
39 #include "extensions/common/permissions/permissions_data.h"
40 #include "grit/generated_resources.h"
41 #include "ui/base/l10n/l10n_util.h"
43 using base::DictionaryValue;
44 using base::ListValue;
45 using extensions::ExtensionPrefs;
46 using storage_monitor::MediaStorageUtil;
47 using storage_monitor::StorageInfo;
48 using storage_monitor::StorageMonitor;
52 // Pref key for the list of media gallery permissions.
53 const char kMediaGalleriesPermissions[] = "media_galleries_permissions";
54 // Pref key for Media Gallery ID.
55 const char kMediaGalleryIdKey[] = "id";
56 // Pref key for Media Gallery Permission Value.
57 const char kMediaGalleryHasPermissionKey[] = "has_permission";
59 const char kMediaGalleriesDeviceIdKey[] = "deviceId";
60 const char kMediaGalleriesDisplayNameKey[] = "displayName";
61 const char kMediaGalleriesPathKey[] = "path";
62 const char kMediaGalleriesPrefIdKey[] = "prefId";
63 const char kMediaGalleriesTypeKey[] = "type";
64 const char kMediaGalleriesVolumeLabelKey[] = "volumeLabel";
65 const char kMediaGalleriesVendorNameKey[] = "vendorName";
66 const char kMediaGalleriesModelNameKey[] = "modelName";
67 const char kMediaGalleriesSizeKey[] = "totalSize";
68 const char kMediaGalleriesLastAttachTimeKey[] = "lastAttachTime";
69 const char kMediaGalleriesScanAudioCountKey[] = "audioCount";
70 const char kMediaGalleriesScanImageCountKey[] = "imageCount";
71 const char kMediaGalleriesScanVideoCountKey[] = "videoCount";
72 const char kMediaGalleriesPrefsVersionKey[] = "preferencesVersion";
74 const char kMediaGalleriesTypeAutoDetectedValue[] = "autoDetected";
75 const char kMediaGalleriesTypeBlackListedValue[] = "blackListed";
76 const char kMediaGalleriesTypeRemovedScanValue[] = "removedScan";
77 const char kMediaGalleriesTypeScanResultValue[] = "scanResult";
78 const char kMediaGalleriesTypeUserAddedValue[] = "userAdded";
80 const char kIPhotoGalleryName[] = "iPhoto";
81 const char kITunesGalleryName[] = "iTunes";
82 const char kPicasaGalleryName[] = "Picasa";
84 const int kCurrentPrefsVersion = 2;
86 int NumberExtensionsUsingMediaGalleries(Profile* profile) {
90 ExtensionService* extension_service =
91 extensions::ExtensionSystem::Get(profile)->extension_service();
92 if (!extension_service)
95 const extensions::ExtensionSet* extensions = extension_service->extensions();
96 for (extensions::ExtensionSet::const_iterator i = extensions->begin();
97 i != extensions->end(); ++i) {
98 if (extensions::PermissionsData::HasAPIPermission(
99 *i, extensions::APIPermission::kMediaGalleries) ||
100 extensions::PermissionsData::HasAPIPermission(
101 *i, extensions::APIPermission::kMediaGalleriesPrivate)) {
108 bool GetPrefId(const base::DictionaryValue& dict, MediaGalleryPrefId* value) {
109 std::string string_id;
110 if (!dict.GetString(kMediaGalleriesPrefIdKey, &string_id) ||
111 !base::StringToUint64(string_id, value)) {
118 bool GetType(const base::DictionaryValue& dict,
119 MediaGalleryPrefInfo::Type* type) {
120 std::string string_type;
121 if (!dict.GetString(kMediaGalleriesTypeKey, &string_type))
124 if (string_type == kMediaGalleriesTypeUserAddedValue) {
125 *type = MediaGalleryPrefInfo::kUserAdded;
128 if (string_type == kMediaGalleriesTypeAutoDetectedValue) {
129 *type = MediaGalleryPrefInfo::kAutoDetected;
132 if (string_type == kMediaGalleriesTypeBlackListedValue) {
133 *type = MediaGalleryPrefInfo::kBlackListed;
136 if (string_type == kMediaGalleriesTypeScanResultValue) {
137 *type = MediaGalleryPrefInfo::kScanResult;
140 if (string_type == kMediaGalleriesTypeRemovedScanValue) {
141 *type = MediaGalleryPrefInfo::kRemovedScan;
148 const char* TypeToStringValue(MediaGalleryPrefInfo::Type type) {
149 const char* result = NULL;
151 case MediaGalleryPrefInfo::kUserAdded:
152 result = kMediaGalleriesTypeUserAddedValue;
154 case MediaGalleryPrefInfo::kAutoDetected:
155 result = kMediaGalleriesTypeAutoDetectedValue;
157 case MediaGalleryPrefInfo::kBlackListed:
158 result = kMediaGalleriesTypeBlackListedValue;
160 case MediaGalleryPrefInfo::kScanResult:
161 result = kMediaGalleriesTypeScanResultValue;
163 case MediaGalleryPrefInfo::kRemovedScan:
164 result = kMediaGalleriesTypeRemovedScanValue;
173 bool PopulateGalleryPrefInfoFromDictionary(
174 const base::DictionaryValue& dict, MediaGalleryPrefInfo* out_gallery_info) {
175 MediaGalleryPrefId pref_id;
176 base::string16 display_name;
177 std::string device_id;
178 base::FilePath::StringType path;
179 MediaGalleryPrefInfo::Type type = MediaGalleryPrefInfo::kInvalidType;
180 base::string16 volume_label;
181 base::string16 vendor_name;
182 base::string16 model_name;
183 double total_size_in_bytes = 0.0;
184 double last_attach_time = 0.0;
185 bool volume_metadata_valid = false;
189 int prefs_version = 0;
191 if (!GetPrefId(dict, &pref_id) ||
192 !dict.GetString(kMediaGalleriesDeviceIdKey, &device_id) ||
193 !dict.GetString(kMediaGalleriesPathKey, &path) ||
194 !GetType(dict, &type)) {
198 dict.GetString(kMediaGalleriesDisplayNameKey, &display_name);
199 dict.GetInteger(kMediaGalleriesPrefsVersionKey, &prefs_version);
201 if (dict.GetString(kMediaGalleriesVolumeLabelKey, &volume_label) &&
202 dict.GetString(kMediaGalleriesVendorNameKey, &vendor_name) &&
203 dict.GetString(kMediaGalleriesModelNameKey, &model_name) &&
204 dict.GetDouble(kMediaGalleriesSizeKey, &total_size_in_bytes) &&
205 dict.GetDouble(kMediaGalleriesLastAttachTimeKey, &last_attach_time)) {
206 volume_metadata_valid = true;
209 if (dict.GetInteger(kMediaGalleriesScanAudioCountKey, &audio_count) &&
210 dict.GetInteger(kMediaGalleriesScanImageCountKey, &image_count) &&
211 dict.GetInteger(kMediaGalleriesScanVideoCountKey, &video_count)) {
212 out_gallery_info->audio_count = audio_count;
213 out_gallery_info->image_count = image_count;
214 out_gallery_info->video_count = video_count;
216 out_gallery_info->audio_count = 0;
217 out_gallery_info->image_count = 0;
218 out_gallery_info->video_count = 0;
221 out_gallery_info->pref_id = pref_id;
222 out_gallery_info->display_name = display_name;
223 out_gallery_info->device_id = device_id;
224 out_gallery_info->path = base::FilePath(path);
225 out_gallery_info->type = type;
226 out_gallery_info->volume_label = volume_label;
227 out_gallery_info->vendor_name = vendor_name;
228 out_gallery_info->model_name = model_name;
229 out_gallery_info->total_size_in_bytes = total_size_in_bytes;
230 out_gallery_info->last_attach_time =
231 base::Time::FromInternalValue(last_attach_time);
232 out_gallery_info->volume_metadata_valid = volume_metadata_valid;
233 out_gallery_info->prefs_version = prefs_version;
238 base::DictionaryValue* CreateGalleryPrefInfoDictionary(
239 const MediaGalleryPrefInfo& gallery) {
240 base::DictionaryValue* dict = new base::DictionaryValue();
241 dict->SetString(kMediaGalleriesPrefIdKey,
242 base::Uint64ToString(gallery.pref_id));
243 dict->SetString(kMediaGalleriesDeviceIdKey, gallery.device_id);
244 dict->SetString(kMediaGalleriesPathKey, gallery.path.value());
245 dict->SetString(kMediaGalleriesTypeKey, TypeToStringValue(gallery.type));
247 if (gallery.volume_metadata_valid) {
248 dict->SetString(kMediaGalleriesVolumeLabelKey, gallery.volume_label);
249 dict->SetString(kMediaGalleriesVendorNameKey, gallery.vendor_name);
250 dict->SetString(kMediaGalleriesModelNameKey, gallery.model_name);
251 dict->SetDouble(kMediaGalleriesSizeKey, gallery.total_size_in_bytes);
252 dict->SetDouble(kMediaGalleriesLastAttachTimeKey,
253 gallery.last_attach_time.ToInternalValue());
255 dict->SetString(kMediaGalleriesDisplayNameKey, gallery.display_name);
258 if (gallery.audio_count || gallery.image_count || gallery.video_count) {
259 dict->SetInteger(kMediaGalleriesScanAudioCountKey, gallery.audio_count);
260 dict->SetInteger(kMediaGalleriesScanImageCountKey, gallery.image_count);
261 dict->SetInteger(kMediaGalleriesScanVideoCountKey, gallery.video_count);
264 // Version 0 of the prefs format was that the display_name was always
265 // used to show the user-visible name of the gallery. Version 1 means
266 // that there is an optional display_name, and when it is present, it
267 // overrides the name that would be built from the volume metadata, path,
268 // or whatever other data. So if we see a display_name with version 0, it
269 // means it may be overwritten simply by getting new volume metadata.
270 // A display_name with version 1 should not be overwritten.
271 dict->SetInteger(kMediaGalleriesPrefsVersionKey, gallery.prefs_version);
276 bool HasAutoDetectedGalleryPermission(const extensions::Extension& extension) {
277 extensions::MediaGalleriesPermission::CheckParam param(
278 extensions::MediaGalleriesPermission::kAllAutoDetectedPermission);
279 return extensions::PermissionsData::CheckAPIPermissionWithParam(
280 &extension, extensions::APIPermission::kMediaGalleries, ¶m);
283 // Retrieves the MediaGalleryPermission from the given dictionary; DCHECKs on
285 bool GetMediaGalleryPermissionFromDictionary(
286 const base::DictionaryValue* dict,
287 MediaGalleryPermission* out_permission) {
288 std::string string_id;
289 if (dict->GetString(kMediaGalleryIdKey, &string_id) &&
290 base::StringToUint64(string_id, &out_permission->pref_id) &&
291 dict->GetBoolean(kMediaGalleryHasPermissionKey,
292 &out_permission->has_permission)) {
299 // For a device with |device_name| and a relative path |sub_folder|, construct
300 // a display name. If |sub_folder| is empty, then just return |device_name|.
301 base::string16 GetDisplayNameForSubFolder(const base::string16& device_name,
302 const base::FilePath& sub_folder) {
303 if (sub_folder.empty())
305 return (sub_folder.BaseName().LossyDisplayName() +
306 base::ASCIIToUTF16(" - ") +
310 void InitializeImportedMediaGalleryRegistryOnFileThread() {
311 DCHECK_CURRENTLY_ON(content::BrowserThread::FILE);
312 ImportedMediaGalleryRegistry::GetInstance()->Initialize();
317 MediaGalleryPrefInfo::MediaGalleryPrefInfo()
318 : pref_id(kInvalidMediaGalleryPrefId),
320 total_size_in_bytes(0),
321 volume_metadata_valid(false),
328 MediaGalleryPrefInfo::~MediaGalleryPrefInfo() {}
330 base::FilePath MediaGalleryPrefInfo::AbsolutePath() const {
331 base::FilePath base_path = MediaStorageUtil::FindDevicePathById(device_id);
332 DCHECK(!path.IsAbsolute());
333 return base_path.empty() ? base_path : base_path.Append(path);
336 bool MediaGalleryPrefInfo::IsBlackListedType() const {
337 return type == kBlackListed || type == kRemovedScan;
340 base::string16 MediaGalleryPrefInfo::GetGalleryDisplayName() const {
341 if (!StorageInfo::IsRemovableDevice(device_id)) {
342 // For fixed storage, the default name is the fully qualified directory
343 // name, or in the case of a root directory, the root directory name.
344 // Exception: ChromeOS -- the full pathname isn't visible there, so only
345 // the directory name is used.
346 base::FilePath path = AbsolutePath();
347 if (!display_name.empty())
350 #if defined(OS_CHROMEOS)
351 // See chrome/browser/chromeos/fileapi/file_system_backend.cc
352 base::FilePath download_path;
353 if (PathService::Get(chrome::DIR_DEFAULT_DOWNLOADS_SAFE, &download_path)) {
354 base::FilePath relative;
355 if (download_path.AppendRelativePath(path, &relative))
356 return relative.LossyDisplayName();
358 return path.BaseName().LossyDisplayName();
360 return path.LossyDisplayName();
364 StorageInfo info(device_id,
365 MediaStorageUtil::FindDevicePathById(device_id).value(),
366 volume_label, vendor_name, model_name, total_size_in_bytes);
367 base::string16 name = info.GetDisplayNameWithOverride(display_name, true);
369 name = GetDisplayNameForSubFolder(name, path);
373 base::string16 MediaGalleryPrefInfo::GetGalleryTooltip() const {
374 return AbsolutePath().LossyDisplayName();
377 base::string16 MediaGalleryPrefInfo::GetGalleryAdditionalDetails() const {
378 base::string16 attached;
379 if (StorageInfo::IsRemovableDevice(device_id)) {
380 if (MediaStorageUtil::IsRemovableStorageAttached(device_id)) {
381 attached = l10n_util::GetStringUTF16(
382 IDS_MEDIA_GALLERIES_DIALOG_DEVICE_ATTACHED);
383 } else if (!last_attach_time.is_null()) {
384 attached = l10n_util::GetStringFUTF16(
385 IDS_MEDIA_GALLERIES_LAST_ATTACHED,
386 base::TimeFormatShortDateNumeric(last_attach_time));
388 attached = l10n_util::GetStringUTF16(
389 IDS_MEDIA_GALLERIES_DIALOG_DEVICE_NOT_ATTACHED);
396 bool MediaGalleryPrefInfo::IsGalleryAvailable() const {
397 return !StorageInfo::IsRemovableDevice(device_id) ||
398 MediaStorageUtil::IsRemovableStorageAttached(device_id);
401 MediaGalleriesPreferences::GalleryChangeObserver::~GalleryChangeObserver() {}
403 MediaGalleriesPreferences::MediaGalleriesPreferences(Profile* profile)
404 : initialized_(false),
405 pre_initialization_callbacks_waiting_(0),
407 extension_prefs_for_testing_(NULL),
408 weak_factory_(this) {
411 MediaGalleriesPreferences::~MediaGalleriesPreferences() {
412 if (StorageMonitor::GetInstance())
413 StorageMonitor::GetInstance()->RemoveObserver(this);
416 void MediaGalleriesPreferences::EnsureInitialized(base::Closure callback) {
417 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
419 if (IsInitialized()) {
420 if (!callback.is_null())
425 on_initialize_callbacks_.push_back(callback);
426 if (on_initialize_callbacks_.size() > 1)
429 // This counter must match the number of async methods dispatched below.
430 // It cannot be incremented inline with each callback, as some may return
431 // synchronously, decrement the counter to 0, and prematurely trigger
432 // FinishInitialization.
433 pre_initialization_callbacks_waiting_ = 4;
435 // Check whether we should be initializing -- are there any extensions that
436 // are using media galleries?
437 media_galleries::UsageCount(media_galleries::PREFS_INITIALIZED);
438 if (NumberExtensionsUsingMediaGalleries(profile_) == 0) {
439 media_galleries::UsageCount(media_galleries::PREFS_INITIALIZED_ERROR);
442 // We determine the freshness of the profile here, before any of the finders
443 // return and add media galleries to it.
444 StorageMonitor::GetInstance()->EnsureInitialized(
445 base::Bind(&MediaGalleriesPreferences::OnStorageMonitorInit,
446 weak_factory_.GetWeakPtr(),
447 !APIHasBeenUsed(profile_) /* add_default_galleries */));
449 // Look for optional default galleries every time.
450 iapps::FindITunesLibrary(
451 base::Bind(&MediaGalleriesPreferences::OnFinderDeviceID,
452 weak_factory_.GetWeakPtr()));
454 picasa::FindPicasaDatabase(
455 base::Bind(&MediaGalleriesPreferences::OnFinderDeviceID,
456 weak_factory_.GetWeakPtr()));
458 iapps::FindIPhotoLibrary(
459 base::Bind(&MediaGalleriesPreferences::OnFinderDeviceID,
460 weak_factory_.GetWeakPtr()));
463 bool MediaGalleriesPreferences::IsInitialized() const { return initialized_; }
465 Profile* MediaGalleriesPreferences::profile() { return profile_; }
467 void MediaGalleriesPreferences::OnInitializationCallbackReturned() {
468 DCHECK(!IsInitialized());
469 DCHECK_GT(pre_initialization_callbacks_waiting_, 0);
470 if (--pre_initialization_callbacks_waiting_ == 0)
471 FinishInitialization();
474 void MediaGalleriesPreferences::FinishInitialization() {
475 DCHECK(!IsInitialized());
479 StorageMonitor* monitor = StorageMonitor::GetInstance();
480 DCHECK(monitor->IsInitialized());
484 StorageMonitor::GetInstance()->AddObserver(this);
486 std::vector<StorageInfo> existing_devices =
487 monitor->GetAllAvailableStorages();
488 for (size_t i = 0; i < existing_devices.size(); i++) {
489 if (!(StorageInfo::IsMediaDevice(existing_devices[i].device_id()) &&
490 StorageInfo::IsRemovableDevice(existing_devices[i].device_id())))
492 AddGallery(existing_devices[i].device_id(),
494 MediaGalleryPrefInfo::kAutoDetected,
495 existing_devices[i].storage_label(),
496 existing_devices[i].vendor_name(),
497 existing_devices[i].model_name(),
498 existing_devices[i].total_size_in_bytes(),
499 base::Time::Now(), 0, 0, 0);
502 for (std::vector<base::Closure>::iterator iter =
503 on_initialize_callbacks_.begin();
504 iter != on_initialize_callbacks_.end();
508 on_initialize_callbacks_.clear();
511 void MediaGalleriesPreferences::AddDefaultGalleries() {
512 const int kDirectoryKeys[] = {
513 chrome::DIR_USER_MUSIC,
514 chrome::DIR_USER_PICTURES,
515 chrome::DIR_USER_VIDEOS,
518 for (size_t i = 0; i < arraysize(kDirectoryKeys); ++i) {
520 if (!PathService::Get(kDirectoryKeys[i], &path))
523 base::FilePath relative_path;
525 if (MediaStorageUtil::GetDeviceInfoFromPath(path, &info, &relative_path)) {
526 AddGalleryInternal(info.device_id(), base::string16(), relative_path,
527 MediaGalleryPrefInfo::kAutoDetected,
528 info.storage_label(), info.vendor_name(),
529 info.model_name(), info.total_size_in_bytes(),
530 base::Time(), true, 0, 0, 0, kCurrentPrefsVersion);
535 bool MediaGalleriesPreferences::UpdateDeviceIDForSingletonType(
536 const std::string& device_id) {
537 StorageInfo::Type singleton_type;
538 if (!StorageInfo::CrackDeviceId(device_id, &singleton_type, NULL))
541 PrefService* prefs = profile_->GetPrefs();
542 scoped_ptr<ListPrefUpdate> update(new ListPrefUpdate(
543 prefs, prefs::kMediaGalleriesRememberedGalleries));
544 base::ListValue* list = update->Get();
545 for (base::ListValue::iterator iter = list->begin();
546 iter != list->end(); ++iter) {
547 // All of these calls should succeed, but preferences file can be corrupt.
548 base::DictionaryValue* dict;
549 if (!(*iter)->GetAsDictionary(&dict))
551 std::string this_device_id;
552 if (!dict->GetString(kMediaGalleriesDeviceIdKey, &this_device_id))
554 if (this_device_id == device_id)
555 return true; // No update is necessary.
556 StorageInfo::Type device_type;
557 if (!StorageInfo::CrackDeviceId(this_device_id, &device_type, NULL))
560 if (device_type == singleton_type) {
561 dict->SetString(kMediaGalleriesDeviceIdKey, device_id);
562 update.reset(); // commits the update.
564 MediaGalleryPrefId pref_id;
565 if (GetPrefId(*dict, &pref_id)) {
566 FOR_EACH_OBSERVER(GalleryChangeObserver,
567 gallery_change_observers_,
568 OnGalleryInfoUpdated(this, pref_id));
576 void MediaGalleriesPreferences::OnStorageMonitorInit(
577 bool add_default_galleries) {
578 if (add_default_galleries)
579 AddDefaultGalleries();
580 OnInitializationCallbackReturned();
583 void MediaGalleriesPreferences::OnFinderDeviceID(const std::string& device_id) {
584 if (!device_id.empty()) {
585 std::string gallery_name;
586 if (StorageInfo::IsIPhotoDevice(device_id))
587 gallery_name = kIPhotoGalleryName;
588 else if (StorageInfo::IsITunesDevice(device_id))
589 gallery_name = kITunesGalleryName;
590 else if (StorageInfo::IsPicasaDevice(device_id))
591 gallery_name = kPicasaGalleryName;
593 if (!gallery_name.empty()) {
594 pre_initialization_callbacks_waiting_++;
595 content::BrowserThread::PostTaskAndReply(
596 content::BrowserThread::FILE,
598 base::Bind(&InitializeImportedMediaGalleryRegistryOnFileThread),
600 &MediaGalleriesPreferences::OnInitializationCallbackReturned,
601 weak_factory_.GetWeakPtr()));
604 if (!UpdateDeviceIDForSingletonType(device_id)) {
605 DCHECK(!gallery_name.empty());
606 AddGalleryInternal(device_id, base::ASCIIToUTF16(gallery_name),
607 base::FilePath(), MediaGalleryPrefInfo::kAutoDetected,
608 base::string16(), base::string16(), base::string16(),
609 0, base::Time(), false, 0, 0, 0, kCurrentPrefsVersion);
613 OnInitializationCallbackReturned();
616 void MediaGalleriesPreferences::InitFromPrefs() {
617 known_galleries_.clear();
620 PrefService* prefs = profile_->GetPrefs();
621 const base::ListValue* list = prefs->GetList(
622 prefs::kMediaGalleriesRememberedGalleries);
624 for (base::ListValue::const_iterator it = list->begin();
625 it != list->end(); ++it) {
626 const base::DictionaryValue* dict = NULL;
627 if (!(*it)->GetAsDictionary(&dict))
630 MediaGalleryPrefInfo gallery_info;
631 if (!PopulateGalleryPrefInfoFromDictionary(*dict, &gallery_info))
634 known_galleries_[gallery_info.pref_id] = gallery_info;
635 device_map_[gallery_info.device_id].insert(gallery_info.pref_id);
640 void MediaGalleriesPreferences::AddGalleryChangeObserver(
641 GalleryChangeObserver* observer) {
642 DCHECK(IsInitialized());
643 gallery_change_observers_.AddObserver(observer);
646 void MediaGalleriesPreferences::RemoveGalleryChangeObserver(
647 GalleryChangeObserver* observer) {
648 DCHECK(IsInitialized());
649 gallery_change_observers_.RemoveObserver(observer);
652 void MediaGalleriesPreferences::OnRemovableStorageAttached(
653 const StorageInfo& info) {
654 DCHECK(IsInitialized());
655 if (!StorageInfo::IsMediaDevice(info.device_id()))
658 AddGallery(info.device_id(), base::FilePath(),
659 MediaGalleryPrefInfo::kAutoDetected, info.storage_label(),
660 info.vendor_name(), info.model_name(), info.total_size_in_bytes(),
661 base::Time::Now(), 0, 0, 0);
664 bool MediaGalleriesPreferences::LookUpGalleryByPath(
665 const base::FilePath& path,
666 MediaGalleryPrefInfo* gallery_info) const {
667 DCHECK(IsInitialized());
669 base::FilePath relative_path;
670 if (!MediaStorageUtil::GetDeviceInfoFromPath(path, &info, &relative_path)) {
672 *gallery_info = MediaGalleryPrefInfo();
676 relative_path = relative_path.NormalizePathSeparators();
677 MediaGalleryPrefIdSet galleries_on_device =
678 LookUpGalleriesByDeviceId(info.device_id());
679 for (MediaGalleryPrefIdSet::const_iterator it = galleries_on_device.begin();
680 it != galleries_on_device.end();
682 const MediaGalleryPrefInfo& gallery = known_galleries_.find(*it)->second;
683 if (gallery.path != relative_path)
687 *gallery_info = gallery;
691 // This method is called by controller::FilesSelected when the user
692 // adds a new gallery. Control reaches here when the selected gallery is
693 // on a volume we know about, but have no gallery already for. Returns
694 // hypothetical data to the caller about what the prefs will look like
695 // if the gallery is added.
696 // TODO(gbillock): split this out into another function so it doesn't
699 gallery_info->pref_id = kInvalidMediaGalleryPrefId;
700 gallery_info->device_id = info.device_id();
701 gallery_info->path = relative_path;
702 gallery_info->type = MediaGalleryPrefInfo::kInvalidType;
703 gallery_info->volume_label = info.storage_label();
704 gallery_info->vendor_name = info.vendor_name();
705 gallery_info->model_name = info.model_name();
706 gallery_info->total_size_in_bytes = info.total_size_in_bytes();
707 gallery_info->last_attach_time = base::Time::Now();
708 gallery_info->volume_metadata_valid = true;
709 gallery_info->prefs_version = kCurrentPrefsVersion;
714 MediaGalleryPrefIdSet MediaGalleriesPreferences::LookUpGalleriesByDeviceId(
715 const std::string& device_id) const {
716 DeviceIdPrefIdsMap::const_iterator found = device_map_.find(device_id);
717 if (found == device_map_.end())
718 return MediaGalleryPrefIdSet();
719 return found->second;
722 base::FilePath MediaGalleriesPreferences::LookUpGalleryPathForExtension(
723 MediaGalleryPrefId gallery_id,
724 const extensions::Extension* extension,
725 bool include_unpermitted_galleries) {
726 DCHECK(IsInitialized());
728 if (!include_unpermitted_galleries &&
729 !ContainsKey(GalleriesForExtension(*extension), gallery_id))
730 return base::FilePath();
732 MediaGalleriesPrefInfoMap::const_iterator it =
733 known_galleries_.find(gallery_id);
734 if (it == known_galleries_.end())
735 return base::FilePath();
736 return MediaStorageUtil::FindDevicePathById(it->second.device_id);
739 MediaGalleryPrefId MediaGalleriesPreferences::AddGallery(
740 const std::string& device_id,
741 const base::FilePath& relative_path,
742 MediaGalleryPrefInfo::Type type,
743 const base::string16& volume_label,
744 const base::string16& vendor_name,
745 const base::string16& model_name,
746 uint64 total_size_in_bytes,
747 base::Time last_attach_time,
751 DCHECK(IsInitialized());
752 return AddGalleryInternal(device_id, base::string16(), relative_path,
753 type, volume_label, vendor_name, model_name,
754 total_size_in_bytes, last_attach_time, true,
755 audio_count, image_count, video_count,
756 kCurrentPrefsVersion);
759 MediaGalleryPrefId MediaGalleriesPreferences::AddGalleryInternal(
760 const std::string& device_id, const base::string16& display_name,
761 const base::FilePath& relative_path, MediaGalleryPrefInfo::Type type,
762 const base::string16& volume_label, const base::string16& vendor_name,
763 const base::string16& model_name, uint64 total_size_in_bytes,
764 base::Time last_attach_time, bool volume_metadata_valid,
765 int audio_count, int image_count, int video_count, int prefs_version) {
766 DCHECK(type == MediaGalleryPrefInfo::kUserAdded ||
767 type == MediaGalleryPrefInfo::kAutoDetected ||
768 type == MediaGalleryPrefInfo::kScanResult);
769 base::FilePath normalized_relative_path =
770 relative_path.NormalizePathSeparators();
771 MediaGalleryPrefIdSet galleries_on_device =
772 LookUpGalleriesByDeviceId(device_id);
773 for (MediaGalleryPrefIdSet::const_iterator pref_id_it =
774 galleries_on_device.begin();
775 pref_id_it != galleries_on_device.end();
777 const MediaGalleryPrefInfo& existing =
778 known_galleries_.find(*pref_id_it)->second;
779 if (existing.path != normalized_relative_path)
782 bool update_gallery_type = false;
783 MediaGalleryPrefInfo::Type new_type = existing.type;
784 if (type == MediaGalleryPrefInfo::kUserAdded) {
785 if (existing.type == MediaGalleryPrefInfo::kBlackListed) {
786 new_type = MediaGalleryPrefInfo::kAutoDetected;
787 update_gallery_type = true;
789 if (existing.type == MediaGalleryPrefInfo::kRemovedScan) {
790 new_type = MediaGalleryPrefInfo::kUserAdded;
791 update_gallery_type = true;
795 // Status quo: In M27 and M28, galleries added manually use version 0,
796 // and galleries added automatically (including default galleries) use
797 // version 1. The name override is used by default galleries as well
798 // as all device attach events.
799 // We want to upgrade the name if the existing version is < 2. Leave it
800 // alone if the existing display name is set with version == 2 and the
801 // proposed new name is empty.
802 bool update_gallery_name = existing.display_name != display_name;
803 if (existing.prefs_version == 2 && !existing.display_name.empty() &&
804 display_name.empty()) {
805 update_gallery_name = false;
807 bool update_gallery_metadata = volume_metadata_valid &&
808 ((existing.volume_label != volume_label) ||
809 (existing.vendor_name != vendor_name) ||
810 (existing.model_name != model_name) ||
811 (existing.total_size_in_bytes != total_size_in_bytes) ||
812 (existing.last_attach_time != last_attach_time));
814 bool update_scan_counts =
815 new_type != MediaGalleryPrefInfo::kRemovedScan &&
816 new_type != MediaGalleryPrefInfo::kBlackListed &&
817 (audio_count > 0 || image_count > 0 || video_count > 0 ||
818 existing.audio_count || existing.image_count || existing.video_count);
820 if (!update_gallery_name && !update_gallery_type &&
821 !update_gallery_metadata && !update_scan_counts)
824 PrefService* prefs = profile_->GetPrefs();
825 scoped_ptr<ListPrefUpdate> update(
826 new ListPrefUpdate(prefs, prefs::kMediaGalleriesRememberedGalleries));
827 base::ListValue* list = update->Get();
829 for (base::ListValue::const_iterator list_iter = list->begin();
830 list_iter != list->end();
832 base::DictionaryValue* dict;
833 MediaGalleryPrefId iter_id;
834 if ((*list_iter)->GetAsDictionary(&dict) &&
835 GetPrefId(*dict, &iter_id) &&
836 *pref_id_it == iter_id) {
837 if (update_gallery_type)
838 dict->SetString(kMediaGalleriesTypeKey, TypeToStringValue(new_type));
839 if (update_gallery_name)
840 dict->SetString(kMediaGalleriesDisplayNameKey, display_name);
841 if (update_gallery_metadata) {
842 dict->SetString(kMediaGalleriesVolumeLabelKey, volume_label);
843 dict->SetString(kMediaGalleriesVendorNameKey, vendor_name);
844 dict->SetString(kMediaGalleriesModelNameKey, model_name);
845 dict->SetDouble(kMediaGalleriesSizeKey, total_size_in_bytes);
846 dict->SetDouble(kMediaGalleriesLastAttachTimeKey,
847 last_attach_time.ToInternalValue());
849 if (update_scan_counts) {
850 dict->SetInteger(kMediaGalleriesScanAudioCountKey, audio_count);
851 dict->SetInteger(kMediaGalleriesScanImageCountKey, image_count);
852 dict->SetInteger(kMediaGalleriesScanVideoCountKey, video_count);
854 dict->SetInteger(kMediaGalleriesPrefsVersionKey, prefs_version);
859 // Commits the prefs update.
863 FOR_EACH_OBSERVER(GalleryChangeObserver, gallery_change_observers_,
864 OnGalleryInfoUpdated(this, *pref_id_it));
868 PrefService* prefs = profile_->GetPrefs();
870 MediaGalleryPrefInfo gallery_info;
871 gallery_info.pref_id = prefs->GetUint64(prefs::kMediaGalleriesUniqueId);
872 prefs->SetUint64(prefs::kMediaGalleriesUniqueId, gallery_info.pref_id + 1);
873 gallery_info.display_name = display_name;
874 gallery_info.device_id = device_id;
875 gallery_info.path = normalized_relative_path;
876 gallery_info.type = type;
877 gallery_info.volume_label = volume_label;
878 gallery_info.vendor_name = vendor_name;
879 gallery_info.model_name = model_name;
880 gallery_info.total_size_in_bytes = total_size_in_bytes;
881 gallery_info.last_attach_time = last_attach_time;
882 gallery_info.volume_metadata_valid = volume_metadata_valid;
883 gallery_info.audio_count = audio_count;
884 gallery_info.image_count = image_count;
885 gallery_info.video_count = video_count;
886 gallery_info.prefs_version = prefs_version;
889 ListPrefUpdate update(prefs, prefs::kMediaGalleriesRememberedGalleries);
890 base::ListValue* list = update.Get();
891 list->Append(CreateGalleryPrefInfoDictionary(gallery_info));
894 FOR_EACH_OBSERVER(GalleryChangeObserver,
895 gallery_change_observers_,
896 OnGalleryAdded(this, gallery_info.pref_id));
898 return gallery_info.pref_id;
901 MediaGalleryPrefId MediaGalleriesPreferences::AddGalleryByPath(
902 const base::FilePath& path, MediaGalleryPrefInfo::Type type) {
903 DCHECK(IsInitialized());
904 MediaGalleryPrefInfo gallery_info;
905 if (LookUpGalleryByPath(path, &gallery_info) &&
906 !gallery_info.IsBlackListedType()) {
907 return gallery_info.pref_id;
909 return AddGalleryInternal(gallery_info.device_id,
910 gallery_info.display_name,
913 gallery_info.volume_label,
914 gallery_info.vendor_name,
915 gallery_info.model_name,
916 gallery_info.total_size_in_bytes,
917 gallery_info.last_attach_time,
918 gallery_info.volume_metadata_valid,
920 kCurrentPrefsVersion);
923 void MediaGalleriesPreferences::ForgetGalleryById(MediaGalleryPrefId id) {
924 EraseOrBlacklistGalleryById(id, false);
927 void MediaGalleriesPreferences::EraseGalleryById(MediaGalleryPrefId id) {
928 EraseOrBlacklistGalleryById(id, true);
931 void MediaGalleriesPreferences::EraseOrBlacklistGalleryById(
932 MediaGalleryPrefId id, bool erase) {
933 DCHECK(IsInitialized());
934 PrefService* prefs = profile_->GetPrefs();
935 scoped_ptr<ListPrefUpdate> update(new ListPrefUpdate(
936 prefs, prefs::kMediaGalleriesRememberedGalleries));
937 base::ListValue* list = update->Get();
939 if (!ContainsKey(known_galleries_, id))
942 for (base::ListValue::iterator iter = list->begin();
943 iter != list->end(); ++iter) {
944 base::DictionaryValue* dict;
945 MediaGalleryPrefId iter_id;
946 if ((*iter)->GetAsDictionary(&dict) && GetPrefId(*dict, &iter_id) &&
948 RemoveGalleryPermissionsFromPrefs(id);
949 MediaGalleryPrefInfo::Type type;
950 if (!erase && GetType(*dict, &type) &&
951 (type == MediaGalleryPrefInfo::kAutoDetected ||
952 type == MediaGalleryPrefInfo::kScanResult)) {
953 if (type == MediaGalleryPrefInfo::kAutoDetected) {
954 dict->SetString(kMediaGalleriesTypeKey,
955 kMediaGalleriesTypeBlackListedValue);
957 dict->SetString(kMediaGalleriesTypeKey,
958 kMediaGalleriesTypeRemovedScanValue);
959 dict->SetInteger(kMediaGalleriesScanAudioCountKey, 0);
960 dict->SetInteger(kMediaGalleriesScanImageCountKey, 0);
961 dict->SetInteger(kMediaGalleriesScanVideoCountKey, 0);
964 list->Erase(iter, NULL);
966 update.reset(NULL); // commits the update.
969 FOR_EACH_OBSERVER(GalleryChangeObserver,
970 gallery_change_observers_,
971 OnGalleryRemoved(this, id));
977 bool MediaGalleriesPreferences::NonAutoGalleryHasPermission(
978 MediaGalleryPrefId id) const {
979 DCHECK(IsInitialized());
980 DCHECK(!ContainsKey(known_galleries_, id) ||
981 known_galleries_.find(id)->second.type !=
982 MediaGalleryPrefInfo::kAutoDetected);
983 ExtensionPrefs* prefs = GetExtensionPrefs();
984 const base::DictionaryValue* extensions =
985 prefs->pref_service()->GetDictionary(extensions::pref_names::kExtensions);
989 for (base::DictionaryValue::Iterator iter(*extensions); !iter.IsAtEnd();
991 if (!extensions::Extension::IdIsValid(iter.key())) {
995 std::vector<MediaGalleryPermission> permissions =
996 GetGalleryPermissionsFromPrefs(iter.key());
997 for (std::vector<MediaGalleryPermission>::const_iterator it =
998 permissions.begin(); it != permissions.end(); ++it) {
999 if (it->pref_id == id) {
1000 if (it->has_permission)
1009 MediaGalleryPrefIdSet MediaGalleriesPreferences::GalleriesForExtension(
1010 const extensions::Extension& extension) const {
1011 DCHECK(IsInitialized());
1012 MediaGalleryPrefIdSet result;
1014 if (HasAutoDetectedGalleryPermission(extension)) {
1015 for (MediaGalleriesPrefInfoMap::const_iterator it =
1016 known_galleries_.begin(); it != known_galleries_.end(); ++it) {
1017 if (it->second.type == MediaGalleryPrefInfo::kAutoDetected)
1018 result.insert(it->second.pref_id);
1022 std::vector<MediaGalleryPermission> stored_permissions =
1023 GetGalleryPermissionsFromPrefs(extension.id());
1024 for (std::vector<MediaGalleryPermission>::const_iterator it =
1025 stored_permissions.begin(); it != stored_permissions.end(); ++it) {
1026 if (!it->has_permission) {
1027 result.erase(it->pref_id);
1029 MediaGalleriesPrefInfoMap::const_iterator gallery =
1030 known_galleries_.find(it->pref_id);
1031 DCHECK(gallery != known_galleries_.end());
1032 if (!gallery->second.IsBlackListedType()) {
1033 result.insert(it->pref_id);
1035 NOTREACHED() << gallery->second.device_id;
1042 bool MediaGalleriesPreferences::SetGalleryPermissionForExtension(
1043 const extensions::Extension& extension,
1044 MediaGalleryPrefId pref_id,
1045 bool has_permission) {
1046 DCHECK(IsInitialized());
1047 // The gallery may not exist anymore if the user opened a second config
1048 // surface concurrently and removed it. Drop the permission update if so.
1049 MediaGalleriesPrefInfoMap::const_iterator gallery_info =
1050 known_galleries_.find(pref_id);
1051 if (gallery_info == known_galleries_.end())
1054 bool default_permission = false;
1055 if (gallery_info->second.type == MediaGalleryPrefInfo::kAutoDetected)
1056 default_permission = HasAutoDetectedGalleryPermission(extension);
1057 // When the permission matches the default, we don't need to remember it.
1058 if (has_permission == default_permission) {
1059 if (!UnsetGalleryPermissionInPrefs(extension.id(), pref_id))
1060 // If permission wasn't set, assume nothing has changed.
1063 if (!SetGalleryPermissionInPrefs(extension.id(), pref_id, has_permission))
1067 FOR_EACH_OBSERVER(GalleryChangeObserver,
1068 gallery_change_observers_,
1069 OnPermissionAdded(this, extension.id(), pref_id));
1071 FOR_EACH_OBSERVER(GalleryChangeObserver,
1072 gallery_change_observers_,
1073 OnPermissionRemoved(this, extension.id(), pref_id));
1077 const MediaGalleriesPrefInfoMap& MediaGalleriesPreferences::known_galleries()
1079 DCHECK(IsInitialized());
1080 return known_galleries_;
1083 base::Time MediaGalleriesPreferences::GetLastScanCompletionTime() const {
1084 int64 last_scan_time_internal =
1085 profile_->GetPrefs()->GetInt64(prefs::kMediaGalleriesLastScanTime);
1086 return base::Time::FromInternalValue(last_scan_time_internal);
1089 void MediaGalleriesPreferences::SetLastScanCompletionTime(
1090 const base::Time& time) {
1091 profile_->GetPrefs()->SetInt64(prefs::kMediaGalleriesLastScanTime,
1092 time.ToInternalValue());
1095 void MediaGalleriesPreferences::Shutdown() {
1096 weak_factory_.InvalidateWeakPtrs();
1101 bool MediaGalleriesPreferences::APIHasBeenUsed(Profile* profile) {
1102 MediaGalleryPrefId current_id =
1103 profile->GetPrefs()->GetUint64(prefs::kMediaGalleriesUniqueId);
1104 return current_id != kInvalidMediaGalleryPrefId + 1;
1108 void MediaGalleriesPreferences::RegisterProfilePrefs(
1109 user_prefs::PrefRegistrySyncable* registry) {
1110 registry->RegisterListPref(prefs::kMediaGalleriesRememberedGalleries,
1111 user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
1112 registry->RegisterUint64Pref(
1113 prefs::kMediaGalleriesUniqueId,
1114 kInvalidMediaGalleryPrefId + 1,
1115 user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
1116 registry->RegisterInt64Pref(
1117 prefs::kMediaGalleriesLastScanTime,
1118 base::Time().ToInternalValue(),
1119 user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
1122 bool MediaGalleriesPreferences::SetGalleryPermissionInPrefs(
1123 const std::string& extension_id,
1124 MediaGalleryPrefId gallery_id,
1126 DCHECK(IsInitialized());
1127 ExtensionPrefs::ScopedListUpdate update(GetExtensionPrefs(),
1129 kMediaGalleriesPermissions);
1130 base::ListValue* permissions = update.Get();
1132 permissions = update.Create();
1134 // If the gallery is already in the list, update the permission...
1135 for (base::ListValue::iterator iter = permissions->begin();
1136 iter != permissions->end(); ++iter) {
1137 base::DictionaryValue* dict = NULL;
1138 if (!(*iter)->GetAsDictionary(&dict))
1140 MediaGalleryPermission perm;
1141 if (!GetMediaGalleryPermissionFromDictionary(dict, &perm))
1143 if (perm.pref_id == gallery_id) {
1144 if (has_access != perm.has_permission) {
1145 dict->SetBoolean(kMediaGalleryHasPermissionKey, has_access);
1153 // ...Otherwise, add a new entry for the gallery.
1154 base::DictionaryValue* dict = new base::DictionaryValue;
1155 dict->SetString(kMediaGalleryIdKey, base::Uint64ToString(gallery_id));
1156 dict->SetBoolean(kMediaGalleryHasPermissionKey, has_access);
1157 permissions->Append(dict);
1161 bool MediaGalleriesPreferences::UnsetGalleryPermissionInPrefs(
1162 const std::string& extension_id,
1163 MediaGalleryPrefId gallery_id) {
1164 DCHECK(IsInitialized());
1165 ExtensionPrefs::ScopedListUpdate update(GetExtensionPrefs(),
1167 kMediaGalleriesPermissions);
1168 base::ListValue* permissions = update.Get();
1172 for (base::ListValue::iterator iter = permissions->begin();
1173 iter != permissions->end(); ++iter) {
1174 const base::DictionaryValue* dict = NULL;
1175 if (!(*iter)->GetAsDictionary(&dict))
1177 MediaGalleryPermission perm;
1178 if (!GetMediaGalleryPermissionFromDictionary(dict, &perm))
1180 if (perm.pref_id == gallery_id) {
1181 permissions->Erase(iter, NULL);
1188 std::vector<MediaGalleryPermission>
1189 MediaGalleriesPreferences::GetGalleryPermissionsFromPrefs(
1190 const std::string& extension_id) const {
1191 DCHECK(IsInitialized());
1192 std::vector<MediaGalleryPermission> result;
1193 const base::ListValue* permissions;
1194 if (!GetExtensionPrefs()->ReadPrefAsList(extension_id,
1195 kMediaGalleriesPermissions,
1200 for (base::ListValue::const_iterator iter = permissions->begin();
1201 iter != permissions->end(); ++iter) {
1202 base::DictionaryValue* dict = NULL;
1203 if (!(*iter)->GetAsDictionary(&dict))
1205 MediaGalleryPermission perm;
1206 if (!GetMediaGalleryPermissionFromDictionary(dict, &perm))
1208 result.push_back(perm);
1214 void MediaGalleriesPreferences::RemoveGalleryPermissionsFromPrefs(
1215 MediaGalleryPrefId gallery_id) {
1216 DCHECK(IsInitialized());
1217 ExtensionPrefs* prefs = GetExtensionPrefs();
1218 const base::DictionaryValue* extensions =
1219 prefs->pref_service()->GetDictionary(extensions::pref_names::kExtensions);
1223 for (base::DictionaryValue::Iterator iter(*extensions); !iter.IsAtEnd();
1225 if (!extensions::Extension::IdIsValid(iter.key())) {
1229 UnsetGalleryPermissionInPrefs(iter.key(), gallery_id);
1233 ExtensionPrefs* MediaGalleriesPreferences::GetExtensionPrefs() const {
1234 DCHECK(IsInitialized());
1235 if (extension_prefs_for_testing_)
1236 return extension_prefs_for_testing_;
1237 return extensions::ExtensionPrefs::Get(profile_);
1240 void MediaGalleriesPreferences::SetExtensionPrefsForTesting(
1241 extensions::ExtensionPrefs* extension_prefs) {
1242 DCHECK(IsInitialized());
1243 extension_prefs_for_testing_ = extension_prefs;