| // Copyright 2017 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "ash/system/status_area_widget.h" |
| |
| #include <memory> |
| |
| #include "ash/constants/ash_features.h" |
| #include "ash/constants/ash_switches.h" |
| #include "ash/focus/focus_cycler.h" |
| #include "ash/ime/ime_controller_impl.h" |
| #include "ash/keyboard/ui/keyboard_ui_controller.h" |
| #include "ash/keyboard/ui/keyboard_util.h" |
| #include "ash/keyboard/ui/test/keyboard_test_util.h" |
| #include "ash/public/cpp/keyboard/keyboard_switches.h" |
| #include "ash/public/cpp/locale_update_controller.h" |
| #include "ash/session/session_controller_impl.h" |
| #include "ash/session/test_session_controller_client.h" |
| #include "ash/shelf/drag_handle.h" |
| #include "ash/shelf/shelf_widget.h" |
| #include "ash/shell.h" |
| #include "ash/system/accessibility/dictation_button_tray.h" |
| #include "ash/system/accessibility/select_to_speak/select_to_speak_tray.h" |
| #include "ash/system/eche/eche_tray.h" |
| #include "ash/system/ime_menu/ime_menu_tray.h" |
| #include "ash/system/model/system_tray_model.h" |
| #include "ash/system/model/virtual_keyboard_model.h" |
| #include "ash/system/notification_center/notification_center_tray.h" |
| #include "ash/system/overview/overview_button_tray.h" |
| #include "ash/system/palette/palette_tray.h" |
| #include "ash/system/session/logout_button_tray.h" |
| #include "ash/system/status_area_widget_test_helper.h" |
| #include "ash/system/tray/status_area_overflow_button_tray.h" |
| #include "ash/system/tray/system_tray_notifier.h" |
| #include "ash/system/tray/system_tray_observer.h" |
| #include "ash/system/unified/date_tray.h" |
| #include "ash/system/unified/unified_system_tray.h" |
| #include "ash/system/unified/unified_system_tray_bubble.h" |
| #include "ash/system/virtual_keyboard/virtual_keyboard_tray.h" |
| #include "ash/test/ash_test_base.h" |
| #include "ash/test/test_ash_web_view_factory.h" |
| #include "ash/wm/tablet_mode/tablet_mode_controller_test_api.h" |
| #include "ash/wm/window_pin_util.h" |
| #include "base/command_line.h" |
| #include "base/functional/callback_helpers.h" |
| #include "base/memory/raw_ptr.h" |
| #include "base/test/scoped_feature_list.h" |
| #include "chromeos/ash/components/network/cellular_metrics_logger.h" |
| #include "chromeos/ash/components/network/network_handler.h" |
| #include "chromeos/ash/components/network/network_handler_test_helper.h" |
| #include "components/prefs/testing_pref_service.h" |
| #include "components/proxy_config/pref_proxy_config_tracker_impl.h" |
| #include "components/session_manager/session_manager_types.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| #include "ui/events/event.h" |
| #include "ui/events/event_constants.h" |
| #include "ui/events/keycodes/keyboard_codes_posix.h" |
| #include "ui/events/test/event_generator.h" |
| #include "ui/gfx/image/image.h" |
| |
| using session_manager::SessionState; |
| using testing::NotNull; |
| |
| namespace ash { |
| |
| class StatusAreaWidgetTest : public AshTestBase { |
| protected: |
| TrayBackgroundView::RoundedCornerBehavior GetTrayCornerBehavior( |
| TrayBackgroundView* tray) { |
| return tray->corner_behavior_; |
| } |
| }; |
| |
| // Tests that status area trays are constructed. |
| TEST_F(StatusAreaWidgetTest, Basics) { |
| StatusAreaWidget* status = StatusAreaWidgetTestHelper::GetStatusAreaWidget(); |
| |
| // Status area is visible by default. |
| EXPECT_TRUE(status->IsVisible()); |
| |
| // No bubbles are open at startup. |
| EXPECT_FALSE(status->IsMessageBubbleShown()); |
| |
| // Auto-hidden shelf would not be forced to be visible. |
| EXPECT_FALSE(status->ShouldShowShelf()); |
| |
| // Default trays are constructed. |
| EXPECT_TRUE(status->overview_button_tray()); |
| EXPECT_TRUE(status->unified_system_tray()); |
| EXPECT_TRUE(status->logout_button_tray_for_testing()); |
| EXPECT_TRUE(status->ime_menu_tray()); |
| EXPECT_TRUE(status->virtual_keyboard_tray_for_testing()); |
| EXPECT_TRUE(status->palette_tray()); |
| |
| // Default trays are visible. |
| EXPECT_FALSE(status->overview_button_tray()->GetVisible()); |
| EXPECT_TRUE(status->unified_system_tray()->GetVisible()); |
| EXPECT_FALSE(status->logout_button_tray_for_testing()->GetVisible()); |
| EXPECT_FALSE(status->ime_menu_tray()->GetVisible()); |
| EXPECT_FALSE(status->virtual_keyboard_tray_for_testing()->GetVisible()); |
| } |
| |
| // Tests that the IME menu shows up when adding a secondary display if the IME |
| // menu was active. |
| TEST_F(StatusAreaWidgetTest, MultiDisplayIME) { |
| // Typical flow to enable the IME menu is to rely on InputMethodManager |
| // observers (of which ImeMenuTray is one) getting notified upon activation of |
| // the ime menu. When a new display is added, the IME menu pod should check |
| // whether the menu is already active and set visibility. |
| Shell::Get()->ime_controller()->ShowImeMenuOnShelf(true); |
| |
| // Create a second display, the IME menu pod should be visible. |
| UpdateDisplay("500x400,500x400"); |
| EXPECT_TRUE(StatusAreaWidgetTestHelper::GetSecondaryStatusAreaWidget() |
| ->ime_menu_tray() |
| ->GetVisible()); |
| } |
| |
| // Tests that the IME menu does not show up when adding a secondary display if |
| // the IME menu was not active. |
| TEST_F(StatusAreaWidgetTest, MultiDisplayIMENotActive) { |
| // Create a second display, the IME menu pod should not be visible. |
| UpdateDisplay("500x400,500x400"); |
| EXPECT_FALSE(StatusAreaWidgetTestHelper::GetSecondaryStatusAreaWidget() |
| ->ime_menu_tray() |
| ->GetVisible()); |
| } |
| |
| TEST_F(StatusAreaWidgetTest, HandleOnLocaleChange) { |
| base::i18n::SetRTLForTesting(false); |
| |
| StatusAreaWidget* status_area = |
| StatusAreaWidgetTestHelper::GetStatusAreaWidget(); |
| TrayBackgroundView* ime_menu = status_area->ime_menu_tray(); |
| TrayBackgroundView* palette = status_area->palette_tray(); |
| TrayBackgroundView* dictation_button = status_area->dictation_button_tray(); |
| TrayBackgroundView* select_to_speak = status_area->select_to_speak_tray(); |
| |
| ime_menu->SetVisiblePreferred(true); |
| palette->SetVisiblePreferred(true); |
| dictation_button->SetVisiblePreferred(true); |
| select_to_speak->SetVisiblePreferred(true); |
| |
| // From left to right: `dictation_button`, `select_to_speak`, `ime_menu`, |
| // palette. |
| EXPECT_GT(palette->layer()->bounds().x(), ime_menu->layer()->bounds().x()); |
| EXPECT_GT(ime_menu->layer()->bounds().x(), |
| select_to_speak->layer()->bounds().x()); |
| EXPECT_GT(select_to_speak->layer()->bounds().x(), |
| dictation_button->layer()->bounds().x()); |
| |
| // Switch to RTL mode. |
| base::i18n::SetRTLForTesting(true); |
| // Trigger the LocaleChangeObserver, which should cause a layout of the menu. |
| ash::LocaleUpdateController::Get()->OnLocaleChanged(); |
| |
| // From left to right: palette, ime_menu_, select_to_speak, |
| // dictation_button_. |
| EXPECT_LT(palette->layer()->bounds().x(), ime_menu->layer()->bounds().x()); |
| EXPECT_LT(ime_menu->layer()->bounds().x(), |
| select_to_speak->layer()->bounds().x()); |
| EXPECT_LT(select_to_speak->layer()->bounds().x(), |
| dictation_button->layer()->bounds().x()); |
| |
| base::i18n::SetRTLForTesting(false); |
| } |
| |
| TEST_F(StatusAreaWidgetTest, OpenTrayBubble) { |
| Shell::Get()->ime_controller()->ShowImeMenuOnShelf(true); |
| |
| StatusAreaWidget* status_area = GetPrimaryShelf()->GetStatusAreaWidget(); |
| TrayBackgroundView* ime_menu = status_area->ime_menu_tray(); |
| UnifiedSystemTray* system_tray = status_area->unified_system_tray(); |
| |
| // Clicking on the system tray should set the open tray bubble in |
| // `status_area`. |
| LeftClickOn(system_tray); |
| |
| EXPECT_EQ(status_area->open_shelf_pod_bubble(), |
| system_tray->bubble()->GetBubbleView()); |
| |
| // Clicking on the ime menu should set the open tray bubble in |
| // `status_area`. |
| LeftClickOn(ime_menu); |
| |
| EXPECT_EQ(status_area->open_shelf_pod_bubble(), ime_menu->GetBubbleView()); |
| } |
| |
| TEST_F(StatusAreaWidgetTest, OnlyOneOpenTrayBubble) { |
| Shell::Get()->ime_controller()->ShowImeMenuOnShelf(true); |
| |
| StatusAreaWidget* status_area = GetPrimaryShelf()->GetStatusAreaWidget(); |
| TrayBackgroundView* ime_menu = status_area->ime_menu_tray(); |
| UnifiedSystemTray* system_tray = status_area->unified_system_tray(); |
| |
| LeftClickOn(ime_menu); |
| ASSERT_EQ(status_area->open_shelf_pod_bubble(), ime_menu->GetBubbleView()); |
| |
| // Open Quick Settings through the accelerator. |
| Shell::Get()->accelerator_controller()->PerformActionIfEnabled( |
| AcceleratorAction::kToggleSystemTrayBubble, {}); |
| |
| // When there's an open shelf pod bubble and we open another bubble through |
| // shortcuts, the previous bubble should hide for the next one to show. |
| EXPECT_FALSE(ime_menu->GetBubbleView()); |
| ASSERT_TRUE(system_tray->bubble()); |
| |
| EXPECT_EQ(status_area->open_shelf_pod_bubble(), |
| system_tray->bubble()->GetBubbleView()); |
| } |
| |
| // The corner radius of the date tray changes based on the visibility of the |
| // `NotificationCenterTray`. The date tray should have rounded corners on the |
| // left if the `NotificationCenterTray` is not visible and no rounded corners |
| // otherwise. |
| TEST_F(StatusAreaWidgetTest, DateTrayRoundedCornerBehavior) { |
| StatusAreaWidget* status_area = |
| StatusAreaWidgetTestHelper::GetStatusAreaWidget(); |
| EXPECT_FALSE(status_area->notification_center_tray()->GetVisible()); |
| EXPECT_EQ(GetTrayCornerBehavior(status_area->date_tray()), |
| TrayBackgroundView::RoundedCornerBehavior::kStartRounded); |
| |
| status_area->notification_center_tray()->SetVisiblePreferred(true); |
| |
| EXPECT_EQ(GetTrayCornerBehavior(status_area->date_tray()), |
| TrayBackgroundView::RoundedCornerBehavior::kNotRounded); |
| |
| status_area->notification_center_tray()->SetVisiblePreferred(false); |
| |
| EXPECT_EQ(GetTrayCornerBehavior(status_area->date_tray()), |
| TrayBackgroundView::RoundedCornerBehavior::kStartRounded); |
| } |
| |
| class LockedFullscreenStatusAreaWidgetTest |
| : public AshTestBase, |
| public testing::WithParamInterface<bool> { |
| protected: |
| bool IsLocked() const { return GetParam(); } |
| }; |
| |
| TEST_P(LockedFullscreenStatusAreaWidgetTest, |
| TrayBubbleVisibilityWithPinnedWindow) { |
| // Create a window for testing purposes. |
| const std::unique_ptr<aura::Window> window = CreateTestWindow(); |
| |
| // Show the unified system tray bubble before pinning the window. |
| auto* const status_area_widget = GetPrimaryShelf()->GetStatusAreaWidget(); |
| ASSERT_THAT(status_area_widget, NotNull()); |
| auto* const unified_system_tray = status_area_widget->unified_system_tray(); |
| ASSERT_THAT(unified_system_tray, NotNull()); |
| unified_system_tray->ShowBubble(); |
| ASSERT_TRUE(unified_system_tray->IsBubbleShown()); |
| |
| // Pin the window and verify tray bubble visibility. |
| PinWindow(window.get(), IsLocked()); |
| EXPECT_EQ(unified_system_tray->IsBubbleShown(), !IsLocked()); |
| } |
| |
| INSTANTIATE_TEST_SUITE_P(LockedFullscreenStatusAreaWidgetTests, |
| LockedFullscreenStatusAreaWidgetTest, |
| testing::Bool()); |
| |
| class SystemTrayFocusTestObserver : public SystemTrayObserver { |
| public: |
| SystemTrayFocusTestObserver() = default; |
| |
| SystemTrayFocusTestObserver(const SystemTrayFocusTestObserver&) = delete; |
| SystemTrayFocusTestObserver& operator=(const SystemTrayFocusTestObserver&) = |
| delete; |
| |
| ~SystemTrayFocusTestObserver() override = default; |
| |
| int focus_out_count() { return focus_out_count_; } |
| int reverse_focus_out_count() { return reverse_focus_out_count_; } |
| |
| protected: |
| // SystemTrayObserver: |
| void OnFocusLeavingSystemTray(bool reverse) override { |
| reverse ? ++reverse_focus_out_count_ : ++focus_out_count_; |
| } |
| |
| private: |
| int focus_out_count_ = 0; |
| int reverse_focus_out_count_ = 0; |
| }; |
| |
| class StatusAreaWidgetFocusTest : public AshTestBase { |
| public: |
| StatusAreaWidgetFocusTest() = default; |
| |
| StatusAreaWidgetFocusTest(const StatusAreaWidgetFocusTest&) = delete; |
| StatusAreaWidgetFocusTest& operator=(const StatusAreaWidgetFocusTest&) = |
| delete; |
| |
| ~StatusAreaWidgetFocusTest() override = default; |
| |
| void GenerateTabEvent(bool reverse) { |
| ui::KeyEvent tab_pressed(ui::EventType::kKeyPressed, ui::VKEY_TAB, |
| reverse ? ui::EF_SHIFT_DOWN : ui::EF_NONE); |
| StatusAreaWidgetTestHelper::GetStatusAreaWidget()->OnKeyEvent(&tab_pressed); |
| } |
| }; |
| |
| class StatusAreaWidgetPaletteTest : public AshTestBase { |
| public: |
| StatusAreaWidgetPaletteTest() = default; |
| ~StatusAreaWidgetPaletteTest() override = default; |
| |
| // testing::Test: |
| void SetUp() override { |
| base::CommandLine* cmd = base::CommandLine::ForCurrentProcess(); |
| cmd->AppendSwitch(switches::kAshForceEnableStylusTools); |
| // It's difficult to write a test that marks the primary display as |
| // internal before the status area is constructed. Just force the palette |
| // for all displays. |
| cmd->AppendSwitch(switches::kAshEnablePaletteOnAllDisplays); |
| AshTestBase::SetUp(); |
| } |
| }; |
| |
| // Tests that the stylus palette tray is constructed. |
| TEST_F(StatusAreaWidgetPaletteTest, Basics) { |
| StatusAreaWidget* status = StatusAreaWidgetTestHelper::GetStatusAreaWidget(); |
| EXPECT_TRUE(status->palette_tray()); |
| |
| // Auto-hidden shelf would not be forced to be visible. |
| EXPECT_FALSE(status->ShouldShowShelf()); |
| } |
| |
| class UnifiedStatusAreaWidgetTest : public AshTestBase { |
| public: |
| UnifiedStatusAreaWidgetTest() = default; |
| |
| UnifiedStatusAreaWidgetTest(const UnifiedStatusAreaWidgetTest&) = delete; |
| UnifiedStatusAreaWidgetTest& operator=(const UnifiedStatusAreaWidgetTest&) = |
| delete; |
| |
| ~UnifiedStatusAreaWidgetTest() override = default; |
| |
| // AshTestBase: |
| void SetUp() override { |
| // Initializing NetworkHandler before ash is more like production. |
| AshTestBase::SetUp(); |
| network_handler_test_helper_.RegisterPrefs(profile_prefs_.registry(), |
| local_state()->registry()); |
| PrefProxyConfigTrackerImpl::RegisterPrefs(profile_prefs_.registry()); |
| |
| network_handler_test_helper_.InitializePrefs(&profile_prefs_, |
| local_state()); |
| |
| // Networking stubs may have asynchronous initialization. |
| base::RunLoop().RunUntilIdle(); |
| } |
| |
| void TearDown() override { |
| // This roughly matches production shutdown order. |
| NetworkHandler::Get()->ShutdownPrefServices(); |
| AshTestBase::TearDown(); |
| } |
| |
| private: |
| NetworkHandlerTestHelper network_handler_test_helper_; |
| TestingPrefServiceSimple profile_prefs_; |
| TestingPrefServiceSimple local_state_; |
| }; |
| |
| TEST_F(UnifiedStatusAreaWidgetTest, Basics) { |
| StatusAreaWidget* status = StatusAreaWidgetTestHelper::GetStatusAreaWidget(); |
| EXPECT_TRUE(status->unified_system_tray()); |
| } |
| |
| class StatusAreaWidgetVirtualKeyboardTest : public AshTestBase { |
| protected: |
| void SetUp() override { |
| base::CommandLine::ForCurrentProcess()->AppendSwitch( |
| keyboard::switches::kEnableVirtualKeyboard); |
| AshTestBase::SetUp(); |
| ASSERT_TRUE(keyboard::IsKeyboardEnabled()); |
| keyboard::test::WaitUntilLoaded(); |
| |
| // These tests only apply to the floating virtual keyboard, as it is the |
| // only case where both the virtual keyboard and the shelf are visible. |
| const gfx::Rect keyboard_bounds(0, 0, 1, 1); |
| keyboard_ui_controller()->SetContainerType( |
| keyboard::ContainerType::kFloating, keyboard_bounds, base::DoNothing()); |
| } |
| |
| keyboard::KeyboardUIController* keyboard_ui_controller() { |
| return keyboard::KeyboardUIController::Get(); |
| } |
| }; |
| |
| // See https://crbug.com/897672. |
| TEST_F(StatusAreaWidgetVirtualKeyboardTest, |
| ClickingVirtualKeyboardTrayHidesShownKeyboard) { |
| // Set up the virtual keyboard tray icon along with some other tray icons. |
| StatusAreaWidget* status = StatusAreaWidgetTestHelper::GetStatusAreaWidget(); |
| status->virtual_keyboard_tray_for_testing()->SetVisiblePreferred(true); |
| status->ime_menu_tray()->SetVisiblePreferred(true); |
| |
| keyboard_ui_controller()->ShowKeyboard(false /* locked */); |
| ASSERT_TRUE(keyboard::test::WaitUntilShown()); |
| |
| // The keyboard should hide when clicked. |
| ui::test::EventGenerator* generator = GetEventGenerator(); |
| generator->set_current_screen_location( |
| status->virtual_keyboard_tray_for_testing() |
| ->GetBoundsInScreen() |
| .CenterPoint()); |
| generator->ClickLeftButton(); |
| ASSERT_TRUE(keyboard::test::WaitUntilHidden()); |
| } |
| |
| // See https://crbug.com/897672. |
| TEST_F(StatusAreaWidgetVirtualKeyboardTest, |
| TappingVirtualKeyboardTrayHidesShownKeyboard) { |
| // Set up the virtual keyboard tray icon along with some other tray icons. |
| StatusAreaWidget* status = StatusAreaWidgetTestHelper::GetStatusAreaWidget(); |
| status->virtual_keyboard_tray_for_testing()->SetVisiblePreferred(true); |
| status->ime_menu_tray()->SetVisiblePreferred(true); |
| |
| keyboard_ui_controller()->ShowKeyboard(false /* locked */); |
| ASSERT_TRUE(keyboard::test::WaitUntilShown()); |
| |
| // The keyboard should hide when tapped. |
| ui::test::EventGenerator* generator = GetEventGenerator(); |
| generator->GestureTapAt(status->virtual_keyboard_tray_for_testing() |
| ->GetBoundsInScreen() |
| .CenterPoint()); |
| ASSERT_TRUE(keyboard::test::WaitUntilHidden()); |
| } |
| |
| TEST_F(StatusAreaWidgetVirtualKeyboardTest, ClickingHidesVirtualKeyboard) { |
| keyboard_ui_controller()->ShowKeyboard(false /* locked */); |
| ASSERT_TRUE(keyboard_ui_controller()->IsKeyboardVisible()); |
| |
| ui::test::EventGenerator* generator = GetEventGenerator(); |
| generator->set_current_screen_location( |
| StatusAreaWidgetTestHelper::GetStatusAreaWidget() |
| ->GetWindowBoundsInScreen() |
| .CenterPoint()); |
| generator->ClickLeftButton(); |
| |
| // Times out if test fails. |
| ASSERT_TRUE(keyboard::test::WaitUntilHidden()); |
| } |
| |
| TEST_F(StatusAreaWidgetVirtualKeyboardTest, TappingHidesVirtualKeyboard) { |
| keyboard_ui_controller()->ShowKeyboard(false /* locked */); |
| ASSERT_TRUE(keyboard::test::WaitUntilShown()); |
| |
| ui::test::EventGenerator* generator = GetEventGenerator(); |
| generator->set_current_screen_location( |
| StatusAreaWidgetTestHelper::GetStatusAreaWidget() |
| ->GetWindowBoundsInScreen() |
| .CenterPoint()); |
| generator->PressTouch(); |
| |
| // Times out if test fails. |
| ASSERT_TRUE(keyboard::test::WaitUntilHidden()); |
| } |
| |
| TEST_F(StatusAreaWidgetVirtualKeyboardTest, DoesNotHideLockedVirtualKeyboard) { |
| keyboard_ui_controller()->ShowKeyboard(true /* locked */); |
| ASSERT_TRUE(keyboard::test::WaitUntilShown()); |
| |
| ui::test::EventGenerator* generator = GetEventGenerator(); |
| generator->set_current_screen_location( |
| StatusAreaWidgetTestHelper::GetStatusAreaWidget() |
| ->GetWindowBoundsInScreen() |
| .CenterPoint()); |
| |
| generator->ClickLeftButton(); |
| EXPECT_FALSE(keyboard::test::IsKeyboardHiding()); |
| |
| generator->PressTouch(); |
| EXPECT_FALSE(keyboard::test::IsKeyboardHiding()); |
| } |
| |
| class StatusAreaWidgetCollapseStateTest : public AshTestBase { |
| protected: |
| void SetUp() override { |
| AshTestBase::SetUp(); |
| |
| status_area_ = StatusAreaWidgetTestHelper::GetStatusAreaWidget(); |
| overflow_button_ = status_area_->overflow_button_tray(); |
| virtual_keyboard_ = status_area_->virtual_keyboard_tray_for_testing(); |
| ime_menu_ = status_area_->ime_menu_tray(); |
| palette_ = status_area_->palette_tray(); |
| dictation_button_ = status_area_->dictation_button_tray(); |
| select_to_speak_ = status_area_->select_to_speak_tray(); |
| |
| virtual_keyboard_->SetVisiblePreferred(true); |
| ime_menu_->SetVisiblePreferred(true); |
| palette_->SetVisiblePreferred(true); |
| dictation_button_->SetVisiblePreferred(true); |
| select_to_speak_->SetVisiblePreferred(true); |
| } |
| |
| void SetCollapseState(StatusAreaWidget::CollapseState collapse_state) { |
| status_area_->set_collapse_state_for_test(collapse_state); |
| |
| virtual_keyboard_->UpdateAfterStatusAreaCollapseChange(); |
| ime_menu_->UpdateAfterStatusAreaCollapseChange(); |
| palette_->UpdateAfterStatusAreaCollapseChange(); |
| dictation_button_->UpdateAfterStatusAreaCollapseChange(); |
| select_to_speak_->UpdateAfterStatusAreaCollapseChange(); |
| } |
| |
| StatusAreaWidget::CollapseState collapse_state() const { |
| return status_area_->collapse_state(); |
| } |
| |
| raw_ptr<StatusAreaWidget, DanglingUntriaged> status_area_; |
| raw_ptr<StatusAreaOverflowButtonTray, DanglingUntriaged> overflow_button_; |
| raw_ptr<TrayBackgroundView, DanglingUntriaged> virtual_keyboard_; |
| raw_ptr<TrayBackgroundView, DanglingUntriaged> ime_menu_; |
| raw_ptr<TrayBackgroundView, DanglingUntriaged> palette_; |
| raw_ptr<TrayBackgroundView, DanglingUntriaged> dictation_button_; |
| raw_ptr<TrayBackgroundView, DanglingUntriaged> select_to_speak_; |
| }; |
| |
| TEST_F(StatusAreaWidgetCollapseStateTest, TrayVisibility) { |
| // Initial visibility. |
| ime_menu_->SetVisiblePreferred(false); |
| virtual_keyboard_->set_show_when_collapsed(false); |
| palette_->set_show_when_collapsed(true); |
| EXPECT_FALSE(ime_menu_->GetVisible()); |
| EXPECT_TRUE(virtual_keyboard_->GetVisible()); |
| EXPECT_TRUE(palette_->GetVisible()); |
| |
| // Post-collapse visibility. |
| SetCollapseState(StatusAreaWidget::CollapseState::COLLAPSED); |
| EXPECT_FALSE(ime_menu_->GetVisible()); |
| EXPECT_FALSE(virtual_keyboard_->GetVisible()); |
| EXPECT_TRUE(palette_->GetVisible()); |
| |
| // Expanded visibility. |
| SetCollapseState(StatusAreaWidget::CollapseState::EXPANDED); |
| EXPECT_FALSE(ime_menu_->GetVisible()); |
| EXPECT_TRUE(virtual_keyboard_->GetVisible()); |
| EXPECT_TRUE(palette_->GetVisible()); |
| } |
| |
| TEST_F(StatusAreaWidgetCollapseStateTest, ImeMenuShownWithVirtualKeyboard) { |
| // Set up tray items. |
| ime_menu_->set_show_when_collapsed(false); |
| palette_->set_show_when_collapsed(true); |
| |
| // Collapsing the status area should hide the IME menu tray item. |
| SetCollapseState(StatusAreaWidget::CollapseState::COLLAPSED); |
| EXPECT_FALSE(ime_menu_->GetVisible()); |
| EXPECT_TRUE(palette_->GetVisible()); |
| |
| // But only the IME menu tray item should be shown after showing keyboard, |
| // simulated here by OnArcInputMethodSurfaceBoundsChanged(). |
| Shell::Get() |
| ->system_tray_model() |
| ->virtual_keyboard() |
| ->OnArcInputMethodBoundsChanged(gfx::Rect(0, 0, 100, 100)); |
| EXPECT_TRUE(ime_menu_->GetVisible()); |
| EXPECT_FALSE(palette_->GetVisible()); |
| EXPECT_FALSE(virtual_keyboard_->GetVisible()); |
| EXPECT_FALSE(dictation_button_->GetVisible()); |
| EXPECT_FALSE(select_to_speak_->GetVisible()); |
| } |
| |
| TEST_F(StatusAreaWidgetCollapseStateTest, OverflowButtonShownWhenCollapsible) { |
| EXPECT_FALSE(overflow_button_->GetVisible()); |
| base::CommandLine::ForCurrentProcess()->AppendSwitch( |
| switches::kAshForceStatusAreaCollapsible); |
| status_area_->UpdateCollapseState(); |
| EXPECT_EQ(StatusAreaWidget::CollapseState::COLLAPSED, collapse_state()); |
| EXPECT_TRUE(overflow_button_->GetVisible()); |
| } |
| |
| TEST_F(StatusAreaWidgetCollapseStateTest, ClickOverflowButton) { |
| base::CommandLine::ForCurrentProcess()->AppendSwitch( |
| switches::kAshForceStatusAreaCollapsible); |
| status_area_->UpdateCollapseState(); |
| |
| // By default, status area is collapsed. |
| EXPECT_EQ(StatusAreaWidget::CollapseState::COLLAPSED, collapse_state()); |
| EXPECT_FALSE(select_to_speak_->GetVisible()); |
| EXPECT_FALSE(ime_menu_->GetVisible()); |
| EXPECT_FALSE(virtual_keyboard_->GetVisible()); |
| EXPECT_TRUE(palette_->GetVisible()); |
| EXPECT_TRUE(overflow_button_->GetVisible()); |
| |
| // Click overflow button. |
| LeftClickOn(overflow_button_); |
| |
| // All tray buttons should be visible in the expanded state. |
| EXPECT_EQ(StatusAreaWidget::CollapseState::EXPANDED, collapse_state()); |
| EXPECT_TRUE(select_to_speak_->GetVisible()); |
| EXPECT_TRUE(ime_menu_->GetVisible()); |
| EXPECT_TRUE(virtual_keyboard_->GetVisible()); |
| EXPECT_TRUE(palette_->GetVisible()); |
| EXPECT_TRUE(overflow_button_->GetVisible()); |
| |
| // Clicking the overflow button again should go back to the collapsed state. |
| LeftClickOn(overflow_button_); |
| EXPECT_EQ(StatusAreaWidget::CollapseState::COLLAPSED, collapse_state()); |
| EXPECT_FALSE(select_to_speak_->GetVisible()); |
| EXPECT_FALSE(ime_menu_->GetVisible()); |
| EXPECT_FALSE(virtual_keyboard_->GetVisible()); |
| EXPECT_TRUE(palette_->GetVisible()); |
| EXPECT_TRUE(overflow_button_->GetVisible()); |
| } |
| |
| TEST_F(StatusAreaWidgetCollapseStateTest, NewTrayShownWhileCollapsed) { |
| base::CommandLine::ForCurrentProcess()->AppendSwitch( |
| switches::kAshForceStatusAreaCollapsible); |
| palette_->SetVisiblePreferred(false); |
| status_area_->UpdateCollapseState(); |
| |
| // The palette tray button should not be visible initially. |
| EXPECT_EQ(StatusAreaWidget::CollapseState::COLLAPSED, collapse_state()); |
| EXPECT_FALSE(ime_menu_->GetVisible()); |
| EXPECT_TRUE(virtual_keyboard_->GetVisible()); |
| EXPECT_FALSE(palette_->GetVisible()); |
| |
| // Showing it should replace the virtual keyboard tray button as it has higher |
| // priority. |
| palette_->SetVisiblePreferred(true); |
| EXPECT_EQ(StatusAreaWidget::CollapseState::COLLAPSED, collapse_state()); |
| EXPECT_FALSE(ime_menu_->GetVisible()); |
| EXPECT_FALSE(virtual_keyboard_->GetVisible()); |
| EXPECT_TRUE(palette_->GetVisible()); |
| // We should also check the opacity to make sure the tray isn't visible with |
| // zero opacity; see b/265165818. |
| EXPECT_EQ(palette_->layer()->opacity(), 1); |
| } |
| |
| TEST_F(StatusAreaWidgetCollapseStateTest, TrayHiddenWhileCollapsed) { |
| base::CommandLine::ForCurrentProcess()->AppendSwitch( |
| switches::kAshForceStatusAreaCollapsible); |
| status_area_->UpdateCollapseState(); |
| |
| EXPECT_EQ(StatusAreaWidget::CollapseState::COLLAPSED, collapse_state()); |
| EXPECT_FALSE(ime_menu_->GetVisible()); |
| EXPECT_FALSE(virtual_keyboard_->GetVisible()); |
| |
| // The palette tray button should visible initially. |
| EXPECT_TRUE(palette_->GetVisible()); |
| |
| // Hiding it should make the virtual keyboard tray button replace it. |
| palette_->SetVisiblePreferred(false); |
| EXPECT_EQ(StatusAreaWidget::CollapseState::COLLAPSED, collapse_state()); |
| EXPECT_FALSE(ime_menu_->GetVisible()); |
| EXPECT_TRUE(virtual_keyboard_->GetVisible()); |
| EXPECT_FALSE(palette_->GetVisible()); |
| } |
| |
| TEST_F(StatusAreaWidgetCollapseStateTest, AllTraysFitInCollapsedState) { |
| base::CommandLine::ForCurrentProcess()->AppendSwitch( |
| switches::kAshForceStatusAreaCollapsible); |
| status_area_->UpdateCollapseState(); |
| EXPECT_EQ(StatusAreaWidget::CollapseState::COLLAPSED, collapse_state()); |
| |
| // If all tray buttons can fit in the available space, the overflow button is |
| // not shown. |
| select_to_speak_->SetVisiblePreferred(false); |
| ime_menu_->SetVisiblePreferred(false); |
| dictation_button_->SetVisiblePreferred(false); |
| EXPECT_EQ(StatusAreaWidget::CollapseState::NOT_COLLAPSIBLE, collapse_state()); |
| EXPECT_FALSE(overflow_button_->GetVisible()); |
| } |
| |
| TEST_F(StatusAreaWidgetCollapseStateTest, |
| HideDragHandleOnOverlapInExpandedState) { |
| std::unique_ptr<aura::Window> test_window = |
| CreateTestWindow(gfx::Rect(0, 0, 400, 400)); |
| ash::TabletModeControllerTestApi().EnterTabletMode(); |
| status_area_->UpdateCollapseState(); |
| |
| // By default, status area is collapsed. |
| EXPECT_EQ(StatusAreaWidget::CollapseState::COLLAPSED, collapse_state()); |
| ShelfWidget* const shelf_widget = |
| AshTestBase::GetPrimaryShelf()->shelf_widget(); |
| DragHandle* const drag_handle = shelf_widget->GetDragHandle(); |
| ASSERT_TRUE(drag_handle); |
| EXPECT_TRUE(drag_handle->GetVisible()); |
| |
| // Expand the status area. |
| GetEventGenerator()->GestureTapAt( |
| overflow_button_->GetBoundsInScreen().CenterPoint()); |
| EXPECT_EQ(StatusAreaWidget::CollapseState::EXPANDED, collapse_state()); |
| |
| // Verify that the drag handle was hidden. |
| EXPECT_FALSE(drag_handle->GetVisible()); |
| } |
| |
| TEST_F(StatusAreaWidgetCollapseStateTest, |
| HideDragHandleWithNudgeOnOverlapInExpandedState) { |
| std::unique_ptr<aura::Window> test_window = |
| CreateTestWindow(gfx::Rect(0, 0, 400, 400)); |
| ash::TabletModeControllerTestApi().EnterTabletMode(); |
| status_area_->UpdateCollapseState(); |
| |
| // By default, status area is collapsed. |
| EXPECT_EQ(StatusAreaWidget::CollapseState::COLLAPSED, collapse_state()); |
| |
| ShelfWidget* const shelf_widget = |
| AshTestBase::GetPrimaryShelf()->shelf_widget(); |
| |
| DragHandle* const drag_handle = shelf_widget->GetDragHandle(); |
| ASSERT_TRUE(drag_handle); |
| EXPECT_TRUE(drag_handle->GetVisible()); |
| |
| // Tap on the drag handle to show drag handle nudge. |
| GetEventGenerator()->GestureTapAt( |
| drag_handle->GetBoundsInScreen().CenterPoint()); |
| ASSERT_TRUE(drag_handle->drag_handle_nudge()); |
| base::WeakPtr<views::Widget> drag_handle_widget = |
| drag_handle->drag_handle_nudge()->GetWidget()->GetWeakPtr(); |
| |
| // Expand the status area. |
| GetEventGenerator()->GestureTapAt( |
| overflow_button_->GetBoundsInScreen().CenterPoint()); |
| EXPECT_EQ(StatusAreaWidget::CollapseState::EXPANDED, collapse_state()); |
| |
| // Verify that the drag handle, and drag handle nudge were hidden. |
| EXPECT_FALSE(drag_handle->GetVisible()); |
| EXPECT_FALSE(drag_handle->drag_handle_nudge()); |
| EXPECT_TRUE(!drag_handle_widget || drag_handle_widget->IsClosed()); |
| } |
| |
| class StatusAreaWidgetEcheTest : public AshTestBase { |
| protected: |
| void SetUp() override { |
| scoped_feature_list_.InitWithFeatures( |
| /*enabled_features=*/{features::kEcheSWA}, |
| /*disabled_features=*/{}); |
| DCHECK(test_web_view_factory_.get()); |
| AshTestBase::SetUp(); |
| } |
| |
| private: |
| base::test::ScopedFeatureList scoped_feature_list_; |
| // Calling the factory constructor is enough to set it up. |
| std::unique_ptr<TestAshWebViewFactory> test_web_view_factory_ = |
| std::make_unique<TestAshWebViewFactory>(); |
| }; |
| |
| // Tests that Eche Tray is shown or hidden |
| TEST_F(StatusAreaWidgetEcheTest, EcheTrayShowHide) { |
| StatusAreaWidget* status_area = |
| StatusAreaWidgetTestHelper::GetStatusAreaWidget(); |
| SkBitmap bitmap; |
| bitmap.allocN32Pixels(30, 30); |
| gfx::ImageSkia image_skia = gfx::ImageSkia::CreateFrom1xBitmap(bitmap); |
| image_skia.MakeThreadSafe(); |
| status_area->eche_tray()->LoadBubble( |
| GURL("http://google.com"), gfx::Image(image_skia), u"app 1", |
| u"your phone", |
| eche_app::mojom::ConnectionStatus::kConnectionStatusDisconnected, |
| eche_app::mojom::AppStreamLaunchEntryPoint::APPS_LIST); |
| status_area->eche_tray()->ShowBubble(); |
| |
| // Auto-hidden shelf would be forced to be visible. |
| EXPECT_TRUE(status_area->ShouldShowShelf()); |
| |
| status_area->eche_tray()->HideBubble(); |
| |
| // Auto-hidden shelf would not be forced to be visible. |
| EXPECT_FALSE(status_area->ShouldShowShelf()); |
| } |
| |
| // Tests that `StatusAreaWidget` keep track of its `open_shelf_pod_bubble()` |
| // when eche is showing/hiding its bubble. |
| TEST_F(StatusAreaWidgetEcheTest, StatusAreaOpenTrayBubble) { |
| StatusAreaWidget* status_area = |
| StatusAreaWidgetTestHelper::GetStatusAreaWidget(); |
| auto* eche_tray = status_area->eche_tray(); |
| SkBitmap bitmap; |
| bitmap.allocN32Pixels(30, 30); |
| gfx::ImageSkia image_skia = gfx::ImageSkia::CreateFrom1xBitmap(bitmap); |
| image_skia.MakeThreadSafe(); |
| eche_tray->LoadBubble( |
| GURL("http://google.com"), gfx::Image(image_skia), u"app 1", |
| u"your phone", |
| eche_app::mojom::ConnectionStatus::kConnectionStatusDisconnected, |
| eche_app::mojom::AppStreamLaunchEntryPoint::APPS_LIST); |
| eche_tray->ShowBubble(); |
| |
| EXPECT_EQ(eche_tray->GetBubbleView(), status_area->open_shelf_pod_bubble()); |
| |
| eche_tray->HideBubble(); |
| |
| EXPECT_EQ(nullptr, status_area->open_shelf_pod_bubble()); |
| } |
| |
| } // namespace ash |