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
;
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
) {
87 ExtensionService
* extension_service
=
88 extensions::ExtensionSystem::Get(profile
)->extension_service();
89 if (!extension_service
)
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
)) {
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
)) {
115 bool GetType(const base::DictionaryValue
& dict
,
116 MediaGalleryPrefInfo::Type
* type
) {
117 std::string string_type
;
118 if (!dict
.GetString(kMediaGalleriesTypeKey
, &string_type
))
121 if (string_type
== kMediaGalleriesTypeUserAddedValue
) {
122 *type
= MediaGalleryPrefInfo::kUserAdded
;
125 if (string_type
== kMediaGalleriesTypeAutoDetectedValue
) {
126 *type
= MediaGalleryPrefInfo::kAutoDetected
;
129 if (string_type
== kMediaGalleriesTypeBlackListedValue
) {
130 *type
= MediaGalleryPrefInfo::kBlackListed
;
133 if (string_type
== kMediaGalleriesTypeScanResultValue
) {
134 *type
= MediaGalleryPrefInfo::kScanResult
;
137 if (string_type
== kMediaGalleriesTypeRemovedScanValue
) {
138 *type
= MediaGalleryPrefInfo::kRemovedScan
;
145 const char* TypeToStringValue(MediaGalleryPrefInfo::Type type
) {
146 const char* result
= NULL
;
148 case MediaGalleryPrefInfo::kUserAdded
:
149 result
= kMediaGalleriesTypeUserAddedValue
;
151 case MediaGalleryPrefInfo::kAutoDetected
:
152 result
= kMediaGalleriesTypeAutoDetectedValue
;
154 case MediaGalleryPrefInfo::kBlackListed
:
155 result
= kMediaGalleriesTypeBlackListedValue
;
157 case MediaGalleryPrefInfo::kScanResult
:
158 result
= kMediaGalleriesTypeScanResultValue
;
160 case MediaGalleryPrefInfo::kRemovedScan
:
161 result
= kMediaGalleriesTypeRemovedScanValue
;
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;
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
)) {
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
;
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());
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
);
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
, ¶m
);
277 // Retrieves the MediaGalleryPermission from the given dictionary; DCHECKs on
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
)) {
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) ?
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())
307 return (sub_folder
.BaseName().LossyDisplayName() +
308 base::ASCIIToUTF16(" - ") +
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
;
330 MediaGalleryPrefInfo::MediaGalleryPrefInfo()
331 : pref_id(kInvalidMediaGalleryPrefId
),
333 total_size_in_bytes(0),
334 volume_metadata_valid(false),
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())
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();
374 return path
.LossyDisplayName();
378 base::string16 name
= display_name
;
382 name
= GetFullProductName(vendor_name
, model_name
);
384 name
= l10n_util::GetStringUTF16(IDS_MEDIA_GALLERIES_UNLABELED_DEVICE
);
386 name
= GetDisplayNameForDevice(total_size_in_bytes
, name
);
389 name
= GetDisplayNameForSubFolder(name
, path
);
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
));
409 attached
= l10n_util::GetStringUTF16(
410 IDS_MEDIA_GALLERIES_DIALOG_DEVICE_NOT_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),
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())
446 on_initialize_callbacks_
.push_back(callback
);
447 if (on_initialize_callbacks_
.size() > 1)
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());
500 StorageMonitor
* monitor
= StorageMonitor::GetInstance();
501 DCHECK(monitor
->IsInitialized());
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())))
513 AddGallery(existing_devices
[i
].device_id(),
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();
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
) {
541 if (!PathService::Get(kDirectoryKeys
[i
], &path
))
544 base::FilePath relative_path
;
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
))
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
))
572 std::string this_device_id
;
573 if (!dict
->GetString(kMediaGalleriesDeviceIdKey
, &this_device_id
))
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
))
581 if (device_type
== singleton_type
) {
582 dict
->SetString(kMediaGalleriesDeviceIdKey
, device_id
);
583 update
.reset(); // commits the update.
585 MediaGalleryPrefId pref_id
;
586 if (GetPrefId(*dict
, &pref_id
)) {
587 FOR_EACH_OBSERVER(GalleryChangeObserver
,
588 gallery_change_observers_
,
589 OnGalleryInfoUpdated(this, pref_id
));
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
;
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();
629 PrefService
* prefs
= profile_
->GetPrefs();
630 const base::ListValue
* list
= prefs
->GetList(
631 prefs::kMediaGalleriesRememberedGalleries
);
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
))
639 MediaGalleryPrefInfo gallery_info
;
640 if (!PopulateGalleryPrefInfoFromDictionary(*dict
, &gallery_info
))
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()))
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());
678 base::FilePath relative_path
;
679 if (!MediaStorageUtil::GetDeviceInfoFromPath(path
, &info
, &relative_path
)) {
681 *gallery_info
= MediaGalleryPrefInfo();
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();
691 const MediaGalleryPrefInfo
& gallery
= known_galleries_
.find(*it
)->second
;
692 if (gallery
.path
!= relative_path
)
696 *gallery_info
= gallery
;
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
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
;
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());
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
,
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();
786 const MediaGalleryPrefInfo
& existing
=
787 known_galleries_
.find(*pref_id_it
)->second
;
788 if (existing
.path
!= normalized_relative_path
)
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
)
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();
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
);
866 // Commits the prefs update.
870 FOR_EACH_OBSERVER(GalleryChangeObserver
, gallery_change_observers_
,
871 OnGalleryInfoUpdated(this, *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
));
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
,
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
,
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
))
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
);
955 dict
->SetString(kMediaGalleriesTypeKey
,
956 kMediaGalleriesTypeRemovedScanValue
);
957 dict
->SetInteger(kMediaGalleriesScanImageCountKey
, 0);
958 dict
->SetInteger(kMediaGalleriesScanMusicCountKey
, 0);
959 dict
->SetInteger(kMediaGalleriesScanVideoCountKey
, 0);
962 list
->Erase(iter
, NULL
);
964 update
.reset(NULL
); // commits the update.
967 FOR_EACH_OBSERVER(GalleryChangeObserver
,
968 gallery_change_observers_
,
969 OnGalleryRemoved(this, pref_id
));
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
);
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
);
1001 NOTREACHED() << gallery
->second
.device_id
;
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())
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.
1029 if (!SetGalleryPermissionInPrefs(extension
.id(), pref_id
, has_permission
))
1033 FOR_EACH_OBSERVER(GalleryChangeObserver
,
1034 gallery_change_observers_
,
1035 OnPermissionAdded(this, extension
.id(), pref_id
));
1037 FOR_EACH_OBSERVER(GalleryChangeObserver
,
1038 gallery_change_observers_
,
1039 OnPermissionRemoved(this, extension
.id(), pref_id
));
1043 const MediaGalleriesPrefInfoMap
& MediaGalleriesPreferences::known_galleries()
1045 DCHECK(IsInitialized());
1046 return known_galleries_
;
1049 void MediaGalleriesPreferences::Shutdown() {
1050 weak_factory_
.InvalidateWeakPtrs();
1055 bool MediaGalleriesPreferences::APIHasBeenUsed(Profile
* profile
) {
1056 MediaGalleryPrefId current_id
=
1057 profile
->GetPrefs()->GetUint64(prefs::kMediaGalleriesUniqueId
);
1058 return current_id
!= kInvalidMediaGalleryPrefId
+ 1;
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
,
1076 DCHECK(IsInitialized());
1077 ExtensionPrefs::ScopedListUpdate
update(GetExtensionPrefs(),
1079 kMediaGalleriesPermissions
);
1080 base::ListValue
* permissions
= update
.Get();
1082 permissions
= update
.Create();
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
))
1090 MediaGalleryPermission perm
;
1091 if (!GetMediaGalleryPermissionFromDictionary(dict
, &perm
))
1093 if (perm
.pref_id
== gallery_id
) {
1094 if (has_access
!= perm
.has_permission
) {
1095 dict
->SetBoolean(kMediaGalleryHasPermissionKey
, has_access
);
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
);
1111 bool MediaGalleriesPreferences::UnsetGalleryPermissionInPrefs(
1112 const std::string
& extension_id
,
1113 MediaGalleryPrefId gallery_id
) {
1114 DCHECK(IsInitialized());
1115 ExtensionPrefs::ScopedListUpdate
update(GetExtensionPrefs(),
1117 kMediaGalleriesPermissions
);
1118 base::ListValue
* permissions
= update
.Get();
1122 for (base::ListValue::iterator iter
= permissions
->begin();
1123 iter
!= permissions
->end(); ++iter
) {
1124 const base::DictionaryValue
* dict
= NULL
;
1125 if (!(*iter
)->GetAsDictionary(&dict
))
1127 MediaGalleryPermission perm
;
1128 if (!GetMediaGalleryPermissionFromDictionary(dict
, &perm
))
1130 if (perm
.pref_id
== gallery_id
) {
1131 permissions
->Erase(iter
, NULL
);
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
,
1150 for (base::ListValue::const_iterator iter
= permissions
->begin();
1151 iter
!= permissions
->end(); ++iter
) {
1152 base::DictionaryValue
* dict
= NULL
;
1153 if (!(*iter
)->GetAsDictionary(&dict
))
1155 MediaGalleryPermission perm
;
1156 if (!GetMediaGalleryPermissionFromDictionary(dict
, &perm
))
1158 result
.push_back(perm
);
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
);
1173 for (base::DictionaryValue::Iterator
iter(*extensions
); !iter
.IsAtEnd();
1175 if (!extensions::Extension::IdIsValid(iter
.key())) {
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
;