| 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..7117aed59b42feab1a89b0568111c6c2404c894a
|
| --- /dev/null
|
| +++ b/chrome/browser/ui/app_list/arc_app_list_prefs.cc
|
| @@ -0,0 +1,380 @@
|
| +// 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 = nullptr;
|
| + 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);
|
| +};
|
| +
|
| +bool InstallIconFromFileThread(const std::string& app_id,
|
| + ui::ScaleFactor scale_factor,
|
| + const base::FilePath& icon_path,
|
| + const std::vector<uint8>& content_png) {
|
| + DCHECK(content::BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread());
|
| + DCHECK(!content_png.empty());
|
| +
|
| + base::CreateDirectory(icon_path.DirName());
|
| +
|
| + 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 false;
|
| + }
|
| +
|
| + return true;
|
| +}
|
| +
|
| +} // 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 = nullptr;
|
| + 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 = nullptr;
|
| + 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::FilePath icon_path = GetIconPath(app_id, scale_factor);
|
| + base::PostTaskAndReplyWithResult(content::BrowserThread::GetBlockingPool(),
|
| + FROM_HERE,
|
| + base::Bind(&InstallIconFromFileThread,
|
| + app_id,
|
| + scale_factor,
|
| + icon_path,
|
| + content_png),
|
| + base::Bind(&ArcAppListPrefs::OnIconInstalled,
|
| + weak_ptr_factory_.GetWeakPtr(),
|
| + app_id,
|
| + scale_factor));
|
| +}
|
| +
|
| +void ArcAppListPrefs::OnIconInstalled(const std::string& app_id,
|
| + ui::ScaleFactor scale_factor,
|
| + bool install_succeed) {
|
| + DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
|
| + if (!install_succeed) {
|
| + return;
|
| + }
|
| +
|
| + FOR_EACH_OBSERVER(Observer,
|
| + observer_list_,
|
| + OnAppIconUpdated(app_id, scale_factor));
|
| +}
|
| +
|
| +ArcAppListPrefs::AppInfo::AppInfo()
|
| + : ready(false) {
|
| +}
|
|
|