Commit 2de5bd8b authored by Jeroen Dhollander's avatar Jeroen Dhollander Committed by Commit Bot

Introduce BloomTray

This is a tray icon to launch Bloom.
As the UX team has not decided yet how Bloom should be launched,
this is is a temporary prototype and as such it is not completely
fleshed out.
More specifically, no strings have been localized, and the icon is a
random other icon.

This is hidden behid the |EnableBloom| feature flag and as such will not
impact anybody.

Bug: b/165871172
Tests: ash_unittests --gtest_filter="Bloom*.*"
Change-Id: I87a708f933d450f98659abeb9398370b993d516c
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2368850
Commit-Queue: Jeroen Dhollander <jeroendh@chromium.org>
Reviewed-by: default avatarXiyuan Slow <xiyuan@chromium.org>
Reviewed-by: default avatarXiaohui Chen <xiaohuic@chromium.org>
Cr-Commit-Position: refs/heads/master@{#803896}
parent a4e31f11
...@@ -872,6 +872,8 @@ component("ash") { ...@@ -872,6 +872,8 @@ component("ash") {
"system/audio/unified_volume_slider_controller.h", "system/audio/unified_volume_slider_controller.h",
"system/audio/unified_volume_view.cc", "system/audio/unified_volume_view.cc",
"system/audio/unified_volume_view.h", "system/audio/unified_volume_view.h",
"system/bloom/bloom_tray.cc",
"system/bloom/bloom_tray.h",
"system/bluetooth/bluetooth_detailed_view.cc", "system/bluetooth/bluetooth_detailed_view.cc",
"system/bluetooth/bluetooth_detailed_view.h", "system/bluetooth/bluetooth_detailed_view.h",
"system/bluetooth/bluetooth_feature_pod_controller.cc", "system/bluetooth/bluetooth_feature_pod_controller.cc",
...@@ -1627,6 +1629,7 @@ component("ash") { ...@@ -1627,6 +1629,7 @@ component("ash") {
"//cc/debug", "//cc/debug",
"//cc/paint:paint", "//cc/paint:paint",
"//chromeos/assistant:buildflags", "//chromeos/assistant:buildflags",
"//chromeos/components/bloom/public/cpp",
"//chromeos/components/quick_answers", "//chromeos/components/quick_answers",
"//chromeos/components/quick_answers/public/cpp:prefs", "//chromeos/components/quick_answers/public/cpp:prefs",
"//services/viz/public/mojom", "//services/viz/public/mojom",
...@@ -2016,6 +2019,7 @@ test("ash_unittests") { ...@@ -2016,6 +2019,7 @@ test("ash_unittests") {
"system/accessibility/switch_access_menu_bubble_controller_unittest.cc", "system/accessibility/switch_access_menu_bubble_controller_unittest.cc",
"system/accessibility/tray_accessibility_unittest.cc", "system/accessibility/tray_accessibility_unittest.cc",
"system/audio/unified_audio_detailed_view_controller_unittest.cc", "system/audio/unified_audio_detailed_view_controller_unittest.cc",
"system/bloom/bloom_tray_unittest.cc",
"system/bluetooth/bluetooth_notification_controller_unittest.cc", "system/bluetooth/bluetooth_notification_controller_unittest.cc",
"system/bluetooth/bluetooth_power_controller_unittest.cc", "system/bluetooth/bluetooth_power_controller_unittest.cc",
"system/bluetooth/tray_bluetooth_helper_legacy_unittest.cc", "system/bluetooth/tray_bluetooth_helper_legacy_unittest.cc",
...@@ -2216,6 +2220,7 @@ test("ash_unittests") { ...@@ -2216,6 +2220,7 @@ test("ash_unittests") {
# TODO(https://crbug.com/644336): Make CrasAudioHandler Chrome or Ash only. # TODO(https://crbug.com/644336): Make CrasAudioHandler Chrome or Ash only.
"//chromeos/audio", "//chromeos/audio",
"//chromeos/components/bloom/public/cpp",
"//chromeos/components/quick_answers:quick_answers", "//chromeos/components/quick_answers:quick_answers",
"//chromeos/constants", "//chromeos/constants",
"//chromeos/dbus:test_support", "//chromeos/dbus:test_support",
......
...@@ -58,6 +58,7 @@ include_rules = [ ...@@ -58,6 +58,7 @@ include_rules = [
"-chromeos", "-chromeos",
"+chromeos/audio", "+chromeos/audio",
"+chromeos/components/multidevice/logging/logging.h", "+chromeos/components/multidevice/logging/logging.h",
"+chromeos/components/bloom/public/cpp",
"+chromeos/components/proximity_auth/public/mojom", "+chromeos/components/proximity_auth/public/mojom",
"+chromeos/components/quick_answers", "+chromeos/components/quick_answers",
"+chromeos/components/security_token_pin", "+chromeos/components/security_token_pin",
......
file://chromeos/assistant/OWNERS
# COMPONENT: UI>Shell>Assistant
// 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 "ash/system/bloom/bloom_tray.h"
#include <memory>
#include "ash/accessibility/accessibility_controller_impl.h"
#include "ash/resources/vector_icons/vector_icons.h"
#include "ash/session/session_controller_impl.h"
#include "ash/shelf/shelf.h"
#include "ash/shell.h"
#include "ash/system/tray/tray_constants.h"
#include "ash/system/tray/tray_container.h"
#include "ash/system/tray/tray_utils.h"
#include "chromeos/components/bloom/public/cpp/bloom_controller.h"
#include "chromeos/components/bloom/public/cpp/bloom_interaction_resolution.h"
#include "ui/gfx/paint_vector_icon.h"
#include "ui/views/controls/image_view.h"
namespace ash {
using chromeos::bloom::BloomController;
using chromeos::bloom::BloomInteractionResolution;
BloomTray::BloomTray(Shelf* shelf)
: TrayBackgroundView(shelf),
icon_(tray_container()->AddChildView(
std::make_unique<views::ImageView>())) {
SetIcon();
SetTooltipText();
}
BloomTray::~BloomTray() = default;
void BloomTray::Initialize() {
TrayBackgroundView::Initialize();
// TODO(jeroendh): Should not be shown on login screen.
SetVisiblePreferred(true);
}
base::string16 BloomTray::GetAccessibleNameForTray() {
// TODO(jeroendh): Use correct name;
return base::ASCIIToUTF16("Bloom (THIS STRING MUST BE LOCALIZED)");
}
void BloomTray::HandleLocaleChange() {
SetTooltipText();
}
void BloomTray::HideBubbleWithView(const TrayBubbleView* bubble_view) {}
void BloomTray::ClickedOutsideBubble() {}
bool BloomTray::PerformAction(const ui::Event& event) {
// TODO(jeroendh): RecordUserClickOnTray
auto* bloom_controller = BloomController::Get();
if (!bloom_controller->HasInteraction()) {
SetIsActive(true);
bloom_controller->StartInteraction();
VLOG(1) << "Starting Bloom interaction";
} else {
SetIsActive(false);
bloom_controller->StopInteraction(BloomInteractionResolution::kNormal);
VLOG(1) << "Stopping Bloom interaction";
}
return true;
}
void BloomTray::SetIcon() {
// TODO(jeroendh): Use correct icon;
gfx::ImageSkia image = gfx::CreateVectorIcon(
kShelfGlobeIcon,
TrayIconColor(Shell::Get()->session_controller()->GetSessionState()));
icon_->SetImage(image);
const int vertical_padding = (kTrayItemSize - image.height()) / 2;
const int horizontal_padding = (kTrayItemSize - image.width()) / 2;
icon_->SetBorder(views::CreateEmptyBorder(
gfx::Insets(vertical_padding, horizontal_padding)));
}
void BloomTray::SetTooltipText() {
// TODO(jeroendh): Use correct tooltip;
icon_->set_tooltip_text(
base::ASCIIToUTF16("Enable Bloom (THIS STRING MUST BE LOCALIZED)"));
}
BEGIN_METADATA(BloomTray, TrayBackgroundView)
END_METADATA
} // namespace ash
// 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 ASH_SYSTEM_BLOOM_BLOOM_TRAY_H_
#define ASH_SYSTEM_BLOOM_BLOOM_TRAY_H_
#include "ash/system/tray/tray_background_view.h"
#include "ui/views/metadata/metadata_header_macros.h"
namespace views {
class ImageView;
}
namespace ash {
class BloomTray : public TrayBackgroundView {
public:
METADATA_HEADER(BloomTray);
explicit BloomTray(Shelf* shelf);
BloomTray(const BloomTray&) = delete;
BloomTray& operator=(const BloomTray&) = delete;
~BloomTray() override;
// TrayBackgroundView:
void Initialize() override;
base::string16 GetAccessibleNameForTray() override;
void HandleLocaleChange() override;
void HideBubbleWithView(const TrayBubbleView* bubble_view) override;
void ClickedOutsideBubble() override;
bool PerformAction(const ui::Event& event) override;
private:
void SetIcon();
void SetTooltipText();
// Weak pointer, will be parented by TrayContainer for its lifetime.
views::ImageView* const icon_;
};
} // namespace ash
#endif // ASH_SYSTEM_BLOOM_BLOOM_TRAY_H_
// 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 "ash/system/bloom/bloom_tray.h"
#include "ash/system/status_area_widget.h"
#include "ash/system/status_area_widget_test_helper.h"
#include "ash/test/ash_test_base.h"
#include "base/bind_helpers.h"
#include "base/command_line.h"
#include "base/notreached.h"
#include "base/test/scoped_feature_list.h"
#include "chromeos/components/bloom/public/cpp/bloom_controller.h"
#include "chromeos/services/assistant/public/cpp/features.h"
#include "ui/gfx/geometry/rect.h"
namespace ash {
namespace {
using chromeos::bloom::BloomController;
using chromeos::bloom::BloomInteractionResolution;
// Fake |BloomController| that will be stored as the singleton
// |BloomController|, and that simply tracks if there is any interaction.
class ScopedBloomController : public BloomController {
public:
// BloomController implementation:
void StartInteraction() override {
EXPECT_FALSE(has_interaction_);
has_interaction_ = true;
}
bool HasInteraction() const override { return has_interaction_; }
void StopInteraction(BloomInteractionResolution resolution) override {
EXPECT_TRUE(has_interaction_);
resolution_ = resolution;
has_interaction_ = false;
}
BloomInteractionResolution GetLastInteractionResolution() const {
return resolution_;
}
void AddObserver(
chromeos::bloom::BloomInteractionObserver* observer) override {
NOTIMPLEMENTED();
}
void AddObserver(std::unique_ptr<chromeos::bloom::BloomInteractionObserver>
observer) override {
NOTIMPLEMENTED();
}
private:
bool has_interaction_ = false;
BloomInteractionResolution resolution_;
};
} // namespace
class BloomTrayTest : public AshTestBase {
protected:
void SetUp() override {
scoped_feature_list_.InitAndEnableFeature(
chromeos::assistant::features::kEnableBloom);
AshTestBase::SetUp();
bloom_tray()->SetVisiblePreferred(true);
}
ScopedBloomController* bloom_controller() { return &bloom_controller_; }
BloomTray* bloom_tray() {
StatusAreaWidget* status =
StatusAreaWidgetTestHelper::GetStatusAreaWidget();
return status->bloom_tray_for_testing();
}
void TapOn(BloomTray* view) {
ASSERT_TRUE(view->GetVisible());
view->PerformAction(
ui::GestureEvent(0, 0, 0, base::TimeTicks(),
ui::GestureEventDetails(ui::ET_GESTURE_TAP)));
}
private:
ScopedBloomController bloom_controller_;
base::test::ScopedFeatureList scoped_feature_list_;
};
TEST_F(BloomTrayTest, ShouldStartBloomInteractionOnTap) {
BloomTray* tray = bloom_tray();
TapOn(tray);
EXPECT_TRUE(tray->is_active());
EXPECT_TRUE(bloom_controller()->HasInteraction());
}
TEST_F(BloomTrayTest, ShouldStopBloomInteractionOnSecondTap) {
BloomTray* tray = bloom_tray();
TapOn(tray);
TapOn(tray);
EXPECT_FALSE(tray->is_active());
EXPECT_FALSE(bloom_controller()->HasInteraction());
EXPECT_EQ(BloomInteractionResolution::kNormal,
bloom_controller()->GetLastInteractionResolution());
}
} // namespace ash
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
#include "ash/shell.h" #include "ash/shell.h"
#include "ash/system/accessibility/dictation_button_tray.h" #include "ash/system/accessibility/dictation_button_tray.h"
#include "ash/system/accessibility/select_to_speak_tray.h" #include "ash/system/accessibility/select_to_speak_tray.h"
#include "ash/system/bloom/bloom_tray.h"
#include "ash/system/holding_space/holding_space_tray.h" #include "ash/system/holding_space/holding_space_tray.h"
#include "ash/system/ime_menu/ime_menu_tray.h" #include "ash/system/ime_menu/ime_menu_tray.h"
#include "ash/system/media/media_tray.h" #include "ash/system/media/media_tray.h"
...@@ -34,6 +35,7 @@ ...@@ -34,6 +35,7 @@
#include "base/i18n/time_formatting.h" #include "base/i18n/time_formatting.h"
#include "base/metrics/histogram_macros.h" #include "base/metrics/histogram_macros.h"
#include "chromeos/constants/chromeos_switches.h" #include "chromeos/constants/chromeos_switches.h"
#include "chromeos/services/assistant/public/cpp/features.h"
#include "media/base/media_switches.h" #include "media/base/media_switches.h"
#include "ui/compositor/scoped_layer_animation_settings.h" #include "ui/compositor/scoped_layer_animation_settings.h"
#include "ui/display/display.h" #include "ui/display/display.h"
...@@ -107,6 +109,11 @@ void StatusAreaWidget::Initialize() { ...@@ -107,6 +109,11 @@ void StatusAreaWidget::Initialize() {
virtual_keyboard_tray_ = std::make_unique<VirtualKeyboardTray>(shelf_); virtual_keyboard_tray_ = std::make_unique<VirtualKeyboardTray>(shelf_);
AddTrayButton(virtual_keyboard_tray_.get()); AddTrayButton(virtual_keyboard_tray_.get());
if (chromeos::assistant::features::IsBloomEnabled()) {
bloom_tray_ = std::make_unique<BloomTray>(shelf_);
AddTrayButton(bloom_tray_.get());
}
if (features::IsCaptureModeEnabled()) { if (features::IsCaptureModeEnabled()) {
stop_recording_button_tray_ = stop_recording_button_tray_ =
std::make_unique<StopRecordingButtonTray>(shelf_); std::make_unique<StopRecordingButtonTray>(shelf_);
......
...@@ -19,21 +19,22 @@ class Window; ...@@ -19,21 +19,22 @@ class Window;
} }
namespace ash { namespace ash {
class BloomTray;
class DictationButtonTray;
class HoldingSpaceTray; class HoldingSpaceTray;
class ImeMenuTray; class ImeMenuTray;
class LogoutButtonTray; class LogoutButtonTray;
class StatusAreaOverflowButtonTray; class MediaTray;
class OverviewButtonTray; class OverviewButtonTray;
class DictationButtonTray;
class PaletteTray; class PaletteTray;
class SelectToSpeakTray; class SelectToSpeakTray;
class Shelf; class Shelf;
class StatusAreaOverflowButtonTray;
class StatusAreaWidgetDelegate; class StatusAreaWidgetDelegate;
class StopRecordingButtonTray; class StopRecordingButtonTray;
class UnifiedSystemTray;
class TrayBackgroundView; class TrayBackgroundView;
class UnifiedSystemTray;
class VirtualKeyboardTray; class VirtualKeyboardTray;
class MediaTray;
// Widget showing the system tray, notification tray, and other tray views in // Widget showing the system tray, notification tray, and other tray views in
// the bottom-right of the screen. Exists separately from ShelfView/ShelfWidget // the bottom-right of the screen. Exists separately from ShelfView/ShelfWidget
...@@ -155,6 +156,8 @@ class ASH_EXPORT StatusAreaWidget : public SessionObserver, ...@@ -155,6 +156,8 @@ class ASH_EXPORT StatusAreaWidget : public SessionObserver,
return virtual_keyboard_tray_.get(); return virtual_keyboard_tray_.get();
} }
BloomTray* bloom_tray_for_testing() { return bloom_tray_.get(); }
CollapseState collapse_state() const { return collapse_state_; } CollapseState collapse_state() const { return collapse_state_; }
void set_collapse_state_for_test(CollapseState state) { void set_collapse_state_for_test(CollapseState state) {
collapse_state_ = state; collapse_state_ = state;
...@@ -219,6 +222,7 @@ class ASH_EXPORT StatusAreaWidget : public SessionObserver, ...@@ -219,6 +222,7 @@ class ASH_EXPORT StatusAreaWidget : public SessionObserver,
std::unique_ptr<PaletteTray> palette_tray_; std::unique_ptr<PaletteTray> palette_tray_;
std::unique_ptr<StopRecordingButtonTray> stop_recording_button_tray_; std::unique_ptr<StopRecordingButtonTray> stop_recording_button_tray_;
std::unique_ptr<VirtualKeyboardTray> virtual_keyboard_tray_; std::unique_ptr<VirtualKeyboardTray> virtual_keyboard_tray_;
std::unique_ptr<BloomTray> bloom_tray_;
std::unique_ptr<ImeMenuTray> ime_menu_tray_; std::unique_ptr<ImeMenuTray> ime_menu_tray_;
std::unique_ptr<SelectToSpeakTray> select_to_speak_tray_; std::unique_ptr<SelectToSpeakTray> select_to_speak_tray_;
std::unique_ptr<HoldingSpaceTray> holding_space_tray_; std::unique_ptr<HoldingSpaceTray> holding_space_tray_;
......
...@@ -38,6 +38,10 @@ void BloomControllerImpl::StartInteraction() { ...@@ -38,6 +38,10 @@ void BloomControllerImpl::StartInteraction() {
observer.OnInteractionStarted(); observer.OnInteractionStarted();
} }
bool BloomControllerImpl::HasInteraction() const {
return current_interaction_ != nullptr;
}
void BloomControllerImpl::ShowUI() { void BloomControllerImpl::ShowUI() {
for (auto& observer : interaction_observers_) for (auto& observer : interaction_observers_)
observer.OnShowUI(); observer.OnShowUI();
......
...@@ -34,6 +34,7 @@ class BloomControllerImpl : public BloomController { ...@@ -34,6 +34,7 @@ class BloomControllerImpl : public BloomController {
// BloomController implementation: // BloomController implementation:
void StartInteraction() override; void StartInteraction() override;
bool HasInteraction() const override;
void StopInteraction(BloomInteractionResolution resolution) override; void StopInteraction(BloomInteractionResolution resolution) override;
void AddObserver(BloomInteractionObserver* observer) override; void AddObserver(BloomInteractionObserver* observer) override;
......
...@@ -29,6 +29,7 @@ class COMPONENT_EXPORT(BLOOM) BloomController { ...@@ -29,6 +29,7 @@ class COMPONENT_EXPORT(BLOOM) BloomController {
// Starts an interaction. This will ask the user for a screenshot, analyze the // Starts an interaction. This will ask the user for a screenshot, analyze the
// content and display the result. // content and display the result.
virtual void StartInteraction() = 0; virtual void StartInteraction() = 0;
virtual bool HasInteraction() const = 0;
virtual void StopInteraction(BloomInteractionResolution resolution) = 0; virtual void StopInteraction(BloomInteractionResolution resolution) = 0;
virtual void AddObserver(BloomInteractionObserver* observer) = 0; virtual void AddObserver(BloomInteractionObserver* observer) = 0;
......
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