Chromium Code Reviews| Index: chrome/browser/ui/app_list/arc_app_list_prefs.cc |
| diff --git a/chrome/browser/ui/app_list/arc_app_list_prefs.cc b/chrome/browser/ui/app_list/arc_app_list_prefs.cc |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..58193dfbdc39f55fb452ef206a33f6d87e00b79f |
| --- /dev/null |
| +++ b/chrome/browser/ui/app_list/arc_app_list_prefs.cc |
| @@ -0,0 +1,373 @@ |
| +// Copyright 2015 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 "chrome/browser/ui/app_list/arc_app_list_prefs.h" |
| + |
| +#include "base/files/file_util.h" |
| +#include "base/prefs/scoped_user_pref_update.h" |
| +#include "base/task_runner_util.h" |
| +#include "chrome/browser/profiles/profile.h" |
| +#include "chrome/browser/ui/app_list/arc_app_list_prefs_factory.h" |
| +#include "chrome/common/pref_names.h" |
| +#include "components/crx_file/id_util.h" |
| +#include "components/pref_registry/pref_registry_syncable.h" |
| +#include "content/public/browser/browser_thread.h" |
| + |
| +namespace { |
| + |
| +const char kName[] = "name"; |
| +const char kPackage[] = "package"; |
| +const char kActivity[] = "activity"; |
| + |
| +// Provider of write access to a dictionary storing ARC app prefs. |
| +class ScopedArcAppListPrefUpdate : public DictionaryPrefUpdate { |
| + public: |
| + ScopedArcAppListPrefUpdate(PrefService* service, const std::string& id) |
| + : DictionaryPrefUpdate(service, prefs::kArcApps), |
| + id_(id) {} |
| + |
| + ~ScopedArcAppListPrefUpdate() override {} |
| + |
| + // DictionaryPrefUpdate overrides: |
| + base::DictionaryValue* Get() override { |
| + base::DictionaryValue* dict = DictionaryPrefUpdate::Get(); |
| + base::DictionaryValue* app = NULL; |
|
xiyuan
2015/11/13 17:14:22
nit: NULL -> nullptr
khmel1
2015/11/17 13:18:02
Done.
|
| + if (!dict->GetDictionary(id_, &app)) { |
| + app = new base::DictionaryValue(); |
| + dict->SetWithoutPathExpansion(id_, app); |
| + } |
| + return app; |
| + } |
| + |
| + private: |
| + const std::string id_; |
| + |
| + DISALLOW_COPY_AND_ASSIGN(ScopedArcAppListPrefUpdate); |
| +}; |
| + |
| +} // namespace |
| + |
| +// static |
| +ArcAppListPrefs* ArcAppListPrefs::Create(const base::FilePath& base_path, |
| + PrefService* prefs) { |
| + return new ArcAppListPrefs(base_path, prefs); |
| +} |
| + |
| +// static |
| +void ArcAppListPrefs::RegisterProfilePrefs( |
| + user_prefs::PrefRegistrySyncable* registry) { |
| + registry->RegisterDictionaryPref( |
| + prefs::kArcApps, |
| + user_prefs::PrefRegistrySyncable::SYNCABLE_PREF); |
| +} |
| + |
| +// static |
| +ArcAppListPrefs* ArcAppListPrefs::Get(content::BrowserContext* context) { |
| + return ArcAppListPrefsFactory::GetInstance()->GetForBrowserContext(context); |
| +} |
| + |
| +// static |
| +std::string ArcAppListPrefs::GetAppId(const std::string& package, |
| + const std::string& activity) { |
| + std::string input = package + "#" + activity; |
| + return crx_file::id_util::GenerateId(input); |
| +} |
| + |
| +ArcAppListPrefs::ArcAppListPrefs(const base::FilePath& base_path, |
| + PrefService* prefs) |
| + : prefs_(prefs), |
| + weak_ptr_factory_(this) { |
| + base_path_ = base_path.AppendASCII(prefs::kArcApps); |
| + |
| + arc::ArcBridgeService* bridge_service = arc::ArcBridgeService::Get(); |
| + if (!bridge_service) { |
| + LOG(ERROR) << "Bridge service is not available. Cannot init."; |
| + return; |
| + } |
| + |
| + bridge_service->AddObserver(this); |
| + OnStateChanged(bridge_service->state()); |
| +} |
| + |
| +ArcAppListPrefs::~ArcAppListPrefs() { |
| + arc::ArcBridgeService* bridge_service = arc::ArcBridgeService::Get(); |
| + if (bridge_service) { |
| + bridge_service->RemoveObserver(this); |
| + } |
| +} |
| + |
| +base::FilePath ArcAppListPrefs::GetIconPath( |
| + const std::string& app_id, |
| + ui::ScaleFactor scale_factor) const { |
| + base::FilePath app_path = base_path_.AppendASCII(app_id); |
| + switch (scale_factor) { |
| + case ui::SCALE_FACTOR_100P: |
| + return app_path.AppendASCII("icon_100p.png"); |
| + case ui::SCALE_FACTOR_125P: |
| + return app_path.AppendASCII("icon_125p.png"); |
| + case ui::SCALE_FACTOR_133P: |
| + return app_path.AppendASCII("icon_133p.png"); |
| + case ui::SCALE_FACTOR_140P: |
| + return app_path.AppendASCII("icon_140p.png"); |
| + case ui::SCALE_FACTOR_150P: |
| + return app_path.AppendASCII("icon_150p.png"); |
| + case ui::SCALE_FACTOR_180P: |
| + return app_path.AppendASCII("icon_180p.png"); |
| + case ui::SCALE_FACTOR_200P: |
| + return app_path.AppendASCII("icon_200p.png"); |
| + case ui::SCALE_FACTOR_250P: |
| + return app_path.AppendASCII("icon_250p.png"); |
| + case ui::SCALE_FACTOR_300P: |
| + return app_path.AppendASCII("icon_300p.png"); |
| + default: |
| + NOTREACHED(); |
| + return base::FilePath(); |
| + } |
| +} |
| + |
| +void ArcAppListPrefs::RequestIcon(const std::string& app_id, |
| + ui::ScaleFactor scale_factor) { |
| + if (!IsRegistered(app_id)) { |
| + LOG(ERROR) << "Request to load icon for non-registered app: " |
| + << app_id << "."; |
| + return; |
| + } |
| + |
| + // In case app is not ready, defer this request. |
| + if (!ready_apps_.count(app_id)) { |
| + request_icon_deferred_[app_id] = |
| + request_icon_deferred_[app_id] | 1 << scale_factor; |
| + return; |
| + } |
| + |
| + arc::ArcBridgeService* bridge_service = arc::ArcBridgeService::Get(); |
| + if (!bridge_service || |
| + bridge_service->state() != arc::ArcBridgeService::State::READY) { |
| + LOG(ERROR) << "Request to load icon when bridge service is not ready: " |
| + << app_id << "."; |
| + return; |
| + } |
| + |
| + scoped_ptr<AppInfo> app_info = GetApp(app_id); |
| + if (!app_info) { |
| + LOG(ERROR) << "Failed to get app info: " << app_id << "."; |
| + return; |
| + } |
| + |
| + bridge_service->RequestIcon(app_info->package, |
| + app_info->activity, |
| + static_cast<int>(scale_factor)); |
| +} |
| + |
| +void ArcAppListPrefs::AddObserver(Observer* observer) { |
| + observer_list_.AddObserver(observer); |
| +} |
| + |
| +void ArcAppListPrefs::RemoveObserver(Observer* observer) { |
| + observer_list_.RemoveObserver(observer); |
| +} |
| + |
| +std::vector<std::string> ArcAppListPrefs::GetAppIds() const { |
| + std::vector<std::string> ids; |
| + |
| + // crx_file::id_util is de-factor utility for id generation. |
| + const base::DictionaryValue* apps = prefs_->GetDictionary(prefs::kArcApps); |
| + for (base::DictionaryValue::Iterator app_id(*apps); |
| + !app_id.IsAtEnd(); app_id.Advance()) { |
| + if (!crx_file::id_util::IdIsValid(app_id.key())) |
| + continue; |
| + ids.push_back(app_id.key()); |
| + } |
| + |
| + return ids; |
| +} |
| + |
| +scoped_ptr<ArcAppListPrefs::AppInfo> ArcAppListPrefs::GetApp( |
| + const std::string& app_id) const { |
| + const base::DictionaryValue* app = NULL; |
|
xiyuan
2015/11/13 17:14:22
nit: NULL -> nullptr
khmel1
2015/11/17 13:18:02
Done.
|
| + const base::DictionaryValue* apps = prefs_->GetDictionary(prefs::kArcApps); |
| + if (!apps || |
| + !apps->GetDictionaryWithoutPathExpansion(app_id, &app)) { |
| + return scoped_ptr<AppInfo>(); |
| + } |
| + |
| + scoped_ptr<AppInfo> app_info(new AppInfo); |
| + app->GetString(kName, &app_info->name); |
| + app->GetString(kPackage, &app_info->package); |
| + app->GetString(kActivity, &app_info->activity); |
| + app_info->ready = ready_apps_.count(app_id) > 0; |
| + |
| + return app_info.Pass(); |
| +} |
| + |
| +bool ArcAppListPrefs::IsRegistered(const std::string& app_id) { |
| + const base::DictionaryValue* app = NULL; |
|
xiyuan
2015/11/13 17:14:22
nit: NULL -> nullptr
khmel1
2015/11/17 13:18:02
Done.
|
| + const base::DictionaryValue* apps = prefs_->GetDictionary(prefs::kArcApps); |
| + if (!apps || !apps->GetDictionary(app_id, &app)) { |
| + return false; |
| + } |
| + return true; |
| +} |
| + |
| +void ArcAppListPrefs::DisableAllApps() { |
| + for (auto& app_id : ready_apps_) { |
| + FOR_EACH_OBSERVER(Observer, |
| + observer_list_, |
| + OnAppReadyChanged(app_id, false)); |
| + } |
| + ready_apps_.clear(); |
| +} |
| + |
| +void ArcAppListPrefs::OnStateChanged(arc::ArcBridgeService::State state) { |
| + if (state == arc::ArcBridgeService::State::READY) { |
| + arc::ArcBridgeService::Get()->RefreshApps(); |
| + } else { |
| + DisableAllApps(); |
| + } |
| +} |
| + |
| +void ArcAppListPrefs::OnAppReady(const std::string& name, |
| + const std::string& package, |
| + const std::string& activity) { |
| + if (name.empty() || package.empty() || activity.empty()) { |
| + LOG(ERROR) << "Name, package and activity cannot be empty."; |
| + return; |
| + } |
| + std::string app_id = GetAppId(package, activity); |
| + bool was_registered = IsRegistered(app_id); |
| + |
| + ScopedArcAppListPrefUpdate update(prefs_, app_id); |
| + base::DictionaryValue* app_dict = update.Get(); |
| + app_dict->SetString(kName, name); |
| + app_dict->SetString(kPackage, package); |
| + app_dict->SetString(kActivity, activity); |
| + |
| + // From now, app is ready. |
| + if (!ready_apps_.count(app_id)) { |
| + ready_apps_.insert(app_id); |
| + } |
| + |
| + if (was_registered) { |
| + FOR_EACH_OBSERVER(Observer, |
| + observer_list_, |
| + OnAppReadyChanged(app_id, true)); |
| + } else { |
| + AppInfo app_info; |
| + app_info.name = name; |
| + app_info.package = package; |
| + app_info.activity = activity; |
| + app_info.ready = true; |
| + FOR_EACH_OBSERVER(Observer, |
| + observer_list_, |
| + OnAppRegistered(app_id, app_info)); |
| + } |
| + |
| + std::map<std::string, uint32>::iterator deferred_icons = |
| + request_icon_deferred_.find(app_id); |
| + if (deferred_icons != request_icon_deferred_.end()) { |
| + for (uint32 i = ui::SCALE_FACTOR_100P; i < ui::NUM_SCALE_FACTORS; ++i) { |
| + if (deferred_icons->second & (1 << i)) { |
| + RequestIcon(app_id, static_cast<ui::ScaleFactor>(i)); |
| + } |
| + } |
| + request_icon_deferred_.erase(deferred_icons); |
| + } |
| +} |
| + |
| +void ArcAppListPrefs::OnAppsRefreshed( |
| + const std::vector<std::string>& names, |
| + const std::vector<std::string>& packages, |
| + const std::vector<std::string>& activities) { |
| + if (names.size() != packages.size() || names.size() != activities.size()) { |
| + LOG(ERROR) << "Name, package and activity must be the same size."; |
| + return; |
| + } |
| + |
| + std::set<std::string> old_ready_apps; |
| + old_ready_apps.swap(ready_apps_); |
| + |
| + for (size_t i = 0; i < names.size(); ++i) { |
| + OnAppReady(names[i], packages[i], activities[i]); |
| + } |
| + |
| + // Detect unavailable apps after current refresh. |
| + for (auto& app_id : old_ready_apps) { |
| + if (!ready_apps_.count(app_id)) { |
| + FOR_EACH_OBSERVER(Observer, |
| + observer_list_, |
| + OnAppReadyChanged(app_id, false)); |
| + } |
| + } |
| +} |
| + |
| +void ArcAppListPrefs::OnAppIcon(const std::string& package, |
| + const std::string& activity, |
| + int scale_factor, |
| + const std::vector<uint8_t>& icon_png_data) { |
| + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| + DCHECK(!icon_png_data.empty()); |
| + DCHECK(scale_factor >= ui::SCALE_FACTOR_100P && |
| + scale_factor < ui::NUM_SCALE_FACTORS); |
| + |
| + std::string app_id = GetAppId(package, activity); |
| + if (!IsRegistered(app_id)) { |
| + LOG(ERROR) << "Request to update icon for non-registered app: " << app_id; |
| + return; |
| + } |
| + |
| + InstallIcon(app_id, |
| + static_cast<ui::ScaleFactor>(scale_factor), |
| + icon_png_data); |
| +} |
| + |
| + |
| +void ArcAppListPrefs::InstallIcon(const std::string& app_id, |
| + ui::ScaleFactor scale_factor, |
| + const std::vector<uint8>& content_png) { |
| + base::Closure task = base::Bind(&ArcAppListPrefs::InstallIconFromFileThread, |
| + weak_ptr_factory_.GetWeakPtr(), |
| + app_id, |
| + scale_factor, |
| + content_png); |
| + content::BrowserThread::GetBlockingPool()->PostTask(FROM_HERE, task); |
| +} |
| + |
| +void ArcAppListPrefs::InstallIconFromFileThread( |
| + const std::string& app_id, |
| + ui::ScaleFactor scale_factor, |
| + const std::vector<uint8>& content_png) { |
| + DCHECK(content::BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread()); |
| + DCHECK(!content_png.empty()); |
| + |
| + base::FilePath app_path = base_path_.AppendASCII(app_id); |
| + base::CreateDirectory(app_path); |
| + base::FilePath icon_path = GetIconPath(app_id, scale_factor); |
| + |
| + int wrote = base::WriteFile(icon_path, |
| + reinterpret_cast<const char*>(&content_png[0]), |
| + content_png.size()); |
| + if (wrote != static_cast<int>(content_png.size())) { |
| + LOG(ERROR) << "Failed to write ARC icon file: " << icon_path.MaybeAsASCII() |
| + << "."; |
| + DCHECK(base::DeleteFile(icon_path, false)); |
| + return; |
| + } |
| + |
| + base::Closure task = base::Bind(&ArcAppListPrefs::OnIconInstalled, |
| + weak_ptr_factory_.GetWeakPtr(), |
| + app_id, |
| + scale_factor); |
| + content::BrowserThread::PostTask(content::BrowserThread::UI, |
| + FROM_HERE, |
| + task); |
| +} |
| + |
| +void ArcAppListPrefs::OnIconInstalled(const std::string& app_id, |
| + ui::ScaleFactor scale_factor) { |
| + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| + |
| + FOR_EACH_OBSERVER(Observer, |
| + observer_list_, |
| + OnAppIconUpdated(app_id, scale_factor)); |
| +} |