blob: 1408d220f904379e2159e8279a850499036d51fc [file] [log] [blame]
// Copyright 2018 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/apps/app_service/arc_apps.h"
#include <memory>
#include <utility>
#include "base/bind.h"
#include "base/callback.h"
#include "chrome/browser/apps/app_service/app_icon_factory.h"
#include "chrome/browser/apps/app_service/app_service_proxy.h"
#include "chrome/browser/apps/app_service/arc_apps_factory.h"
#include "chrome/browser/apps/app_service/dip_px_util.h"
#include "chrome/browser/chromeos/arc/arc_util.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/app_list/arc/arc_app_icon_descriptor.h"
#include "chrome/browser/ui/app_list/arc/arc_app_utils.h"
#include "components/arc/arc_bridge_service.h"
#include "components/arc/arc_service_manager.h"
#include "content/public/common/service_manager_connection.h"
#include "mojo/public/cpp/bindings/interface_request.h"
#include "services/data_decoder/public/cpp/decode_image.h"
#include "ui/gfx/geometry/size.h"
namespace {
// ArcApps::LoadIcon (via ArcApps::LoadIconFromVM) runs a series of callbacks,
// defined here in back-to-front order so that e.g. the compiler knows
// LoadIcon2's signature when compiling LoadIcon1 (which binds LoadIcon2).
//
// - LoadIcon0 is called back when the AppConnectionHolder is connected.
// - LoadIcon1 is called back when the compressed (PNG) image is loaded.
// - LoadIcon2 is called back when the uncompressed image is loaded.
void LoadIcon2(apps::mojom::Publisher::LoadIconCallback callback,
const SkBitmap& bitmap) {
apps::mojom::IconValuePtr iv = apps::mojom::IconValue::New();
iv->icon_compression = apps::mojom::IconCompression::kUncompressed;
iv->uncompressed = gfx::ImageSkia(gfx::ImageSkiaRep(bitmap, 0.0f));
std::move(callback).Run(std::move(iv));
}
void LoadIcon1(apps::mojom::IconCompression icon_compression,
apps::mojom::Publisher::LoadIconCallback callback,
const std::vector<uint8_t>& icon_png_data) {
switch (icon_compression) {
case apps::mojom::IconCompression::kUnknown:
std::move(callback).Run(apps::mojom::IconValue::New());
break;
case apps::mojom::IconCompression::kUncompressed:
data_decoder::DecodeImage(
content::ServiceManagerConnection::GetForProcess()->GetConnector(),
icon_png_data, data_decoder::mojom::ImageCodec::DEFAULT, false,
data_decoder::kDefaultMaxSizeInBytes, gfx::Size(),
base::BindOnce(&LoadIcon2, std::move(callback)));
break;
case apps::mojom::IconCompression::kCompressed:
apps::mojom::IconValuePtr iv = apps::mojom::IconValue::New();
iv->icon_compression = apps::mojom::IconCompression::kCompressed;
iv->compressed = icon_png_data;
std::move(callback).Run(std::move(iv));
break;
}
}
void LoadIcon0(apps::mojom::IconCompression icon_compression,
int size_hint_in_px,
std::string package_name,
std::string activity,
std::string icon_resource_id,
apps::mojom::Publisher::LoadIconCallback callback,
apps::ArcApps::AppConnectionHolder* app_connection_holder) {
if (icon_resource_id.empty()) {
auto* app_instance =
ARC_GET_INSTANCE_FOR_METHOD(app_connection_holder, RequestAppIcon);
if (app_instance) {
app_instance->RequestAppIcon(
package_name, activity, size_hint_in_px,
base::BindOnce(&LoadIcon1, icon_compression, std::move(callback)));
return;
}
} else {
auto* app_instance =
ARC_GET_INSTANCE_FOR_METHOD(app_connection_holder, RequestShortcutIcon);
if (app_instance) {
app_instance->RequestShortcutIcon(
icon_resource_id, size_hint_in_px,
base::BindOnce(&LoadIcon1, icon_compression, std::move(callback)));
return;
}
}
// On failure, we still run the callback, with the zero IconValue.
std::move(callback).Run(apps::mojom::IconValue::New());
}
} // namespace
namespace apps {
// static
ArcApps* ArcApps::Get(Profile* profile) {
return ArcAppsFactory::GetForProfile(profile);
}
ArcApps::ArcApps(Profile* profile)
: binding_(this),
profile_(profile),
prefs_(ArcAppListPrefs::Get(profile)),
next_u_key_(1) {
if (!prefs_ || !arc::IsArcAllowedForProfile(profile_)) {
return;
}
auto* arc_service_manager = arc::ArcServiceManager::Get();
if (!arc_service_manager) {
return;
}
ObservePrefs();
apps::mojom::PublisherPtr publisher;
binding_.Bind(mojo::MakeRequest(&publisher));
apps::AppServiceProxy::Get(profile)->AppService()->RegisterPublisher(
std::move(publisher), apps::mojom::AppType::kArc);
}
ArcApps::~ArcApps() {
if (prefs_) {
prefs_->app_connection_holder()->RemoveObserver(this);
prefs_->RemoveObserver(this);
}
}
void ArcApps::Connect(apps::mojom::SubscriberPtr subscriber,
apps::mojom::ConnectOptionsPtr opts) {
std::vector<apps::mojom::AppPtr> apps;
if (prefs_) {
for (const auto& app_id : prefs_->GetAppIds()) {
std::unique_ptr<ArcAppListPrefs::AppInfo> app_info =
prefs_->GetApp(app_id);
if (app_info) {
apps.push_back(Convert(app_id, *app_info));
}
}
}
subscriber->OnApps(std::move(apps));
subscribers_.AddPtr(std::move(subscriber));
}
void ArcApps::LoadIcon(const std::string& app_id,
apps::mojom::IconKeyPtr icon_key,
apps::mojom::IconCompression icon_compression,
int32_t size_hint_in_dip,
LoadIconCallback callback) {
if (prefs_ && !icon_key.is_null() &&
(icon_key->icon_type == apps::mojom::IconType::kArc) &&
!icon_key->s_key.empty()) {
// TODO(crbug.com/826982): treat (app_id == arc::kPlayStoreAppId) as a
// special case, like what ArcAppIcon::Source::GetImageForScale does.
// Try loading the icon from an on-disk cache. If that fails, fall back to
// LoadIconFromVM.
LoadIconFromFileWithFallback(
icon_compression, size_hint_in_dip,
GetCachedIconFilePath(icon_key->s_key, size_hint_in_dip),
std::move(callback),
base::BindOnce(&ArcApps::LoadIconFromVM, weak_ptr_factory_.GetWeakPtr(),
icon_key->s_key, icon_compression, size_hint_in_dip));
return;
}
// On failure, we still run the callback, with the zero IconValue.
std::move(callback).Run(apps::mojom::IconValue::New());
}
void ArcApps::Launch(const std::string& app_id,
int32_t event_flags,
apps::mojom::LaunchSource launch_source,
int64_t display_id) {
auto uit = arc::UserInteractionType::NOT_USER_INITIATED;
switch (launch_source) {
case apps::mojom::LaunchSource::kUnknown:
return;
case apps::mojom::LaunchSource::kFromAppListGrid:
uit = arc::UserInteractionType::APP_STARTED_FROM_LAUNCHER;
break;
case apps::mojom::LaunchSource::kFromAppListGridContextMenu:
uit = arc::UserInteractionType::APP_STARTED_FROM_LAUNCHER_CONTEXT_MENU;
break;
case apps::mojom::LaunchSource::kFromAppListQuery:
uit = arc::UserInteractionType::APP_STARTED_FROM_LAUNCHER_SEARCH;
break;
case apps::mojom::LaunchSource::kFromAppListQueryContextMenu:
uit = arc::UserInteractionType::
APP_STARTED_FROM_LAUNCHER_SEARCH_CONTEXT_MENU;
break;
case apps::mojom::LaunchSource::kFromAppListRecommendation:
uit = arc::UserInteractionType::APP_STARTED_FROM_LAUNCHER_SUGGESTED_APP;
break;
}
arc::LaunchApp(profile_, app_id, event_flags, uit, display_id);
}
void ArcApps::SetPermission(const std::string& app_id,
apps::mojom::PermissionPtr permission) {
NOTIMPLEMENTED();
}
void ArcApps::Uninstall(const std::string& app_id) {
NOTIMPLEMENTED();
}
void ArcApps::OpenNativeSettings(const std::string& app_id) {
NOTIMPLEMENTED();
}
void ArcApps::OnConnectionReady() {
if (!prefs_) {
prefs_ = ArcAppListPrefs::Get(profile_);
ObservePrefs();
}
if (prefs_) {
AppConnectionHolder* app_connection_holder =
prefs_->app_connection_holder();
for (auto& pending : pending_load_icon_calls_) {
std::move(pending).Run(app_connection_holder);
}
pending_load_icon_calls_.clear();
}
}
void ArcApps::OnAppRegistered(const std::string& app_id,
const ArcAppListPrefs::AppInfo& app_info) {
OnAppStatesChanged(app_id, app_info);
}
void ArcApps::OnAppStatesChanged(const std::string& app_id,
const ArcAppListPrefs::AppInfo& app_info) {
apps::mojom::AppPtr app = apps::mojom::App::New();
app->app_type = apps::mojom::AppType::kArc;
app->app_id = app_id;
app->readiness = NewReadiness(app_info.ready);
Publish(std::move(app));
}
void ArcApps::OnAppRemoved(const std::string& app_id) {
apps::mojom::AppPtr app = apps::mojom::App::New();
app->app_type = apps::mojom::AppType::kArc;
app->app_id = app_id;
app->readiness = NewReadiness(false);
Publish(std::move(app));
}
void ArcApps::OnAppIconUpdated(const std::string& app_id,
const ArcAppIconDescriptor& descriptor) {
apps::mojom::AppPtr app = apps::mojom::App::New();
app->app_type = apps::mojom::AppType::kArc;
app->app_id = app_id;
app->icon_key = NewIconKey(app_id);
Publish(std::move(app));
}
void ArcApps::OnAppNameUpdated(const std::string& app_id,
const std::string& name) {
apps::mojom::AppPtr app = apps::mojom::App::New();
app->app_type = apps::mojom::AppType::kArc;
app->app_id = app_id;
app->name = name;
Publish(std::move(app));
}
void ArcApps::OnAppLastLaunchTimeUpdated(const std::string& app_id) {
// TODO(crbug.com/826982): implement.
}
void ArcApps::ObservePrefs() {
prefs_->AddObserver(this);
prefs_->app_connection_holder()->AddObserver(this);
}
const base::FilePath ArcApps::GetCachedIconFilePath(const std::string& app_id,
int32_t size_hint_in_dip) {
if (!prefs_) {
return base::FilePath();
}
// TODO(crbug.com/826982): process the app_id argument like the private
// GetAppFromAppOrGroupId function and the ArcAppIcon::mapped_app_id_ field
// in arc_app_icon.cc?
//
// TODO(crbug.com/826982): don't hard-code SCALE_FACTOR_100P.
return prefs_->GetIconPath(
app_id, ArcAppIconDescriptor(size_hint_in_dip,
ui::ScaleFactor::SCALE_FACTOR_100P));
}
void ArcApps::LoadIconFromVM(const std::string icon_key_s_key,
apps::mojom::IconCompression icon_compression,
int32_t size_hint_in_dip,
LoadIconCallback callback) {
if (prefs_) {
std::unique_ptr<ArcAppListPrefs::AppInfo> app_info =
prefs_->GetApp(icon_key_s_key);
if (app_info) {
base::OnceCallback<void(apps::ArcApps::AppConnectionHolder*)> pending =
base::BindOnce(&LoadIcon0, icon_compression,
ConvertDipToPx(size_hint_in_dip),
app_info->package_name, app_info->activity,
app_info->icon_resource_id, std::move(callback));
AppConnectionHolder* app_connection_holder =
prefs_->app_connection_holder();
if (app_connection_holder->IsConnected()) {
std::move(pending).Run(app_connection_holder);
} else {
pending_load_icon_calls_.push_back(std::move(pending));
}
return;
}
}
// On failure, we still run the callback, with the zero IconValue.
std::move(callback).Run(apps::mojom::IconValue::New());
}
apps::mojom::AppPtr ArcApps::Convert(const std::string& app_id,
const ArcAppListPrefs::AppInfo& app_info) {
apps::mojom::AppPtr app = apps::mojom::App::New();
app->app_type = apps::mojom::AppType::kArc;
app->app_id = app_id;
app->readiness = NewReadiness(app_info.ready);
app->name = app_info.name;
app->icon_key = NewIconKey(app_id);
bool installed_internally =
prefs_->IsDefault(app_id) ||
prefs_->IsControlledByPolicy(app_info.package_name);
app->installed_internally = installed_internally
? apps::mojom::OptionalBool::kTrue
: apps::mojom::OptionalBool::kFalse;
auto show = app_info.show_in_launcher ? apps::mojom::OptionalBool::kTrue
: apps::mojom::OptionalBool::kFalse;
app->show_in_launcher = show;
app->show_in_search = show;
return app;
}
apps::mojom::IconKeyPtr ArcApps::NewIconKey(const std::string& app_id) {
auto icon_key = apps::mojom::IconKey::New();
icon_key->icon_type = apps::mojom::IconType::kArc;
icon_key->s_key = app_id;
icon_key->u_key = next_u_key_++;
return icon_key;
}
// static
apps::mojom::Readiness ArcApps::NewReadiness(bool ready) {
// TODO(crbug.com/826982): examine ArcAppListPrefs::AppInfo::suspended, and
// possibly have a corresponding 'suspended' apps::mojom::Readiness enum
// value.
return ready ? apps::mojom::Readiness::kReady
: apps::mojom::Readiness::kUninstalledByUser;
}
void ArcApps::Publish(apps::mojom::AppPtr app) {
subscribers_.ForAllPtrs([&app](apps::mojom::Subscriber* subscriber) {
std::vector<apps::mojom::AppPtr> apps;
apps.push_back(app.Clone());
subscriber->OnApps(std::move(apps));
});
}
} // namespace apps