Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(212)

Unified Diff: chrome/browser/ui/app_list/arc_app_prefs.cc

Issue 1413153007: arc-app-launcher: Minimal support for ARC app launcher. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: switch to new IPC Created 5 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: chrome/browser/ui/app_list/arc_app_prefs.cc
diff --git a/chrome/browser/ui/app_list/arc_app_prefs.cc b/chrome/browser/ui/app_list/arc_app_prefs.cc
new file mode 100644
index 0000000000000000000000000000000000000000..7922b1f83dbb5e57d68f90697a00a3bb39df9260
--- /dev/null
+++ b/chrome/browser/ui/app_list/arc_app_prefs.cc
@@ -0,0 +1,364 @@
+// 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_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_prefs_factory.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 kArcApps[] = "arcapps";
+const char kName[] = "name";
+const char kPackage[] = "package";
+const char kActivity[] = "activity";
+
+// Provider of write access to a dictionary storing arc app prefs.
+class ScopedArcAppPrefUpdate : public DictionaryPrefUpdate {
+ public:
+ ScopedArcAppPrefUpdate(PrefService* service, const std::string& id)
+ : DictionaryPrefUpdate(service, kArcApps),
+ id_(id) {}
+
+ ~ScopedArcAppPrefUpdate() override {}
+
+ // DictionaryPrefUpdate overrides:
+ base::DictionaryValue* Get() override {
+ base::DictionaryValue* dict = DictionaryPrefUpdate::Get();
+ base::DictionaryValue* app = NULL;
+ if (!dict->GetDictionary(id_, &app)) {
+ app = new base::DictionaryValue();
+ dict->SetWithoutPathExpansion(id_, app);
+ }
+ return app;
+ }
+
+ private:
+ const std::string id_;
+
+ DISALLOW_COPY_AND_ASSIGN(ScopedArcAppPrefUpdate);
+};
+
+} // namespace
+
+// static
+ArcAppPrefs* ArcAppPrefs::Create(content::BrowserContext* browser_context,
+ const base::FilePath& base_path,
+ PrefService* prefs) {
+ return new ArcAppPrefs(browser_context, base_path, prefs);
+}
+
+// static
+void ArcAppPrefs::RegisterProfilePrefs(
+ user_prefs::PrefRegistrySyncable* registry) {
+ registry->RegisterDictionaryPref(
+ kArcApps,
xiyuan 2015/11/11 22:15:51 kArcApps should be in chrome/common/pref_names.h
khmel1 2015/11/12 08:05:30 Done.
+ user_prefs::PrefRegistrySyncable::SYNCABLE_PREF);
xiyuan 2015/11/11 22:15:50 ArcAppPrefs looks like a local cache to store app
khmel1 2015/11/12 08:05:30 One of the next idea is to use sync-able feature t
xiyuan 2015/11/12 17:32:44 Acknowledged. Thanks for the explanation.
elijahtaylor1 2015/11/13 01:56:00 Just chiming in that yes we do want to sync this i
+}
+
+// static
+ArcAppPrefs* ArcAppPrefs::Get(content::BrowserContext* context) {
+ return ArcAppPrefsFactory::GetInstance()->GetForBrowserContext(context);
+}
+
+// static
+std::string ArcAppPrefs::GetAppId(const std::string& package,
+ const std::string& activity) {
+ std::string input = package + "#" + activity;
+ return crx_file::id_util::GenerateId(input);
xiyuan 2015/11/11 22:15:50 nit: App list does not assume the format of the id
khmel1 2015/11/12 08:05:30 One of the reason I used this generator is '.' in
xiyuan 2015/11/12 17:32:44 Acknowledged.
+}
+
+ArcAppPrefs::ArcAppPrefs(content::BrowserContext* browser_context,
+ const base::FilePath& base_path,
+ PrefService* prefs)
+ : browser_context_(browser_context),
xiyuan 2015/11/11 22:15:50 |browser_context_| seems not used.
khmel1 2015/11/12 08:05:30 Yes, we use here only root folder for profile.
+ prefs_(prefs) {
+ base_path_ = base_path.AppendASCII(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);
+ if (bridge_service->state() == arc::ArcBridgeService::State::READY) {
xiyuan 2015/11/11 22:15:51 nit: How about doing "OnStateChanged(bridge_servic
khmel1 2015/11/12 08:05:30 Personally I prefer to do but was not sure that is
+ bridge_service->RefreshApps();
+ }
+}
+
+ArcAppPrefs::~ArcAppPrefs() {
+ arc::ArcBridgeService* bridge_service = arc::ArcBridgeService::Get();
+ if (!bridge_service) {
+ LOG(ERROR) << "Bridge service is not available. Cannot release.";
xiyuan 2015/11/11 22:15:51 Do we know how ArcBridgeService starts and shuts d
khmel1 2015/11/12 08:05:31 This CL(1412863004) is still under review. But it
xiyuan 2015/11/12 17:32:44 Thanks for the CL reference. Yep, CL(1412863004) i
+ return;
+ }
+ bridge_service->RemoveObserver(this);
+}
+
+base::FilePath ArcAppPrefs::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 ArcAppPrefs::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 ArcAppPrefs::AddObserver(Observer* observer) {
+ observer_list_.AddObserver(observer);
+}
+
+void ArcAppPrefs::RemoveObserver(Observer* observer) {
+ observer_list_.RemoveObserver(observer);
+}
+
+std::vector<std::string> ArcAppPrefs::GetAppIds() const {
+ std::vector<std::string> ids;
+
+ // crx_file::id_util is de-factor utility for id generation.
+ const base::DictionaryValue* apps = prefs_->GetDictionary(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<ArcAppPrefs::AppInfo> ArcAppPrefs::GetApp(
+ const std::string& app_id) const {
+ const base::DictionaryValue* app = NULL;
+ const base::DictionaryValue* apps = prefs_->GetDictionary(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 ArcAppPrefs::IsRegistered(const std::string& app_id) {
+ const base::DictionaryValue* app = NULL;
+ const base::DictionaryValue* apps = prefs_->GetDictionary(kArcApps);
+ if (!apps || !apps->GetDictionary(app_id, &app)) {
+ return false;
+ }
+ return true;
+}
+
+void ArcAppPrefs::DisableAllApps() {
+ std::set<std::string>::const_iterator it;
+ for (it = ready_apps_.begin(); it != ready_apps_.end(); ++it) {
xiyuan 2015/11/11 22:15:51 nit: for (auto& app_id : ready_apps_)
khmel1 2015/11/12 08:05:30 Done.
+ const std::string& app_id = *it;
+ FOR_EACH_OBSERVER(Observer,
+ observer_list_,
+ OnAppReady(app_id, false));
+ }
+ ready_apps_.clear();
+}
+
+void ArcAppPrefs::OnStateChanged(arc::ArcBridgeService::State state) {
+ if (state == arc::ArcBridgeService::State::READY) {
+ arc::ArcBridgeService::Get()->RefreshApps();
+ } else {
+ DisableAllApps();
+ }
+}
+
+void ArcAppPrefs::OnAppReady(const std::string& name,
+ const std::string& package) {
+ std::string app_id = GetAppId(package, "");
+ bool was_registered = IsRegistered(app_id);
+
+ ScopedArcAppPrefUpdate update(prefs_, app_id);
+ base::DictionaryValue* app_dict = update.Get();
+ app_dict->SetString(kName, name);
+ app_dict->SetString(kPackage, package);
+ app_dict->SetString(kActivity, "");
+
+ // 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_,
+ OnAppReady(app_id, true));
+ } else {
+ AppInfo app_info;
+ app_info.name = name;
+ app_info.package = package;
+ app_info.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 ArcAppPrefs::OnAppsRefreshed(const std::vector<std::string>& names,
+ const std::vector<std::string>& packages) {
+ std::set<std::string> old_ready_apps_;
xiyuan 2015/11/11 22:15:51 nit: old_ready_apps_ -> old_ready_apps
khmel1 2015/11/12 08:05:30 Done.
+ old_ready_apps_.swap(ready_apps_);
+
+ for (size_t i = 0; i < names.size(); ++i) {
+ OnAppReady(names[i], packages[i]);
+ }
+
+ // Detect unavailable apps after current refresh.
+ std::set<std::string>::const_iterator it;
+ for (it = old_ready_apps_.begin(); it != old_ready_apps_.end(); ++it) {
xiyuan 2015/11/11 22:15:50 nit: for (auto& app_id : old_ready_apps)
khmel1 2015/11/12 08:05:30 Done.
+ const std::string& app_id = *it;
+ if (!ready_apps_.count(app_id)) {
+ FOR_EACH_OBSERVER(Observer,
+ observer_list_,
+ OnAppReady(app_id, false));
+ }
+ }
+}
+
+void ArcAppPrefs::OnAppIcon(const std::string& package,
+ const std::string& activity,
+ int density,
+ const std::vector<uint8_t>& icon_png_data) {
+ DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+ DCHECK(!icon_png_data.empty());
+ DCHECK(density >= ui::SCALE_FACTOR_100P && density < 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>(density), icon_png_data);
+}
+
+
+void ArcAppPrefs::InstallIcon(const std::string& app_id,
+ ui::ScaleFactor scale_factor,
+ const std::vector<uint8>& content_png) {
+ base::Closure task = base::Bind(&ArcAppPrefs::InstallIconFromFileThread,
+ base::Unretained(this),
xiyuan 2015/11/11 22:15:50 base::Unretained is dangerous. Add a WeakPtrFactor
khmel1 2015/11/12 08:05:30 Done.
+ app_id,
+ scale_factor,
+ content_png);
+ content::BrowserThread::PostTask(content::BrowserThread::FILE,
xiyuan 2015/11/11 22:15:50 Use BrowserThrad::GetBlockingPool instead of file
khmel1 2015/11/12 08:05:30 Done.
+ FROM_HERE,
+ task);
+}
+
+void ArcAppPrefs::InstallIconFromFileThread(
+ const std::string& app_id,
+ ui::ScaleFactor scale_factor,
+ const std::vector<uint8>& content_png) {
+ DCHECK_CURRENTLY_ON(content::BrowserThread::FILE);
+ 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());
+ DCHECK(wrote == static_cast<int>(content_png.size()));
xiyuan 2015/11/11 22:15:51 nit: DCHECK_EQ(wrote, static_cast<int>(content_png
khmel1 2015/11/12 08:05:30 As I understand potentially we may get situation w
xiyuan 2015/11/12 17:32:44 Acknowledged.
+
+ base::Closure task = base::Bind(&ArcAppPrefs::OnIconInstalled,
+ base::Unretained(this),
xiyuan 2015/11/11 22:15:51 ditto
khmel1 2015/11/12 08:05:30 Done.
+ app_id,
+ scale_factor);
+ content::BrowserThread::PostTask(content::BrowserThread::UI,
+ FROM_HERE,
+ task);
+}
+
+void ArcAppPrefs::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));
+}

Powered by Google App Engine
This is Rietveld 408576698