1 // Copyright 2014 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/drive/drive_app_registry.h"
11 #include "base/callback.h"
12 #include "base/files/file_path.h"
13 #include "chrome/browser/drive/drive_app_registry_observer.h"
14 #include "chrome/browser/drive/drive_service_interface.h"
15 #include "content/public/browser/browser_thread.h"
16 #include "google_apis/drive/drive_api_parser.h"
17 #include "google_apis/google_api_keys.h"
19 using content::BrowserThread
;
23 // Add {selector -> app_id} mapping to |map|.
24 void AddAppSelectorList(const ScopedVector
<std::string
>& selectors
,
25 const std::string
& app_id
,
26 std::multimap
<std::string
, std::string
>* map
) {
27 for (size_t i
= 0; i
< selectors
.size(); ++i
)
28 map
->insert(std::make_pair(*selectors
[i
], app_id
));
31 // Append list of app ids in |map| looked up by |selector| to |matched_apps|.
32 void FindAppsForSelector(const std::string
& selector
,
33 const std::multimap
<std::string
, std::string
>& map
,
34 std::vector
<std::string
>* matched_apps
) {
35 typedef std::multimap
<std::string
, std::string
>::const_iterator iterator
;
36 std::pair
<iterator
, iterator
> range
= map
.equal_range(selector
);
37 for (iterator it
= range
.first
; it
!= range
.second
; ++it
)
38 matched_apps
->push_back(it
->second
);
41 void RemoveAppFromSelector(const std::string
& app_id
,
42 std::multimap
<std::string
, std::string
>* map
) {
43 typedef std::multimap
<std::string
, std::string
>::iterator iterator
;
44 for (iterator it
= map
->begin(); it
!= map
->end(); ) {
46 if (now
->second
== app_id
)
55 DriveAppInfo::DriveAppInfo() {
58 DriveAppInfo::DriveAppInfo(
59 const std::string
& app_id
,
60 const std::string
& product_id
,
61 const google_apis::InstalledApp::IconList
& app_icons
,
62 const google_apis::InstalledApp::IconList
& document_icons
,
63 const std::string
& app_name
,
64 const GURL
& create_url
,
67 product_id(product_id
),
69 document_icons(document_icons
),
71 create_url(create_url
),
72 is_removable(is_removable
) {
75 DriveAppInfo::~DriveAppInfo() {
78 DriveAppRegistry::DriveAppRegistry(DriveServiceInterface
* drive_service
)
79 : drive_service_(drive_service
),
81 weak_ptr_factory_(this) {
84 DriveAppRegistry::~DriveAppRegistry() {
87 void DriveAppRegistry::GetAppsForFile(
88 const base::FilePath::StringType
& file_extension
,
89 const std::string
& mime_type
,
90 std::vector
<DriveAppInfo
>* apps
) const {
91 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
93 std::vector
<std::string
> matched_apps
;
94 if (!file_extension
.empty()) {
95 const std::string without_dot
=
96 base::FilePath(file_extension
.substr(1)).AsUTF8Unsafe();
97 FindAppsForSelector(without_dot
, extension_map_
, &matched_apps
);
99 if (!mime_type
.empty())
100 FindAppsForSelector(mime_type
, mimetype_map_
, &matched_apps
);
102 // Insert found Drive apps into |apps|, but skip duplicate results.
103 std::set
<std::string
> inserted_app_ids
;
104 for (size_t i
= 0; i
< matched_apps
.size(); ++i
) {
105 if (inserted_app_ids
.count(matched_apps
[i
]) == 0) {
106 inserted_app_ids
.insert(matched_apps
[i
]);
107 std::map
<std::string
, DriveAppInfo
>::const_iterator it
=
108 all_apps_
.find(matched_apps
[i
]);
109 DCHECK(it
!= all_apps_
.end());
110 apps
->push_back(it
->second
);
115 void DriveAppRegistry::GetAppList(std::vector
<DriveAppInfo
>* apps
) const {
116 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
119 for (std::map
<std::string
, DriveAppInfo
>::const_iterator
120 it
= all_apps_
.begin(); it
!= all_apps_
.end(); ++it
) {
121 apps
->push_back(it
->second
);
125 void DriveAppRegistry::Update() {
126 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
128 if (is_updating_
) // There is already an update in progress.
132 drive_service_
->GetAppList(
133 base::Bind(&DriveAppRegistry::UpdateAfterGetAppList
,
134 weak_ptr_factory_
.GetWeakPtr()));
137 void DriveAppRegistry::UpdateAfterGetAppList(
138 google_apis::GDataErrorCode gdata_error
,
139 scoped_ptr
<google_apis::AppList
> app_list
) {
140 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
142 DCHECK(is_updating_
);
143 is_updating_
= false;
145 // Failed to fetch the data from the server. We can do nothing here.
146 if (gdata_error
!= google_apis::HTTP_SUCCESS
)
150 UpdateFromAppList(*app_list
);
153 void DriveAppRegistry::UpdateFromAppList(const google_apis::AppList
& app_list
) {
155 extension_map_
.clear();
156 mimetype_map_
.clear();
158 for (size_t i
= 0; i
< app_list
.items().size(); ++i
) {
159 const google_apis::AppResource
& app
= *app_list
.items()[i
];
160 const std::string id
= app
.application_id();
162 google_apis::InstalledApp::IconList app_icons
;
163 google_apis::InstalledApp::IconList document_icons
;
164 for (size_t j
= 0; j
< app
.icons().size(); ++j
) {
165 const google_apis::DriveAppIcon
& icon
= *app
.icons()[j
];
166 if (icon
.icon_url().is_empty())
168 if (icon
.category() == google_apis::DriveAppIcon::APPLICATION
)
169 app_icons
.push_back(std::make_pair(icon
.icon_side_length(),
171 if (icon
.category() == google_apis::DriveAppIcon::DOCUMENT
)
172 document_icons
.push_back(std::make_pair(icon
.icon_side_length(),
176 all_apps_
[id
] = DriveAppInfo(app
.application_id(),
184 // TODO(kinaba): consider taking primary/secondary distinction into account.
185 AddAppSelectorList(app
.primary_mimetypes(), id
, &mimetype_map_
);
186 AddAppSelectorList(app
.secondary_mimetypes(), id
, &mimetype_map_
);
187 AddAppSelectorList(app
.primary_file_extensions(), id
, &extension_map_
);
188 AddAppSelectorList(app
.secondary_file_extensions(), id
, &extension_map_
);
191 FOR_EACH_OBSERVER(DriveAppRegistryObserver
,
193 OnDriveAppRegistryUpdated());
196 void DriveAppRegistry::AddObserver(DriveAppRegistryObserver
* observer
) {
197 observers_
.AddObserver(observer
);
200 void DriveAppRegistry::RemoveObserver(DriveAppRegistryObserver
* observer
) {
201 observers_
.RemoveObserver(observer
);
204 void DriveAppRegistry::UninstallApp(const std::string
& app_id
,
205 const UninstallCallback
& callback
) {
206 DCHECK(!callback
.is_null());
208 drive_service_
->UninstallApp(app_id
,
209 base::Bind(&DriveAppRegistry::OnAppUninstalled
,
210 weak_ptr_factory_
.GetWeakPtr(),
215 void DriveAppRegistry::OnAppUninstalled(const std::string
& app_id
,
216 const UninstallCallback
& callback
,
217 google_apis::GDataErrorCode error
) {
218 if (error
== google_apis::HTTP_NO_CONTENT
) {
219 all_apps_
.erase(app_id
);
220 RemoveAppFromSelector(app_id
, &mimetype_map_
);
221 RemoveAppFromSelector(app_id
, &extension_map_
);
227 bool DriveAppRegistry::IsAppUninstallSupported() {
228 return google_apis::IsGoogleChromeAPIKeyUsed();
233 GURL
FindPreferredIcon(const google_apis::InstalledApp::IconList
& icons
,
234 int preferred_size
) {
238 google_apis::InstalledApp::IconList sorted_icons
= icons
;
239 std::sort(sorted_icons
.rbegin(), sorted_icons
.rend());
241 // Go forward while the size is larger or equal to preferred_size.
243 while (i
< sorted_icons
.size() && sorted_icons
[i
].first
>= preferred_size
)
245 return sorted_icons
[i
- 1].second
;