Commit 444e86be authored by nancylingwang's avatar nancylingwang Committed by Commit Bot

Modify intent to support the adaptive icon

Add the interface ArcActivityIconsToImageSkias in app_icon_factory.cc to
load a group of icons for the ARC activity.

Add ArcActivityAdaptiveIconImpl to generate the adaptive icons for the
ARC activity by calling ArcActivityIconsToImageSkias. Modify ArcApps to
set the ArcActivityAdaptiveIconImpl as the delegate for the
ActivityIconLoader if the adaptive icon is enabled.

Modify start_smart_selection_action_menu to generate the adaptive icons
by calling ArcActivityIconsToImageSkias.

BUG=1083331

Change-Id: I64a14c5459676593ceeedb442894ca9abe2cebe9
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2305816Reviewed-by: default avatarDavid Jacobo <djacobo@chromium.org>
Reviewed-by: default avatarDominick Ng <dominickn@chromium.org>
Reviewed-by: default avatarYusuke Sato <yusukes@chromium.org>
Commit-Queue: Nancy Wang <nancylingwang@chromium.org>
Cr-Commit-Position: refs/heads/master@{#792587}
parent 8c0b4876
......@@ -3816,6 +3816,8 @@ static_library("browser") {
if (is_chromeos) {
sources += [
"apps/app_service/arc_activity_adaptive_icon_impl.cc",
"apps/app_service/arc_activity_adaptive_icon_impl.h",
"apps/app_service/arc_apps.cc",
"apps/app_service/arc_apps.h",
"apps/app_service/arc_apps_factory.cc",
......
......@@ -7,7 +7,6 @@
#include <map>
#include <memory>
#include <utility>
#include <vector>
#include "base/bind.h"
#include "base/callback.h"
......@@ -257,6 +256,11 @@ class IconLoadingPipeline : public base::RefCounted<IconLoadingPipeline> {
: size_hint_in_dip_(size_hint_in_dip),
image_skia_callback_(std::move(callback)) {}
explicit IconLoadingPipeline(
base::OnceCallback<void(const std::vector<gfx::ImageSkia>& icons)>
callback)
: arc_activity_icons_callback_(std::move(callback)) {}
void LoadWebAppIcon(const std::string& web_app_id,
const GURL& launch_url,
const web_app::AppIconManager& icon_manager,
......@@ -280,6 +284,10 @@ class IconLoadingPipeline : public base::RefCounted<IconLoadingPipeline> {
// then apply the mask.
void LoadCompositeImages(const std::vector<uint8_t>& foreground_data,
const std::vector<uint8_t>& background_data);
// Loads icons for ARC activities.
void LoadArcActivityIcons(
const std::vector<arc::mojom::ActivityIconPtr>& icons);
#endif // OS_CHROMEOS
private:
......@@ -289,6 +297,13 @@ class IconLoadingPipeline : public base::RefCounted<IconLoadingPipeline> {
if (!callback_.is_null()) {
std::move(callback_).Run(apps::mojom::IconValue::New());
}
if (!image_skia_callback_.is_null()) {
std::move(image_skia_callback_).Run(gfx::ImageSkia());
}
if (!arc_activity_icons_callback_.is_null()) {
std::move(arc_activity_icons_callback_)
.Run(std::vector<gfx::ImageSkia>());
}
}
#if defined(OS_CHROMEOS)
......@@ -300,6 +315,9 @@ class IconLoadingPipeline : public base::RefCounted<IconLoadingPipeline> {
void CompositeImagesAndApplyMask(bool is_foreground,
const gfx::ImageSkia& image);
void OnArcActivityIconLoaded(gfx::ImageSkia* arc_activity_icon,
const gfx::ImageSkia& icon);
#endif // OS_CHROMEOS
void MaybeApplyEffectsAndComplete(const gfx::ImageSkia image);
......@@ -340,6 +358,11 @@ class IconLoadingPipeline : public base::RefCounted<IconLoadingPipeline> {
bool background_is_set_ = false;
base::OnceCallback<void(const gfx::ImageSkia& icon)> image_skia_callback_;
std::vector<gfx::ImageSkia> arc_activity_icons_;
size_t count_ = 0;
base::OnceCallback<void(const std::vector<gfx::ImageSkia>& icon)>
arc_activity_icons_callback_;
#if defined(OS_CHROMEOS)
std::unique_ptr<arc::IconDecodeRequest> arc_icon_decode_request_;
std::unique_ptr<arc::IconDecodeRequest> arc_foreground_icon_decode_request_;
......@@ -556,6 +579,35 @@ void IconLoadingPipeline::LoadCompositeImages(
background_data);
}
void IconLoadingPipeline::LoadArcActivityIcons(
const std::vector<arc::mojom::ActivityIconPtr>& icons) {
arc_activity_icons_.resize(icons.size());
DCHECK_EQ(0U, count_);
for (size_t i = 0; i < icons.size(); i++) {
if (!icons[i] || !icons[i]->icon_png_data) {
++count_;
continue;
}
constexpr size_t kMaxIconSizeInPx = 200;
if (icons[i]->width > kMaxIconSizeInPx ||
icons[i]->height > kMaxIconSizeInPx || icons[i]->width == 0 ||
icons[i]->height == 0) {
++count_;
continue;
}
apps::ArcRawIconPngDataToImageSkia(
std::move(icons[i]->icon_png_data), icons[i]->width,
base::BindOnce(&IconLoadingPipeline::OnArcActivityIconLoaded,
base::WrapRefCounted(this), &arc_activity_icons_[i]));
}
if (count_ == arc_activity_icons_.size() && !image_skia_callback_.is_null()) {
std::move(arc_activity_icons_callback_).Run(arc_activity_icons_);
}
}
std::unique_ptr<arc::IconDecodeRequest>
IconLoadingPipeline::CreateArcIconDecodeRequest(
base::OnceCallback<void(const gfx::ImageSkia& icon)> callback,
......@@ -587,7 +639,8 @@ void IconLoadingPipeline::CompositeImagesAndApplyMask(
background_image_ = image;
}
if (!foreground_is_set_ || !background_is_set_ || callback_.is_null()) {
if (!foreground_is_set_ || !background_is_set_ ||
image_skia_callback_.is_null()) {
return;
}
......@@ -605,6 +658,19 @@ void IconLoadingPipeline::CompositeImagesAndApplyMask(
skia::ImageOperations::RESIZE_BEST,
gfx::Size(size_hint_in_dip_, size_hint_in_dip_)));
}
void IconLoadingPipeline::OnArcActivityIconLoaded(
gfx::ImageSkia* arc_activity_icon,
const gfx::ImageSkia& icon) {
DCHECK(arc_activity_icon);
++count_;
*arc_activity_icon = icon;
if (count_ == arc_activity_icons_.size() &&
!arc_activity_icons_callback_.is_null()) {
std::move(arc_activity_icons_callback_).Run(arc_activity_icons_);
}
}
#endif // OS_CHROMEOS
void IconLoadingPipeline::MaybeApplyEffectsAndComplete(
......@@ -825,6 +891,20 @@ void ArcRawIconPngDataToImageSkia(
std::move(icon->foreground_icon_png_data.value()),
std::move(icon->background_icon_png_data.value()));
}
void ArcActivityIconsToImageSkias(
const std::vector<arc::mojom::ActivityIconPtr>& icons,
base::OnceCallback<void(const std::vector<gfx::ImageSkia>& icons)>
callback) {
if (icons.empty()) {
std::move(callback).Run(std::vector<gfx::ImageSkia>{});
return;
}
scoped_refptr<IconLoadingPipeline> icon_loader =
base::MakeRefCounted<IconLoadingPipeline>(std::move(callback));
icon_loader->LoadArcActivityIcons(icons);
}
#endif // OS_CHROMEOS
void ApplyIconEffects(IconEffects icon_effects,
......
......@@ -6,6 +6,7 @@
#define CHROME_BROWSER_APPS_APP_SERVICE_APP_ICON_FACTORY_H_
#include <string>
#include <vector>
#include "base/callback_forward.h"
#include "base/files/file_path.h"
......@@ -16,6 +17,7 @@
#if defined(OS_CHROMEOS)
#include "components/arc/mojom/app.mojom.h"
#include "components/arc/mojom/intent_helper.mojom.h"
#endif // OS_CHROMEOS
namespace content {
......@@ -63,6 +65,11 @@ void ArcRawIconPngDataToImageSkia(
arc::mojom::RawIconPngDataPtr icon,
int size_hint_in_dip,
base::OnceCallback<void(const gfx::ImageSkia& icon)> callback);
void ArcActivityIconsToImageSkias(
const std::vector<arc::mojom::ActivityIconPtr>& icons,
base::OnceCallback<void(const std::vector<gfx::ImageSkia>& icons)>
callback);
#endif // OS_CHROMEOS
// Modifies |image_skia| to apply icon post-processing effects like badging and
......
......@@ -9,6 +9,7 @@
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/files/scoped_temp_dir.h"
#include "base/path_service.h"
#include "base/run_loop.h"
#include "cc/test/pixel_comparator.h"
#include "cc/test/pixel_test_utils.h"
......@@ -16,6 +17,11 @@
#include "content/public/test/browser_task_environment.h"
#include "testing/gtest/include/gtest/gtest.h"
#if defined(OS_CHROMEOS)
#include "chrome/browser/chromeos/arc/icon_decode_request.h"
#include "components/arc/mojom/intent_helper.mojom.h"
#endif
class AppIconFactoryTest : public testing::Test {
public:
base::FilePath GetPath() {
......@@ -56,6 +62,20 @@ class AppIconFactoryTest : public testing::Test {
run_loop_.Run();
}
std::string GetPngData(const std::string file_name) {
base::FilePath base_path;
std::string png_data_as_string;
CHECK(base::PathService::Get(base::DIR_SOURCE_ROOT, &base_path));
base::FilePath icon_file_path = base_path.AppendASCII("components")
.AppendASCII("test")
.AppendASCII("data")
.AppendASCII("arc")
.AppendASCII(file_name);
CHECK(base::PathExists(icon_file_path));
CHECK(base::ReadFileToString(icon_file_path, &png_data_as_string));
return png_data_as_string;
}
protected:
content::BrowserTaskEnvironment task_env_{};
base::ScopedTempDir tmp_dir_{};
......@@ -138,3 +158,121 @@ TEST_F(AppIconFactoryTest, LoadFromFileFallbackDoesNotReturn) {
EXPECT_TRUE(fallback_called);
EXPECT_FALSE(result.is_null());
}
#if defined(OS_CHROMEOS)
TEST_F(AppIconFactoryTest, ArcNonAdaptiveIconToImageSkia) {
arc::IconDecodeRequest::DisableSafeDecodingForTesting();
std::string png_data_as_string = GetPngData("icon_100p.png");
arc::mojom::RawIconPngDataPtr icon = arc::mojom::RawIconPngData::New(
false,
std::vector<uint8_t>(png_data_as_string.begin(),
png_data_as_string.end()),
std::vector<uint8_t>(), std::vector<uint8_t>());
bool callback_called = false;
apps::ArcRawIconPngDataToImageSkia(
std::move(icon), 100,
base::BindOnce(
[](bool* called, base::OnceClosure quit,
const gfx::ImageSkia& image) {
if (!image.isNull()) {
*called = true;
}
std::move(quit).Run();
},
base::Unretained(&callback_called), run_loop_.QuitClosure()));
run_loop_.Run();
EXPECT_TRUE(callback_called);
}
TEST_F(AppIconFactoryTest, ArcAdaptiveIconToImageSkia) {
arc::IconDecodeRequest::DisableSafeDecodingForTesting();
std::string png_data_as_string = GetPngData("icon_100p.png");
arc::mojom::RawIconPngDataPtr icon = arc::mojom::RawIconPngData::New(
true, std::vector<uint8_t>(),
std::vector<uint8_t>(png_data_as_string.begin(),
png_data_as_string.end()),
std::vector<uint8_t>(png_data_as_string.begin(),
png_data_as_string.end()));
bool callback_called = false;
apps::ArcRawIconPngDataToImageSkia(
std::move(icon), 100,
base::BindOnce(
[](bool* called, base::OnceClosure quit,
const gfx::ImageSkia& image) {
if (!image.isNull()) {
*called = true;
}
std::move(quit).Run();
},
base::Unretained(&callback_called), run_loop_.QuitClosure()));
run_loop_.Run();
EXPECT_TRUE(callback_called);
}
TEST_F(AppIconFactoryTest, ArcActivityIconsToImageSkias) {
arc::IconDecodeRequest::DisableSafeDecodingForTesting();
std::string png_data_as_string = GetPngData("icon_100p.png");
std::vector<arc::mojom::ActivityIconPtr> icons;
icons.emplace_back(
arc::mojom::ActivityIcon::New(arc::mojom::ActivityName::New("p0", "a0"),
100, 100, std::vector<uint8_t>()));
icons.emplace_back(arc::mojom::ActivityIcon::New(
arc::mojom::ActivityName::New("p0", "a0"), 100, 100,
std::vector<uint8_t>(),
arc::mojom::RawIconPngData::New(
false,
std::vector<uint8_t>(png_data_as_string.begin(),
png_data_as_string.end()),
std::vector<uint8_t>(), std::vector<uint8_t>())));
icons.emplace_back(arc::mojom::ActivityIcon::New(
arc::mojom::ActivityName::New("p0", "a0"), 201, 201,
std::vector<uint8_t>(),
arc::mojom::RawIconPngData::New(
false,
std::vector<uint8_t>(png_data_as_string.begin(),
png_data_as_string.end()),
std::vector<uint8_t>(), std::vector<uint8_t>())));
icons.emplace_back(arc::mojom::ActivityIcon::New(
arc::mojom::ActivityName::New("p1", "a1"), 100, 100,
std::vector<uint8_t>(),
arc::mojom::RawIconPngData::New(
true, std::vector<uint8_t>(),
std::vector<uint8_t>(png_data_as_string.begin(),
png_data_as_string.end()),
std::vector<uint8_t>(png_data_as_string.begin(),
png_data_as_string.end()))));
std::vector<gfx::ImageSkia> result;
bool callback_called = false;
apps::ArcActivityIconsToImageSkias(
icons, base::BindOnce(
[](bool* called, std::vector<gfx::ImageSkia>* result,
base::OnceClosure quit,
const std::vector<gfx::ImageSkia>& images) {
*called = true;
for (auto image : images) {
result->emplace_back(image);
}
std::move(quit).Run();
},
base::Unretained(&callback_called), base::Unretained(&result),
run_loop_.QuitClosure()));
run_loop_.Run();
EXPECT_TRUE(callback_called);
EXPECT_EQ(4U, result.size());
if (result.size() == 4U) {
EXPECT_TRUE(result[0].isNull());
EXPECT_FALSE(result[1].isNull());
EXPECT_TRUE(result[2].isNull());
EXPECT_FALSE(result[3].isNull());
}
}
#endif
// Copyright 2020 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_activity_adaptive_icon_impl.h"
#include <utility>
#include "base/bind.h"
#include "chrome/browser/apps/app_service/app_icon_factory.h"
#include "components/arc/intent_helper/adaptive_icon_delegate.h"
namespace apps {
ArcActivityAdaptiveIconImpl::ArcActivityAdaptiveIconImpl() = default;
ArcActivityAdaptiveIconImpl::~ArcActivityAdaptiveIconImpl() = default;
void ArcActivityAdaptiveIconImpl::GenerateAdaptiveIcons(
const std::vector<arc::mojom::ActivityIconPtr>& icons,
AdaptiveIconDelegateCallback callback) {
apps::ArcActivityIconsToImageSkias(
icons, base::BindOnce(
[](AdaptiveIconDelegateCallback callback,
const std::vector<gfx::ImageSkia>& images) {
std::move(callback).Run(images);
},
std::move(callback)));
}
} // namespace apps
// Copyright 2020 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.
#ifndef CHROME_BROWSER_APPS_APP_SERVICE_ARC_ACTIVITY_ADAPTIVE_ICON_IMPL_H_
#define CHROME_BROWSER_APPS_APP_SERVICE_ARC_ACTIVITY_ADAPTIVE_ICON_IMPL_H_
#include <vector>
#include "components/arc/intent_helper/adaptive_icon_delegate.h"
#include "components/arc/mojom/intent_helper.mojom.h"
namespace arc {
class AdaptiveIconDelegate;
}
namespace apps {
// An icon loader specific to an activity icon from ARC, to generate the
// adaptive icon with the raw foreground and background icon images.
class ArcActivityAdaptiveIconImpl : public arc::AdaptiveIconDelegate {
public:
ArcActivityAdaptiveIconImpl();
~ArcActivityAdaptiveIconImpl() override;
ArcActivityAdaptiveIconImpl(const ArcActivityAdaptiveIconImpl&) = delete;
ArcActivityAdaptiveIconImpl& operator=(const ArcActivityAdaptiveIconImpl&) =
delete;
void GenerateAdaptiveIcons(
const std::vector<arc::mojom::ActivityIconPtr>& icons,
AdaptiveIconDelegateCallback callback) override;
};
} // namespace apps
#endif // CHROME_BROWSER_APPS_APP_SERVICE_ARC_ACTIVITY_ADAPTIVE_ICON_IMPL_H_
......@@ -587,6 +587,10 @@ ArcApps::ArcApps(Profile* profile, apps::AppServiceProxy* proxy)
auto* intent_helper_bridge =
arc::ArcIntentHelperBridge::GetForBrowserContext(profile_);
if (intent_helper_bridge) {
if (base::FeatureList::IsEnabled(features::kAppServiceAdaptiveIcon)) {
intent_helper_bridge->SetAdaptiveIconDelegate(
&arc_activity_adaptive_icon_impl_);
}
arc_intent_helper_observer_.Add(intent_helper_bridge);
}
......@@ -633,6 +637,13 @@ void ArcApps::Shutdown() {
}
arc_icon_once_loader_.StopObserving(prefs);
auto* intent_helper_bridge =
arc::ArcIntentHelperBridge::GetForBrowserContext(profile_);
if (intent_helper_bridge &&
base::FeatureList::IsEnabled(features::kAppServiceAdaptiveIcon)) {
intent_helper_bridge->SetAdaptiveIconDelegate(nullptr);
}
arc_intent_helper_observer_.RemoveAll();
}
......
......@@ -20,6 +20,7 @@
#include "base/scoped_observer.h"
#include "chrome/browser/apps/app_service/app_icon_factory.h"
#include "chrome/browser/apps/app_service/app_notifications.h"
#include "chrome/browser/apps/app_service/arc_activity_adaptive_icon_impl.h"
#include "chrome/browser/apps/app_service/arc_icon_once_loader.h"
#include "chrome/browser/apps/app_service/icon_key_util.h"
#include "chrome/browser/apps/app_service/paused_apps.h"
......@@ -189,6 +190,7 @@ class ArcApps : public KeyedService,
Profile* const profile_;
ArcIconOnceLoader arc_icon_once_loader_;
ArcActivityAdaptiveIconImpl arc_activity_adaptive_icon_impl_;
apps_util::IncrementingIconKeyFactory icon_key_factory_;
......
......@@ -14,6 +14,8 @@
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
#include "chrome/app/chrome_command_ids.h"
#include "chrome/browser/apps/app_service/app_icon_factory.h"
#include "chrome/common/chrome_features.h"
#include "chrome/grit/generated_resources.h"
#include "components/arc/arc_features.h"
#include "components/arc/arc_service_manager.h"
......@@ -23,7 +25,6 @@
#include "components/arc/session/arc_bridge_service.h"
#include "components/renderer_context_menu/render_view_context_menu_proxy.h"
#include "content/public/browser/context_menu_params.h"
#include "ui/gfx/image/image_skia.h"
#include "ui/gfx/image/image_skia_operations.h"
namespace arc {
......@@ -121,11 +122,8 @@ void StartSmartSelectionActionMenu::HandleTextSelectionActions(
/*title=*/base::UTF8ToUTF16(actions_[i]->title));
if (actions_[i]->icon) {
gfx::Image icon = GetIconImage(std::move(actions_[i]->icon));
if (!icon.IsEmpty()) {
proxy_->UpdateMenuIcon(
IDC_CONTENT_CONTEXT_START_SMART_SELECTION_ACTION1 + i, icon);
}
UpdateMenuIcon(IDC_CONTENT_CONTEXT_START_SMART_SELECTION_ACTION1 + i,
std::move(actions_[i]->icon));
}
}
......@@ -143,19 +141,30 @@ void StartSmartSelectionActionMenu::HandleTextSelectionActions(
proxy_->RemoveAdjacentSeparators();
}
gfx::Image StartSmartSelectionActionMenu::GetIconImage(
void StartSmartSelectionActionMenu::UpdateMenuIcon(
int command_id,
mojom::ActivityIconPtr icon) {
constexpr size_t kBytesPerPixel = 4; // BGRA
if (icon->width > kMaxIconSizeInPx || icon->height > kMaxIconSizeInPx ||
icon->width == 0 || icon->height == 0 ||
icon->icon.size() != (icon->width * icon->height * kBytesPerPixel)) {
return gfx::Image();
return;
}
if (base::FeatureList::IsEnabled(features::kAppServiceAdaptiveIcon)) {
DCHECK(icon->icon_png_data);
apps::ArcRawIconPngDataToImageSkia(
std::move(icon->icon_png_data), kSmallIconSizeInDip,
base::BindOnce(&StartSmartSelectionActionMenu::SetMenuIcon,
weak_ptr_factory_.GetWeakPtr(), command_id));
return;
}
SkBitmap bitmap;
bitmap.allocPixels(SkImageInfo::MakeN32Premul(icon->width, icon->height));
if (!bitmap.getPixels())
return gfx::Image();
return;
DCHECK_GE(bitmap.computeByteSize(), icon->icon.size());
memcpy(bitmap.getPixels(), &icon->icon.front(), icon->icon.size());
......@@ -165,7 +174,12 @@ gfx::Image StartSmartSelectionActionMenu::GetIconImage(
original, skia::ImageOperations::RESIZE_BEST,
gfx::Size(kSmallIconSizeInDip, kSmallIconSizeInDip)));
return gfx::Image(icon_small);
SetMenuIcon(command_id, icon_small);
}
void StartSmartSelectionActionMenu::SetMenuIcon(int command_id,
const gfx::ImageSkia& image) {
proxy_->UpdateMenuIcon(command_id, gfx::Image(image));
}
} // namespace arc
......@@ -12,6 +12,7 @@
#include "components/arc/mojom/intent_helper.mojom.h"
#include "components/renderer_context_menu/render_view_context_menu_observer.h"
#include "ui/gfx/image/image.h"
#include "ui/gfx/image/image_skia.h"
class RenderViewContextMenuProxy;
......@@ -35,7 +36,9 @@ class StartSmartSelectionActionMenu : public RenderViewContextMenuObserver {
void HandleTextSelectionActions(
std::vector<mojom::TextSelectionActionPtr> actions);
gfx::Image GetIconImage(mojom::ActivityIconPtr icon);
void UpdateMenuIcon(int command_id, mojom::ActivityIconPtr icon);
void SetMenuIcon(int command_id, const gfx::ImageSkia& image);
RenderViewContextMenuProxy* const proxy_; // Owned by RenderViewContextMenu.
......
......@@ -30,6 +30,7 @@ static_library("arc") {
"ime/arc_ime_service.h",
"intent_helper/activity_icon_loader.cc",
"intent_helper/activity_icon_loader.h",
"intent_helper/adaptive_icon_delegate.h",
"intent_helper/arc_intent_helper_bridge.cc",
"intent_helper/arc_intent_helper_bridge.h",
"intent_helper/arc_intent_helper_observer.h",
......
......@@ -16,10 +16,10 @@
#include "base/task/thread_pool.h"
#include "components/arc/arc_service_manager.h"
#include "components/arc/arc_util.h"
#include "components/arc/intent_helper/adaptive_icon_delegate.h"
#include "components/arc/session/arc_bridge_service.h"
#include "ui/base/layout.h"
#include "ui/gfx/codec/png_codec.h"
#include "ui/gfx/image/image_skia.h"
#include "ui/gfx/image/image_skia_operations.h"
namespace arc {
......@@ -80,6 +80,14 @@ mojom::IntentHelperInstance* GetInstanceForRequestActivityIcons(
return instance;
}
ActivityIconLoader::ActivityName GenerateActivityName(
const mojom::ActivityIconPtr& icon) {
return ActivityIconLoader::ActivityName(
icon->activity->package_name, icon->activity->activity_name.has_value()
? (*icon->activity->activity_name)
: std::string());
}
// Encodes the |image| as PNG data considering scale factor, and returns it as
// data: URL.
scoped_refptr<base::RefCountedData<GURL>> GeneratePNGDataUrl(
......@@ -98,6 +106,23 @@ scoped_refptr<base::RefCountedData<GURL>> GeneratePNGDataUrl(
new base::RefCountedData<GURL>(GURL(kPngDataUrlPrefix + encoded)));
}
ActivityIconLoader::Icons ResizeIconsInternal(const gfx::ImageSkia& image,
ui::ScaleFactor scale_factor) {
// Resize the original icon to the sizes intent_helper needs.
gfx::ImageSkia icon_large(gfx::ImageSkiaOperations::CreateResizedImage(
image, skia::ImageOperations::RESIZE_BEST,
gfx::Size(kLargeIconSizeInDip, kLargeIconSizeInDip)));
gfx::Image icon20(icon_large);
gfx::ImageSkia icon_small(gfx::ImageSkiaOperations::CreateResizedImage(
image, skia::ImageOperations::RESIZE_BEST,
gfx::Size(kSmallIconSizeInDip, kSmallIconSizeInDip)));
gfx::Image icon16(icon_small);
return ActivityIconLoader::Icons(
icon16, icon20, GeneratePNGDataUrl(icon_small, scale_factor));
}
std::unique_ptr<ActivityIconLoader::ActivityToIconsMap> ResizeAndEncodeIcons(
std::vector<mojom::ActivityIconPtr> icons,
ui::ScaleFactor scale_factor) {
......@@ -120,24 +145,22 @@ std::unique_ptr<ActivityIconLoader::ActivityToIconsMap> ResizeAndEncodeIcons(
gfx::ImageSkia original(gfx::ImageSkia::CreateFrom1xBitmap(bitmap));
// Resize the original icon to the sizes intent_helper needs.
gfx::ImageSkia icon_large(gfx::ImageSkiaOperations::CreateResizedImage(
original, skia::ImageOperations::RESIZE_BEST,
gfx::Size(kLargeIconSizeInDip, kLargeIconSizeInDip)));
gfx::ImageSkia icon_small(gfx::ImageSkiaOperations::CreateResizedImage(
original, skia::ImageOperations::RESIZE_BEST,
gfx::Size(kSmallIconSizeInDip, kSmallIconSizeInDip)));
gfx::Image icon16(icon_small);
gfx::Image icon20(icon_large);
result->insert(std::make_pair(GenerateActivityName(icon),
ResizeIconsInternal(original, scale_factor)));
}
const std::string activity_name = icon->activity->activity_name.has_value()
? (*icon->activity->activity_name)
: std::string();
return result;
}
std::unique_ptr<ActivityIconLoader::ActivityToIconsMap> ResizeIcons(
std::vector<ActivityIconLoader::ActivityName> activity_names,
const std::vector<gfx::ImageSkia>& images,
ui::ScaleFactor scale_factor) {
DCHECK_EQ(activity_names.size(), images.size());
auto result = std::make_unique<ActivityIconLoader::ActivityToIconsMap>();
for (size_t i = 0; i < activity_names.size(); ++i) {
result->insert(std::make_pair(
ActivityIconLoader::ActivityName(icon->activity->package_name,
activity_name),
ActivityIconLoader::Icons(
icon16, icon20, GeneratePNGDataUrl(icon_small, scale_factor))));
activity_names[i], ResizeIconsInternal(images[i], scale_factor)));
}
return result;
......@@ -170,6 +193,11 @@ ActivityIconLoader::ActivityIconLoader()
ActivityIconLoader::~ActivityIconLoader() = default;
void ActivityIconLoader::SetAdaptiveIconDelegate(
AdaptiveIconDelegate* delegate) {
delegate_ = delegate;
}
void ActivityIconLoader::InvalidateIcons(const std::string& package_name) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
for (auto it = cached_icons_.begin(); it != cached_icons_.end();) {
......@@ -235,6 +263,13 @@ void ActivityIconLoader::AddCacheEntryForTesting(const ActivityName& activity) {
std::make_pair(activity, Icons(gfx::Image(), gfx::Image(), nullptr)));
}
void ActivityIconLoader::OnIconsReadyForTesting(
std::unique_ptr<ActivityToIconsMap> cached_result,
OnIconsReadyCallback cb,
std::vector<mojom::ActivityIconPtr> icons) {
OnIconsReady(std::move(cached_result), std::move(cb), std::move(icons));
}
// static
bool ActivityIconLoader::HasIconsReadyCallbackRun(GetResult result) {
switch (result) {
......@@ -253,6 +288,21 @@ void ActivityIconLoader::OnIconsReady(
OnIconsReadyCallback cb,
std::vector<mojom::ActivityIconPtr> icons) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
if (delegate_) {
std::vector<ActivityName> actvity_names;
for (const auto& icon : icons)
actvity_names.emplace_back(GenerateActivityName(icon));
delegate_->GenerateAdaptiveIcons(
icons,
base::BindOnce(&ActivityIconLoader::OnAdaptiveIconGenerated,
weak_ptr_factory_.GetWeakPtr(), std::move(actvity_names),
std::move(cached_result), std::move(cb)));
return;
}
// TODO(crbug.com/1083331): Remove when the adaptive icon feature is enabled
// by default.
base::ThreadPool::PostTaskAndReplyWithResult(
FROM_HERE,
base::BindOnce(&ResizeAndEncodeIcons, std::move(icons), scale_factor_),
......@@ -261,6 +311,22 @@ void ActivityIconLoader::OnIconsReady(
std::move(cb)));
}
void ActivityIconLoader::OnAdaptiveIconGenerated(
std::vector<ActivityName> actvity_names,
std::unique_ptr<ActivityToIconsMap> cached_result,
OnIconsReadyCallback cb,
const std::vector<gfx::ImageSkia>& adaptive_icons) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
base::ThreadPool::PostTaskAndReplyWithResult(
FROM_HERE,
base::BindOnce(&ResizeIcons, std::move(actvity_names), adaptive_icons,
scale_factor_),
base::BindOnce(&ActivityIconLoader::OnIconsResized,
weak_ptr_factory_.GetWeakPtr(), std::move(cached_result),
std::move(cb)));
}
void ActivityIconLoader::OnIconsResized(
std::unique_ptr<ActivityToIconsMap> cached_result,
OnIconsReadyCallback cb,
......
......@@ -18,9 +18,13 @@
#include "components/arc/mojom/intent_helper.mojom.h"
#include "ui/base/layout.h"
#include "ui/gfx/image/image.h"
#include "ui/gfx/image/image_skia.h"
#include "url/gurl.h"
namespace arc {
class AdaptiveIconDelegate;
namespace internal {
// A class which retrieves an activity icon from ARC.
......@@ -71,6 +75,8 @@ class ActivityIconLoader {
ActivityIconLoader();
~ActivityIconLoader();
void SetAdaptiveIconDelegate(AdaptiveIconDelegate* delegate);
// Removes icons associated with |package_name| from the cache.
void InvalidateIcons(const std::string& package_name);
......@@ -86,6 +92,10 @@ class ActivityIconLoader {
std::unique_ptr<ActivityToIconsMap> result);
void AddCacheEntryForTesting(const ActivityName& activity);
void OnIconsReadyForTesting(std::unique_ptr<ActivityToIconsMap> cached_result,
OnIconsReadyCallback cb,
std::vector<mojom::ActivityIconPtr> icons);
// Returns true if |result| indicates that the |cb| object passed to
// GetActivityIcons() has already called.
static bool HasIconsReadyCallbackRun(GetResult result);
......@@ -98,6 +108,13 @@ class ActivityIconLoader {
OnIconsReadyCallback cb,
std::vector<mojom::ActivityIconPtr> icons);
// A function called when the adaptive icons are generated.
void OnAdaptiveIconGenerated(
std::vector<ActivityName> actvity_names,
std::unique_ptr<ActivityToIconsMap> cached_result,
OnIconsReadyCallback cb,
const std::vector<gfx::ImageSkia>& adaptive_icons);
// A function called when ResizeIcons finishes. Append items in |result| to
// |cached_icons_|.
void OnIconsResized(std::unique_ptr<ActivityToIconsMap> cached_result,
......@@ -109,6 +126,9 @@ class ActivityIconLoader {
// A map which holds icons in a scale-factor independent form (gfx::Image).
ActivityToIconsMap cached_icons_;
// A delegate which converts the icon to the adaptive icon.
AdaptiveIconDelegate* delegate_ = nullptr;
THREAD_CHECKER(thread_checker_);
// This must come last to make sure weak pointers are invalidated first.
......
......@@ -9,7 +9,12 @@
#include <vector>
#include "base/bind.h"
#include "base/callback.h"
#include "base/run_loop.h"
#include "base/test/task_environment.h"
#include "components/arc/intent_helper/adaptive_icon_delegate.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/gfx/image/image_skia.h"
namespace arc {
namespace internal {
......@@ -47,6 +52,39 @@ void OnIconsReady3(
ActivityIconLoader::ActivityName("p2", "a2")));
}
class FakeAdaptiveIconDelegate : public AdaptiveIconDelegate {
public:
FakeAdaptiveIconDelegate() = default;
~FakeAdaptiveIconDelegate() override = default;
FakeAdaptiveIconDelegate(const FakeAdaptiveIconDelegate&) = delete;
FakeAdaptiveIconDelegate& operator=(const FakeAdaptiveIconDelegate&) = delete;
void GenerateAdaptiveIcons(
const std::vector<arc::mojom::ActivityIconPtr>& icons,
AdaptiveIconDelegateCallback callback) override {
++count_;
std::vector<gfx::ImageSkia> result;
for (const auto& icon : icons) {
if (icon && icon->icon_png_data &&
icon->icon_png_data->is_adaptive_icon &&
icon->icon_png_data->foreground_icon_png_data) {
auto png_data(icon->icon_png_data->foreground_icon_png_data.value());
png_data_.emplace_back(std::string(png_data.begin(), png_data.end()));
result.emplace_back(gfx::ImageSkia());
}
}
std::move(callback).Run(std::move(result));
}
std::vector<std::string> png_data() { return png_data_; }
int count() { return count_; }
private:
int count_ = 0;
std::vector<std::string> png_data_;
};
// Tests if InvalidateIcons properly cleans up the cache.
TEST(ActivityIconLoaderTest, TestInvalidateIcons) {
ActivityIconLoader loader;
......@@ -156,6 +194,129 @@ TEST(ActivityIconLoaderTest, TestOnIconsResized) {
ActivityIconLoader::ActivityName("p2", "a2")));
}
class ActivityIconLoaderOnIconsReadyTest : public ::testing::Test {
public:
ActivityIconLoaderOnIconsReadyTest() = default;
~ActivityIconLoaderOnIconsReadyTest() override = default;
ActivityIconLoaderOnIconsReadyTest(
const ActivityIconLoaderOnIconsReadyTest&) = delete;
ActivityIconLoaderOnIconsReadyTest& operator=(
const ActivityIconLoaderOnIconsReadyTest&) = delete;
void OnIconsReady(std::unique_ptr<ActivityIconLoader::ActivityToIconsMap>
activity_to_icons) {
EXPECT_EQ(4U, activity_to_icons->size());
EXPECT_EQ(1U, activity_to_icons->count(
ActivityIconLoader::ActivityName("p0", "a0")));
EXPECT_EQ(1U, activity_to_icons->count(
ActivityIconLoader::ActivityName("p1", "a1")));
EXPECT_EQ(1U, activity_to_icons->count(
ActivityIconLoader::ActivityName("p2", "a2")));
EXPECT_EQ(1U, activity_to_icons->count(
ActivityIconLoader::ActivityName("p3", "a3")));
if (!on_icon_ready_callback_.is_null())
std::move(on_icon_ready_callback_).Run();
}
void WaitForIconReady() {
base::RunLoop run_loop;
on_icon_ready_callback_ = run_loop.QuitClosure();
run_loop.Run();
}
base::OnceClosure on_icon_ready_callback_;
base::test::TaskEnvironment task_environment_;
base::WeakPtrFactory<ActivityIconLoaderOnIconsReadyTest> weak_ptr_factory_{
this};
};
// Tests OnIconsReady with a delegate.
TEST_F(ActivityIconLoaderOnIconsReadyTest, TestWithDelegate) {
ActivityIconLoader loader;
std::unique_ptr<ActivityIconLoader::ActivityToIconsMap> activity_to_icons(
new ActivityIconLoader::ActivityToIconsMap);
auto activity_name0 = ActivityIconLoader::ActivityName("p0", "a0");
auto activity_name1 = ActivityIconLoader::ActivityName("p1", "a1");
loader.AddCacheEntryForTesting(activity_name0);
loader.AddCacheEntryForTesting(activity_name1);
activity_to_icons->insert(std::make_pair(
activity_name0,
ActivityIconLoader::Icons(gfx::Image(), gfx::Image(), nullptr)));
activity_to_icons->insert(std::make_pair(
activity_name1,
ActivityIconLoader::Icons(gfx::Image(), gfx::Image(), nullptr)));
std::vector<mojom::ActivityIconPtr> icons;
std::string foreground_png_data_as_string0 = "FOREGROUND_ICON_CONTENT_0";
std::string foreground_png_data_as_string1 = "FOREGROUND_ICON_CONTENT_1";
icons.emplace_back(mojom::ActivityIcon::New(
mojom::ActivityName::New("p2", "a2"), 32, 32, std::vector<uint8_t>(),
mojom::RawIconPngData::New(
true, std::vector<uint8_t>(),
std::vector<uint8_t>(foreground_png_data_as_string0.begin(),
foreground_png_data_as_string0.end()),
std::vector<uint8_t>())));
icons.emplace_back(mojom::ActivityIcon::New(
mojom::ActivityName::New("p3", "a3"), 32, 32, std::vector<uint8_t>(),
mojom::RawIconPngData::New(
true, std::vector<uint8_t>(),
std::vector<uint8_t>(foreground_png_data_as_string1.begin(),
foreground_png_data_as_string1.end()),
std::vector<uint8_t>())));
FakeAdaptiveIconDelegate delegate;
loader.SetAdaptiveIconDelegate(&delegate);
// Call OnIconsReady() and check that the cache is properly updated.
loader.OnIconsReadyForTesting(
std::move(activity_to_icons),
base::BindOnce(&ActivityIconLoaderOnIconsReadyTest::OnIconsReady,
weak_ptr_factory_.GetWeakPtr()),
std::move(icons));
EXPECT_EQ(1, delegate.count());
EXPECT_EQ(2U, delegate.png_data().size());
EXPECT_EQ(foreground_png_data_as_string0, delegate.png_data()[0]);
EXPECT_EQ(foreground_png_data_as_string1, delegate.png_data()[1]);
WaitForIconReady();
}
// Tests OnIconsReady without a delegate.
TEST_F(ActivityIconLoaderOnIconsReadyTest, TestWithoutDelegate) {
ActivityIconLoader loader;
std::unique_ptr<ActivityIconLoader::ActivityToIconsMap> activity_to_icons(
new ActivityIconLoader::ActivityToIconsMap);
auto activity_name0 = ActivityIconLoader::ActivityName("p0", "a0");
auto activity_name1 = ActivityIconLoader::ActivityName("p1", "a1");
loader.AddCacheEntryForTesting(activity_name0);
loader.AddCacheEntryForTesting(activity_name1);
activity_to_icons->insert(std::make_pair(
activity_name0,
ActivityIconLoader::Icons(gfx::Image(), gfx::Image(), nullptr)));
activity_to_icons->insert(std::make_pair(
activity_name1,
ActivityIconLoader::Icons(gfx::Image(), gfx::Image(), nullptr)));
std::vector<mojom::ActivityIconPtr> icons;
icons.emplace_back(mojom::ActivityIcon::New(
mojom::ActivityName::New("p2", "a2"), 1, 1, std::vector<uint8_t>(4),
mojom::RawIconPngData::New()));
icons.emplace_back(mojom::ActivityIcon::New(
mojom::ActivityName::New("p3", "a3"), 1, 1, std::vector<uint8_t>(4),
mojom::RawIconPngData::New()));
// Call OnIconsReady() and check that the cache is properly updated.
loader.OnIconsReadyForTesting(
std::move(activity_to_icons),
base::BindOnce(&ActivityIconLoaderOnIconsReadyTest::OnIconsReady,
weak_ptr_factory_.GetWeakPtr()),
std::move(icons));
WaitForIconReady();
}
} // namespace
} // namespace internal
} // namespace arc
// Copyright 2020 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.
#ifndef COMPONENTS_ARC_INTENT_HELPER_ADAPTIVE_ICON_DELEGATE_H_
#define COMPONENTS_ARC_INTENT_HELPER_ADAPTIVE_ICON_DELEGATE_H_
#include <vector>
#include "base/callback.h"
#include "components/arc/mojom/intent_helper.mojom.h"
#include "ui/gfx/image/image_skia.h"
namespace arc {
class AdaptiveIconDelegate {
public:
using AdaptiveIconDelegateCallback = base::OnceCallback<void(
const std::vector<gfx::ImageSkia>& adaptive_icons)>;
virtual ~AdaptiveIconDelegate() = default;
// Generates adaptive icons from the |icons| and calls |callback|.
virtual void GenerateAdaptiveIcons(
const std::vector<mojom::ActivityIconPtr>& icons,
AdaptiveIconDelegateCallback callback) = 0;
};
} // namespace arc
#endif // COMPONENTS_ARC_INTENT_HELPER_ADAPTIVE_ICON_DELEGATE_H_
......@@ -318,6 +318,11 @@ bool ArcIntentHelperBridge::ShouldChromeHandleUrl(const GURL& url) {
return true;
}
void ArcIntentHelperBridge::SetAdaptiveIconDelegate(
AdaptiveIconDelegate* delegate) {
icon_loader_.SetAdaptiveIconDelegate(delegate);
}
void ArcIntentHelperBridge::AddObserver(ArcIntentHelperObserver* observer) {
observer_list_.AddObserver(observer);
}
......
......@@ -29,6 +29,7 @@ class BrowserContext;
namespace arc {
class AdaptiveIconDelegate;
class ArcBridgeService;
class ControlCameraAppDelegate;
class FactoryResetDelegate;
......@@ -61,6 +62,8 @@ class ArcIntentHelperBridge : public KeyedService,
ArcBridgeService* bridge_service);
~ArcIntentHelperBridge() override;
void SetAdaptiveIconDelegate(AdaptiveIconDelegate* delegate);
void AddObserver(ArcIntentHelperObserver* observer);
void RemoveObserver(ArcIntentHelperObserver* observer);
bool HasObserver(ArcIntentHelperObserver* observer) const;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment