// Copyright 2014 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "components/drive/drive_app_registry.h" #include #include #include #include "base/callback.h" #include "base/files/file_path.h" #include "components/drive/drive_app_registry_observer.h" #include "components/drive/service/drive_service_interface.h" #include "google_apis/drive/drive_api_parser.h" #include "google_apis/google_api_keys.h" namespace { // Add {selector -> app_id} mapping to |map|. void AddAppSelectorList(const ScopedVector& selectors, const std::string& app_id, std::multimap* map) { for (size_t i = 0; i < selectors.size(); ++i) map->insert(std::make_pair(*selectors[i], app_id)); } // Append list of app ids in |map| looked up by |selector| to |matched_apps|. void FindAppsForSelector(const std::string& selector, const std::multimap& map, std::vector* matched_apps) { typedef std::multimap::const_iterator iterator; std::pair range = map.equal_range(selector); for (iterator it = range.first; it != range.second; ++it) matched_apps->push_back(it->second); } void RemoveAppFromSelector(const std::string& app_id, std::multimap* map) { typedef std::multimap::iterator iterator; for (iterator it = map->begin(); it != map->end(); ) { iterator now = it++; if (now->second == app_id) map->erase(now); } } } // namespace namespace drive { DriveAppInfo::DriveAppInfo() { } DriveAppInfo::DriveAppInfo( const std::string& app_id, const std::string& product_id, const IconList& app_icons, const IconList& document_icons, const std::string& app_name, const GURL& create_url, bool is_removable) : app_id(app_id), product_id(product_id), app_icons(app_icons), document_icons(document_icons), app_name(app_name), create_url(create_url), is_removable(is_removable) { } DriveAppInfo::~DriveAppInfo() { } DriveAppRegistry::DriveAppRegistry(DriveServiceInterface* drive_service) : drive_service_(drive_service), is_updating_(false), weak_ptr_factory_(this) { } DriveAppRegistry::~DriveAppRegistry() { } void DriveAppRegistry::GetAppsForFile( const base::FilePath::StringType& file_extension, const std::string& mime_type, std::vector* apps) const { DCHECK(thread_checker_.CalledOnValidThread()); std::vector matched_apps; if (!file_extension.empty()) { const std::string without_dot = base::FilePath(file_extension.substr(1)).AsUTF8Unsafe(); FindAppsForSelector(without_dot, extension_map_, &matched_apps); } if (!mime_type.empty()) FindAppsForSelector(mime_type, mimetype_map_, &matched_apps); // Insert found Drive apps into |apps|, but skip duplicate results. std::set inserted_app_ids; for (size_t i = 0; i < matched_apps.size(); ++i) { if (inserted_app_ids.count(matched_apps[i]) == 0) { inserted_app_ids.insert(matched_apps[i]); std::map::const_iterator it = all_apps_.find(matched_apps[i]); DCHECK(it != all_apps_.end()); apps->push_back(it->second); } } } void DriveAppRegistry::GetAppList(std::vector* apps) const { DCHECK(thread_checker_.CalledOnValidThread()); apps->clear(); for (std::map::const_iterator it = all_apps_.begin(); it != all_apps_.end(); ++it) { apps->push_back(it->second); } } void DriveAppRegistry::Update() { DCHECK(thread_checker_.CalledOnValidThread()); if (is_updating_) // There is already an update in progress. return; is_updating_ = true; drive_service_->GetAppList( base::Bind(&DriveAppRegistry::UpdateAfterGetAppList, weak_ptr_factory_.GetWeakPtr())); } void DriveAppRegistry::UpdateAfterGetAppList( google_apis::DriveApiErrorCode gdata_error, scoped_ptr app_list) { DCHECK(thread_checker_.CalledOnValidThread()); DCHECK(is_updating_); is_updating_ = false; // Failed to fetch the data from the server. We can do nothing here. if (gdata_error != google_apis::HTTP_SUCCESS) return; DCHECK(app_list); UpdateFromAppList(*app_list); } void DriveAppRegistry::UpdateFromAppList(const google_apis::AppList& app_list) { all_apps_.clear(); extension_map_.clear(); mimetype_map_.clear(); for (size_t i = 0; i < app_list.items().size(); ++i) { const google_apis::AppResource& app = *app_list.items()[i]; const std::string id = app.application_id(); DriveAppInfo::IconList app_icons; DriveAppInfo::IconList document_icons; for (size_t j = 0; j < app.icons().size(); ++j) { const google_apis::DriveAppIcon& icon = *app.icons()[j]; if (icon.icon_url().is_empty()) continue; if (icon.category() == google_apis::DriveAppIcon::APPLICATION) app_icons.push_back(std::make_pair(icon.icon_side_length(), icon.icon_url())); if (icon.category() == google_apis::DriveAppIcon::DOCUMENT) document_icons.push_back(std::make_pair(icon.icon_side_length(), icon.icon_url())); } all_apps_[id] = DriveAppInfo(app.application_id(), app.product_id(), app_icons, document_icons, app.name(), app.create_url(), app.is_removable()); // TODO(kinaba): consider taking primary/secondary distinction into account. AddAppSelectorList(app.primary_mimetypes(), id, &mimetype_map_); AddAppSelectorList(app.secondary_mimetypes(), id, &mimetype_map_); AddAppSelectorList(app.primary_file_extensions(), id, &extension_map_); AddAppSelectorList(app.secondary_file_extensions(), id, &extension_map_); } FOR_EACH_OBSERVER(DriveAppRegistryObserver, observers_, OnDriveAppRegistryUpdated()); } void DriveAppRegistry::AddObserver(DriveAppRegistryObserver* observer) { observers_.AddObserver(observer); } void DriveAppRegistry::RemoveObserver(DriveAppRegistryObserver* observer) { observers_.RemoveObserver(observer); } void DriveAppRegistry::UninstallApp(const std::string& app_id, const UninstallCallback& callback) { DCHECK(!callback.is_null()); drive_service_->UninstallApp(app_id, base::Bind(&DriveAppRegistry::OnAppUninstalled, weak_ptr_factory_.GetWeakPtr(), app_id, callback)); } void DriveAppRegistry::OnAppUninstalled(const std::string& app_id, const UninstallCallback& callback, google_apis::DriveApiErrorCode error) { if (error == google_apis::HTTP_NO_CONTENT) { all_apps_.erase(app_id); RemoveAppFromSelector(app_id, &mimetype_map_); RemoveAppFromSelector(app_id, &extension_map_); } callback.Run(error); } // static bool DriveAppRegistry::IsAppUninstallSupported() { return google_apis::IsGoogleChromeAPIKeyUsed(); } namespace util { GURL FindPreferredIcon(const DriveAppInfo::IconList& icons, int preferred_size) { if (icons.empty()) return GURL(); DriveAppInfo::IconList sorted_icons = icons; std::sort(sorted_icons.rbegin(), sorted_icons.rend()); // Go forward while the size is larger or equal to preferred_size. size_t i = 1; while (i < sorted_icons.size() && sorted_icons[i].first >= preferred_size) ++i; return sorted_icons[i - 1].second; } } // namespace util } // namespace drive