// Copyright (c) 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 #include #include #include "ash/app_list/app_list_controller_impl.h" #include "ash/app_list/test/app_list_test_helper.h" #include "ash/app_list/views/app_list_view.h" #include "ash/assistant/assistant_controller_impl.h" #include "ash/constants/ash_features.h" #include "ash/focus_cycler.h" #include "ash/public/cpp/assistant/controller/assistant_ui_controller.h" #include "ash/public/cpp/test/assistant_test_api.h" #include "ash/public/cpp/test/shell_test_api.h" #include "ash/shelf/drag_window_from_shelf_controller_test_api.h" #include "ash/shelf/home_button.h" #include "ash/shelf/hotseat_widget.h" #include "ash/shelf/scrollable_shelf_view.h" #include "ash/shelf/shelf.h" #include "ash/shelf/shelf_app_button.h" #include "ash/shelf/shelf_controller.h" #include "ash/shelf/shelf_focus_cycler.h" #include "ash/shelf/shelf_layout_manager.h" #include "ash/shelf/shelf_metrics.h" #include "ash/shelf/shelf_navigation_widget.h" #include "ash/shelf/shelf_test_util.h" #include "ash/shelf/shelf_view.h" #include "ash/shelf/shelf_view_test_api.h" #include "ash/shelf/test/hotseat_state_watcher.h" #include "ash/shelf/test/shelf_layout_manager_test_base.h" #include "ash/shelf/test/widget_animation_smoothness_inspector.h" #include "ash/shell.h" #include "ash/system/ime_menu/ime_menu_tray.h" #include "ash/system/overview/overview_button_tray.h" #include "ash/system/status_area_widget.h" #include "ash/system/unified/unified_system_tray.h" #include "ash/test/ash_test_base.h" #include "ash/test/layer_animation_verifier.h" #include "ash/wm/overview/overview_controller.h" #include "ash/wm/tablet_mode/tablet_mode_controller_test_api.h" #include "ash/wm/window_state.h" #include "ash/wm/wm_event.h" #include "ash/wm/work_area_insets.h" #include "base/test/icu_test_util.h" #include "base/test/metrics/histogram_tester.h" #include "base/test/scoped_feature_list.h" #include "chromeos/ash/services/assistant/public/cpp/assistant_service.h" #include "testing/gtest/include/gtest/gtest.h" #include "ui/aura/client/aura_constants.h" #include "ui/compositor/presentation_time_recorder.h" #include "ui/compositor/scoped_animation_duration_scale_mode.h" #include "ui/compositor/scoped_layer_animation_settings.h" #include "ui/events/gesture_detection/gesture_configuration.h" #include "ui/events/test/event_generator.h" #include "ui/views/test/widget_animation_waiter.h" #include "ui/wm/core/window_util.h" namespace ash { namespace { ShelfWidget* GetShelfWidget() { return AshTestBase::GetPrimaryShelf()->shelf_widget(); } ShelfLayoutManager* GetShelfLayoutManager() { return AshTestBase::GetPrimaryShelf()->shelf_layout_manager(); } } // namespace class HotseatWidgetTest : public ShelfLayoutManagerTestBase, public testing::WithParamInterface< std::tuple> { public: HotseatWidgetTest() : ShelfLayoutManagerTestBase( base::test::TaskEnvironment::TimeSource::MOCK_TIME), shelf_auto_hide_behavior_(std::get<0>(GetParam())), is_assistant_enabled_(std::get<1>(GetParam())), navigation_buttons_shown_in_tablet_mode_(std::get<2>(GetParam())) { if (is_assistant_enabled_) assistant_test_api_ = AssistantTestApi::Create(); std::vector enabled_features; std::vector disabled_features; if (navigation_buttons_shown_in_tablet_mode_) { disabled_features.push_back(features::kHideShelfControlsInTabletMode); } else { enabled_features.push_back(features::kHideShelfControlsInTabletMode); } enabled_features.push_back(features::kShelfPalmRejectionSwipeOffset); scoped_feature_list_.InitWithFeatures(enabled_features, disabled_features); } // testing::Test: void SetUp() override { ShelfLayoutManagerTestBase::SetUp(); if (is_assistant_enabled_) { assistant_test_api_->SetAssistantEnabled(true); assistant_test_api_->GetAssistantState()->NotifyFeatureAllowed( assistant::AssistantAllowedState::ALLOWED); assistant_test_api_->GetAssistantState()->NotifyStatusChanged( assistant::AssistantStatus::READY); assistant_test_api_->WaitUntilIdle(); } } void TearDown() override { // Some tests may override this value, make sure it's reset. ui::PresentationTimeRecorder::SetReportPresentationTimeImmediatelyForTest( false); ShelfLayoutManagerTestBase::TearDown(); } ShelfAutoHideBehavior shelf_auto_hide_behavior() const { return shelf_auto_hide_behavior_; } bool is_assistant_enabled() const { return is_assistant_enabled_; } bool navigation_buttons_shown_in_tablet_mode() const { return navigation_buttons_shown_in_tablet_mode_; } AssistantTestApi* assistant_test_api() { return assistant_test_api_.get(); } void ShowShelfAndActivateAssistant() { if (shelf_auto_hide_behavior() == ShelfAutoHideBehavior::kAlways) SwipeUpOnShelf(); // If the launcher button is not expected to be shown, show the assistant UI // directly; otherwise, simulate the long press on the home button, if (!navigation_buttons_shown_in_tablet_mode_ && Shell::Get()->tablet_mode_controller()->InTabletMode()) { AssistantUiController::Get()->ShowUi( assistant::AssistantEntryPoint::kLongPressLauncher); return; } views::View* home_button = GetPrimaryShelf()->navigation_widget()->GetHomeButton(); auto center_point = home_button->GetBoundsInScreen().CenterPoint(); GetEventGenerator()->set_current_screen_location(center_point); GetEventGenerator()->PressTouch(); GetAppListTestHelper()->WaitUntilIdle(); // Advance clock to make sure long press gesture is triggered. task_environment()->AdvanceClock(base::Seconds(5)); GetAppListTestHelper()->WaitUntilIdle(); GetEventGenerator()->ReleaseTouch(); GetAppListTestHelper()->WaitUntilIdle(); } void ShowShelfAndGoHome() { // If the launcher button is not expected to be shown, go home directly; // otherwise, simulate tap on the home button, if (!navigation_buttons_shown_in_tablet_mode_ && Shell::Get()->tablet_mode_controller()->InTabletMode()) { Shell::Get()->app_list_controller()->GoHome(GetPrimaryDisplay().id()); return; } // Ensure the shelf, and the home button, are visible. if (shelf_auto_hide_behavior() == ShelfAutoHideBehavior::kAlways) SwipeUpOnShelf(); views::View* home_button = GetPrimaryShelf()->navigation_widget()->GetHomeButton(); GetEventGenerator()->GestureTapAt( home_button->GetBoundsInScreen().CenterPoint()); } void StartOverview() { ASSERT_FALSE(Shell::Get()->overview_controller()->InOverviewSession()); // If the overview button is not expected to be shown, start overview // directly; otherwise, simulate tap on the overview button, which should // toggle overview. if (!navigation_buttons_shown_in_tablet_mode_ && Shell::Get()->tablet_mode_controller()->InTabletMode()) { EnterOverview(); return; } const gfx::Point overview_button_center = GetPrimaryShelf() ->status_area_widget() ->overview_button_tray() ->GetBoundsInScreen() .CenterPoint(); GetEventGenerator()->GestureTapAt(overview_button_center); } void EndOverview() { ASSERT_TRUE(Shell::Get()->overview_controller()->InOverviewSession()); // If the overview button is not expected to be shown, end overview // directly; otherwise, simulate tap on the overview button, which should // toggle overview. if (!navigation_buttons_shown_in_tablet_mode_ && Shell::Get()->tablet_mode_controller()->InTabletMode()) { ExitOverview(); return; } const gfx::Point overview_button_center = GetPrimaryShelf() ->status_area_widget() ->overview_button_tray() ->GetBoundsInScreen() .CenterPoint(); GetEventGenerator()->GestureTapAt(overview_button_center); } private: const ShelfAutoHideBehavior shelf_auto_hide_behavior_; const bool is_assistant_enabled_; const bool navigation_buttons_shown_in_tablet_mode_; std::unique_ptr assistant_test_api_; base::test::ScopedFeatureList scoped_feature_list_; }; // Counts the number of times the work area changes. class DisplayWorkAreaChangeCounter : public display::DisplayObserver { public: DisplayWorkAreaChangeCounter() { Shell::Get()->display_manager()->AddObserver(this); } DisplayWorkAreaChangeCounter(const DisplayWorkAreaChangeCounter&) = delete; DisplayWorkAreaChangeCounter& operator=(const DisplayWorkAreaChangeCounter&) = delete; ~DisplayWorkAreaChangeCounter() override { Shell::Get()->display_manager()->RemoveObserver(this); } void OnDisplayMetricsChanged(const display::Display& display, uint32_t metrics) override { if (metrics & display::DisplayObserver::DISPLAY_METRIC_WORK_AREA) work_area_change_count_++; } int count() const { return work_area_change_count_; } private: int work_area_change_count_ = 0; }; // Watches the shelf for state changes. class ShelfStateWatcher : public ShelfObserver { public: ShelfStateWatcher() { AshTestBase::GetPrimaryShelf()->AddObserver(this); } ~ShelfStateWatcher() override { AshTestBase::GetPrimaryShelf()->RemoveObserver(this); } void WillChangeVisibilityState(ShelfVisibilityState new_state) override { state_change_count_++; } int state_change_count() const { return state_change_count_; } private: int state_change_count_ = 0; }; // Watches the Hotseat transition animation states. class HotseatTransitionAnimationObserver : public HotseatTransitionAnimator::Observer { public: explicit HotseatTransitionAnimationObserver( HotseatTransitionAnimator* hotseat_transition_animator) : hotseat_transition_animator_(hotseat_transition_animator) { hotseat_transition_animator_->AddObserver(this); } ~HotseatTransitionAnimationObserver() override { hotseat_transition_animator_->RemoveObserver(this); } // HotseatTransitionAnimtor::Observer: void OnHotseatTransitionAnimationWillStart(HotseatState from_state, HotseatState to_start) override { ++observer_counts_.started; } void OnHotseatTransitionAnimationEnded(HotseatState from_state, HotseatState to_start) override { ++observer_counts_.ended; if (run_loop_) run_loop_->Quit(); } void OnHotseatTransitionAnimationAborted() override { ++observer_counts_.aborted; } void Wait() { run_loop_ = std::make_unique(); run_loop_->Run(); } void Reset() { if (run_loop_) run_loop_->Quit(); observer_counts_ = {0}; } // Checks that the started and ending/aborting methods have fired the same // amount of times. bool ObserverCountsEqual() const { return observer_counts_.started == (observer_counts_.ended + observer_counts_.aborted); } int AnimationAbortedCalls() const { return observer_counts_.aborted; } private: // Struct which keeps track of the counts of the Observer method has fired. // These are used to verify that started calls = ended calls + aborted calls. struct ObserverCounts { int started; int ended; int aborted; } observer_counts_ = {0}; std::unique_ptr run_loop_; HotseatTransitionAnimator* hotseat_transition_animator_; }; // Used to test the Hotseat, ScrollableShelf, and DenseShelf features. INSTANTIATE_TEST_SUITE_P( All, HotseatWidgetTest, testing::Combine( testing::Values(ShelfAutoHideBehavior::kNever, ShelfAutoHideBehavior::kAlways), /*is_assistant_enabled*/ testing::Bool(), /*navigation_buttons_shown_in_tablet_mode*/ testing::Bool())); TEST_P(HotseatWidgetTest, LongPressHomeWithoutAppWindow) { GetPrimaryShelf()->SetAutoHideBehavior(shelf_auto_hide_behavior()); TabletModeControllerTestApi().EnterTabletMode(); GetAppListTestHelper()->CheckVisibility(true); HotseatStateWatcher watcher(GetShelfLayoutManager()); ShowShelfAndActivateAssistant(); GetAppListTestHelper()->CheckVisibility(true); EXPECT_EQ( is_assistant_enabled(), GetAppListTestHelper()->GetAppListView()->IsShowingEmbeddedAssistantUI()); // Hotseat should not change when showing Assistant. watcher.CheckEqual({}); } TEST_P(HotseatWidgetTest, LongPressHomeWithAppWindow) { GetPrimaryShelf()->SetAutoHideBehavior(shelf_auto_hide_behavior()); TabletModeControllerTestApi().EnterTabletMode(); GetAppListTestHelper()->CheckVisibility(true); std::unique_ptr window = AshTestBase::CreateTestWindow(gfx::Rect(0, 0, 400, 400)); wm::ActivateWindow(window.get()); GetAppListTestHelper()->CheckVisibility(false); HotseatStateWatcher watcher(GetShelfLayoutManager()); ShowShelfAndActivateAssistant(); GetAppListTestHelper()->CheckVisibility(false); EXPECT_EQ( is_assistant_enabled(), GetAppListTestHelper()->GetAppListView()->IsShowingEmbeddedAssistantUI()); std::vector expected_state; if (shelf_auto_hide_behavior() == ShelfAutoHideBehavior::kAlways) { // |ShowShelfAndActivateAssistant()| will bring up shelf so it will trigger // one hotseat state change. expected_state.push_back(HotseatState::kExtended); // Launching the assistant from a shelf button on an autohidden shelf will // hide the shelf at the end of the operation. if (is_assistant_enabled() && navigation_buttons_shown_in_tablet_mode()) expected_state.push_back(HotseatState::kHidden); } watcher.CheckEqual(expected_state); } // Tests that closing a window which was opened prior to entering tablet mode // results in a kShownHomeLauncher hotseat. TEST_P(HotseatWidgetTest, ClosingLastWindowInTabletMode) { GetPrimaryShelf()->SetAutoHideBehavior(shelf_auto_hide_behavior()); std::unique_ptr window = AshTestBase::CreateTestWindow(gfx::Rect(0, 0, 400, 400)); // Activate the window and go to tablet mode. wm::ActivateWindow(window.get()); TabletModeControllerTestApi().EnterTabletMode(); // Close the window, the AppListView should be shown, and the hotseat should // be kShownHomeLauncher. window->Hide(); EXPECT_EQ(HotseatState::kShownHomeLauncher, GetShelfLayoutManager()->hotseat_state()); GetAppListTestHelper()->CheckVisibility(true); } // Tests that the hotseat is kShownHomeLauncher when entering tablet mode with // no windows. TEST_P(HotseatWidgetTest, GoingToTabletModeNoWindows) { GetPrimaryShelf()->SetAutoHideBehavior(shelf_auto_hide_behavior()); TabletModeControllerTestApi().EnterTabletMode(); GetAppListTestHelper()->CheckVisibility(true); EXPECT_EQ(HotseatState::kShownHomeLauncher, GetShelfLayoutManager()->hotseat_state()); } // Tests that the hotseat is kHidden when entering tablet mode with a window. TEST_P(HotseatWidgetTest, GoingToTabletModeWithWindows) { GetPrimaryShelf()->SetAutoHideBehavior(shelf_auto_hide_behavior()); std::unique_ptr window = AshTestBase::CreateTestWindow(gfx::Rect(0, 0, 400, 400)); // Activate the window and go to tablet mode. wm::ActivateWindow(window.get()); TabletModeControllerTestApi().EnterTabletMode(); EXPECT_EQ(HotseatState::kHidden, GetShelfLayoutManager()->hotseat_state()); GetAppListTestHelper()->CheckVisibility(false); } // The in-app Hotseat should not be hidden automatically when the shelf context // menu shows (https://crbug.com/1020388). TEST_P(HotseatWidgetTest, InAppShelfShowingContextMenu) { GetPrimaryShelf()->SetAutoHideBehavior(shelf_auto_hide_behavior()); TabletModeControllerTestApi().EnterTabletMode(); std::unique_ptr window = AshTestBase::CreateTestWindow(gfx::Rect(0, 0, 400, 400)); wm::ActivateWindow(window.get()); EXPECT_FALSE(Shell::Get()->app_list_controller()->IsVisible( display::Screen::GetScreen()->GetPrimaryDisplay().id())); ShelfTestUtil::AddAppShortcut("app_id", TYPE_PINNED_APP); // Swipe up on the shelf to show the hotseat. SwipeUpOnShelf(); EXPECT_EQ(HotseatState::kExtended, GetShelfLayoutManager()->hotseat_state()); ShelfViewTestAPI shelf_view_test_api( GetPrimaryShelf()->shelf_widget()->shelf_view_for_testing()); ShelfAppButton* app_icon = shelf_view_test_api.GetButton(0); // Accelerate the generation of the long press event. ui::GestureConfiguration::GetInstance()->set_show_press_delay_in_ms(1); ui::GestureConfiguration::GetInstance()->set_short_press_time( base::Milliseconds(1)); ui::GestureConfiguration::GetInstance()->set_long_press_time_in_ms(1); // Press the icon enough long time to generate the long press event. GetEventGenerator()->MoveTouch(app_icon->GetBoundsInScreen().CenterPoint()); GetEventGenerator()->PressTouch(); ui::GestureConfiguration* gesture_config = ui::GestureConfiguration::GetInstance(); const int long_press_delay_ms = gesture_config->long_press_time_in_ms() + gesture_config->show_press_delay_in_ms(); base::RunLoop run_loop; base::ThreadTaskRunnerHandle::Get()->PostDelayedTask( FROM_HERE, run_loop.QuitClosure(), base::Milliseconds(long_press_delay_ms)); run_loop.Run(); GetEventGenerator()->ReleaseTouch(); // Expects that the hotseat's state is kExntended. EXPECT_EQ(HotseatState::kExtended, GetShelfLayoutManager()->hotseat_state()); // Ensures that the ink drop state is InkDropState::ACTIVATED before closing // the menu. app_icon->FireRippleActivationTimerForTest(); } // Tests that a window that is created after going to tablet mode, then closed, // results in a kShownHomeLauncher hotseat. TEST_P(HotseatWidgetTest, CloseLastWindowOpenedInTabletMode) { GetPrimaryShelf()->SetAutoHideBehavior(shelf_auto_hide_behavior()); TabletModeControllerTestApi().EnterTabletMode(); std::unique_ptr window = AshTestBase::CreateTestWindow(gfx::Rect(0, 0, 400, 400)); // Activate the window after entering tablet mode. wm::ActivateWindow(window.get()); EXPECT_EQ(HotseatState::kHidden, GetShelfLayoutManager()->hotseat_state()); GetAppListTestHelper()->CheckVisibility(false); // Hide the window, the hotseat should be kShownHomeLauncher, and the home // launcher should be visible. window->Hide(); EXPECT_EQ(HotseatState::kShownHomeLauncher, GetShelfLayoutManager()->hotseat_state()); GetAppListTestHelper()->CheckVisibility(true); } // Verifies removing a shelf item by dragging it off the extended hotseat. TEST_P(HotseatWidgetTest, DragItemOffExtendedHotseat) { TabletModeControllerTestApi().EnterTabletMode(); std::unique_ptr window = AshTestBase::CreateTestWindow(gfx::Rect(0, 0, 400, 400)); wm::ActivateWindow(window.get()); ShelfTestUtil::AddAppShortcut("app_id_1", TYPE_PINNED_APP); ShelfTestUtil::AddAppShortcut("app_id_2", TYPE_PINNED_APP); ShelfView* shelf_view = GetPrimaryShelf() ->hotseat_widget() ->scrollable_shelf_view() ->shelf_view(); EXPECT_EQ(2u, shelf_view->view_model_for_test()->view_size()); // Show the in-app shelf. SwipeUpOnShelf(); EXPECT_EQ(HotseatState::kExtended, GetShelfLayoutManager()->hotseat_state()); // Start mouse drag on a shelf item. ShelfAppButton* dragged_button = ShelfViewTestAPI(shelf_view).GetButton(/*index=*/0); GetEventGenerator()->MoveMouseTo( dragged_button->GetBoundsInScreen().CenterPoint()); GetEventGenerator()->PressLeftButton(); EXPECT_TRUE(dragged_button->FireDragTimerForTest()); EXPECT_TRUE(shelf_view->drag_view()); // Move mouse. Verify that the hotseat is still extended. GetEventGenerator()->MoveMouseBy(/*x=*/0, /*y=*/-80); EXPECT_EQ(HotseatState::kExtended, GetShelfLayoutManager()->hotseat_state()); // Release the mouse press. Verify that: // 1. Shelf item count decreases by one; and // 2. Hotseat is still extended. GetEventGenerator()->ReleaseLeftButton(); EXPECT_EQ(1u, shelf_view->view_model_for_test()->view_size()); EXPECT_EQ(HotseatState::kExtended, GetShelfLayoutManager()->hotseat_state()); } // Tests that swiping up on an autohidden shelf shows the hotseat, and swiping // down hides it. TEST_P(HotseatWidgetTest, ShowingAndHidingAutohiddenShelf) { if (shelf_auto_hide_behavior() != ShelfAutoHideBehavior::kAlways) return; GetPrimaryShelf()->SetAutoHideBehavior(ShelfAutoHideBehavior::kAlways); TabletModeControllerTestApi().EnterTabletMode(); std::unique_ptr window = AshTestBase::CreateTestWindow(gfx::Rect(0, 0, 400, 400)); wm::ActivateWindow(window.get()); SwipeUpOnShelf(); EXPECT_EQ(HotseatState::kExtended, GetShelfLayoutManager()->hotseat_state()); EXPECT_EQ(SHELF_AUTO_HIDE_SHOWN, GetPrimaryShelf()->GetAutoHideState()); SwipeDownOnShelf(); EXPECT_EQ(HotseatState::kHidden, GetShelfLayoutManager()->hotseat_state()); EXPECT_EQ(SHELF_AUTO_HIDE_HIDDEN, GetPrimaryShelf()->GetAutoHideState()); // Swipe down again, nothing should change. SwipeDownOnShelf(); EXPECT_EQ(HotseatState::kHidden, GetShelfLayoutManager()->hotseat_state()); EXPECT_EQ(SHELF_AUTO_HIDE_HIDDEN, GetPrimaryShelf()->GetAutoHideState()); } // Tests that swiping up on several places in the in-app shelf shows the // hotseat (crbug.com/1016931). TEST_P(HotseatWidgetTest, SwipeUpInAppShelfShowsHotseat) { TabletModeControllerTestApi().EnterTabletMode(); std::unique_ptr window = AshTestBase::CreateTestWindow(gfx::Rect(0, 0, 400, 400)); wm::ActivateWindow(window.get()); base::HistogramTester histogram_tester; histogram_tester.ExpectBucketCount(kHotseatGestureHistogramName, InAppShelfGestures::kSwipeDownToHide, 0); histogram_tester.ExpectBucketCount(kHotseatGestureHistogramName, InAppShelfGestures::kSwipeUpToShow, 0); // Swipe up from the center of the shelf. SwipeUpOnShelf(); EXPECT_EQ(HotseatState::kExtended, GetShelfLayoutManager()->hotseat_state()); histogram_tester.ExpectBucketCount(kHotseatGestureHistogramName, InAppShelfGestures::kSwipeDownToHide, 0); histogram_tester.ExpectBucketCount(kHotseatGestureHistogramName, InAppShelfGestures::kSwipeUpToShow, 1); // Swipe down from the hotseat to hide it. gfx::Rect hotseat_bounds = GetPrimaryShelf()->hotseat_widget()->GetWindowBoundsInScreen(); gfx::Point start = hotseat_bounds.top_center(); gfx::Point end = start + gfx::Vector2d(0, 80); const base::TimeDelta kTimeDelta = base::Milliseconds(100); const int kNumScrollSteps = 4; GetEventGenerator()->GestureScrollSequence(start, end, kTimeDelta, kNumScrollSteps); ASSERT_EQ(HotseatState::kHidden, GetShelfLayoutManager()->hotseat_state()); histogram_tester.ExpectBucketCount(kHotseatGestureHistogramName, InAppShelfGestures::kSwipeDownToHide, 1); histogram_tester.ExpectBucketCount(kHotseatGestureHistogramName, InAppShelfGestures::kSwipeUpToShow, 1); // Swipe up from the right part of the shelf (the system tray). start = GetShelfWidget() ->status_area_widget() ->GetWindowBoundsInScreen() .CenterPoint(); end = start + gfx::Vector2d(0, -80); GetEventGenerator()->GestureScrollSequence(start, end, kTimeDelta, kNumScrollSteps); EXPECT_EQ(HotseatState::kExtended, GetShelfLayoutManager()->hotseat_state()); histogram_tester.ExpectBucketCount(kHotseatGestureHistogramName, InAppShelfGestures::kSwipeDownToHide, 1); histogram_tester.ExpectBucketCount(kHotseatGestureHistogramName, InAppShelfGestures::kSwipeUpToShow, 2); // Swipe down from the hotseat to hide it. start = hotseat_bounds.top_center(); end = start + gfx::Vector2d(0, 80); GetEventGenerator()->GestureScrollSequence(start, end, kTimeDelta, kNumScrollSteps); ASSERT_EQ(HotseatState::kHidden, GetShelfLayoutManager()->hotseat_state()); histogram_tester.ExpectBucketCount(kHotseatGestureHistogramName, InAppShelfGestures::kSwipeDownToHide, 2); histogram_tester.ExpectBucketCount(kHotseatGestureHistogramName, InAppShelfGestures::kSwipeUpToShow, 2); // Swipe up from the left part of the shelf (the home/back button). start = GetShelfWidget() ->navigation_widget() ->GetWindowBoundsInScreen() .CenterPoint(); end = start + gfx::Vector2d(0, -80); GetEventGenerator()->GestureScrollSequence(start, end, kTimeDelta, kNumScrollSteps); EXPECT_EQ(HotseatState::kExtended, GetShelfLayoutManager()->hotseat_state()); histogram_tester.ExpectBucketCount(kHotseatGestureHistogramName, InAppShelfGestures::kSwipeDownToHide, 2); histogram_tester.ExpectBucketCount(kHotseatGestureHistogramName, InAppShelfGestures::kSwipeUpToShow, 3); } // Tests that swiping up on the hotseat does nothing. TEST_P(HotseatWidgetTest, SwipeUpOnHotseatBackgroundDoesNothing) { GetPrimaryShelf()->SetAutoHideBehavior(shelf_auto_hide_behavior()); TabletModeControllerTestApi().EnterTabletMode(); std::unique_ptr window = AshTestBase::CreateTestWindow(gfx::Rect(0, 0, 400, 400)); wm::ActivateWindow(window.get()); base::HistogramTester histogram_tester; histogram_tester.ExpectBucketCount(kHotseatGestureHistogramName, InAppShelfGestures::kSwipeDownToHide, 0); histogram_tester.ExpectBucketCount(kHotseatGestureHistogramName, InAppShelfGestures::kSwipeUpToShow, 0); // Swipe up on the shelf to show the hotseat. EXPECT_FALSE(Shell::Get()->app_list_controller()->IsVisible( display::Screen::GetScreen()->GetPrimaryDisplay().id())); SwipeUpOnShelf(); EXPECT_EQ(HotseatState::kExtended, GetShelfLayoutManager()->hotseat_state()); histogram_tester.ExpectBucketCount(kHotseatGestureHistogramName, InAppShelfGestures::kSwipeDownToHide, 0); histogram_tester.ExpectBucketCount(kHotseatGestureHistogramName, InAppShelfGestures::kSwipeUpToShow, 1); if (shelf_auto_hide_behavior() == ShelfAutoHideBehavior::kAlways) EXPECT_EQ(SHELF_AUTO_HIDE_SHOWN, GetPrimaryShelf()->GetAutoHideState()); // Swipe up on the Hotseat (parent of ShelfView) does nothing. gfx::Point start(GetPrimaryShelf() ->shelf_widget() ->hotseat_widget() ->GetWindowBoundsInScreen() .top_center()); const gfx::Point end(start + gfx::Vector2d(0, -300)); const base::TimeDelta kTimeDelta = base::Milliseconds(100); const int kNumScrollSteps = 4; GetEventGenerator()->GestureScrollSequence(start, end, kTimeDelta, kNumScrollSteps); EXPECT_FALSE(Shell::Get()->app_list_controller()->IsVisible( display::Screen::GetScreen()->GetPrimaryDisplay().id())); EXPECT_EQ(HotseatState::kExtended, GetShelfLayoutManager()->hotseat_state()); if (shelf_auto_hide_behavior() == ShelfAutoHideBehavior::kAlways) EXPECT_EQ(SHELF_AUTO_HIDE_SHOWN, GetPrimaryShelf()->GetAutoHideState()); histogram_tester.ExpectBucketCount(kHotseatGestureHistogramName, InAppShelfGestures::kSwipeDownToHide, 0); histogram_tester.ExpectBucketCount(kHotseatGestureHistogramName, InAppShelfGestures::kSwipeUpToShow, 1); } // Tests that tapping an active window with an extended hotseat results in a // hidden hotseat. TEST_P(HotseatWidgetTest, TappingActiveWindowHidesHotseat) { GetPrimaryShelf()->SetAutoHideBehavior(shelf_auto_hide_behavior()); TabletModeControllerTestApi().EnterTabletMode(); std::unique_ptr window = AshTestBase::CreateTestWindow(gfx::Rect(0, 0, 400, 400)); wm::ActivateWindow(window.get()); base::HistogramTester histogram_tester; histogram_tester.ExpectBucketCount(kHotseatGestureHistogramName, InAppShelfGestures::kSwipeDownToHide, 0); histogram_tester.ExpectBucketCount(kHotseatGestureHistogramName, InAppShelfGestures::kSwipeUpToShow, 0); histogram_tester.ExpectBucketCount( kHotseatGestureHistogramName, InAppShelfGestures::kHotseatHiddenDueToInteractionOutsideOfShelf, 0); // Swipe up on the shelf to show the hotseat. SwipeUpOnShelf(); histogram_tester.ExpectBucketCount(kHotseatGestureHistogramName, InAppShelfGestures::kSwipeDownToHide, 0); histogram_tester.ExpectBucketCount(kHotseatGestureHistogramName, InAppShelfGestures::kSwipeUpToShow, 1); histogram_tester.ExpectBucketCount( kHotseatGestureHistogramName, InAppShelfGestures::kHotseatHiddenDueToInteractionOutsideOfShelf, 0); // Tap the shelf background, nothing should happen. gfx::Rect display_bounds = display::Screen::GetScreen()->GetPrimaryDisplay().bounds(); gfx::Point tap_point = display_bounds.bottom_center(); GetEventGenerator()->GestureTapAt(tap_point); EXPECT_EQ(HotseatState::kExtended, GetShelfLayoutManager()->hotseat_state()); if (shelf_auto_hide_behavior() == ShelfAutoHideBehavior::kAlways) EXPECT_EQ(SHELF_AUTO_HIDE_SHOWN, GetPrimaryShelf()->GetAutoHideState()); // Tap the active window, the hotseat should hide. tap_point.Offset(0, -200); GetEventGenerator()->GestureTapAt(tap_point); EXPECT_EQ(HotseatState::kHidden, GetShelfLayoutManager()->hotseat_state()); if (shelf_auto_hide_behavior() == ShelfAutoHideBehavior::kAlways) EXPECT_EQ(SHELF_AUTO_HIDE_HIDDEN, GetPrimaryShelf()->GetAutoHideState()); histogram_tester.ExpectBucketCount(kHotseatGestureHistogramName, InAppShelfGestures::kSwipeDownToHide, 0); histogram_tester.ExpectBucketCount(kHotseatGestureHistogramName, InAppShelfGestures::kSwipeUpToShow, 1); histogram_tester.ExpectBucketCount( kHotseatGestureHistogramName, InAppShelfGestures::kHotseatHiddenDueToInteractionOutsideOfShelf, 1); } // Tests that gesture dragging an active window hides the hotseat. TEST_P(HotseatWidgetTest, GestureDraggingActiveWindowHidesHotseat) { GetPrimaryShelf()->SetAutoHideBehavior(shelf_auto_hide_behavior()); TabletModeControllerTestApi().EnterTabletMode(); std::unique_ptr window = AshTestBase::CreateTestWindow(gfx::Rect(0, 0, 400, 400)); wm::ActivateWindow(window.get()); base::HistogramTester histogram_tester; histogram_tester.ExpectBucketCount(kHotseatGestureHistogramName, InAppShelfGestures::kSwipeDownToHide, 0); histogram_tester.ExpectBucketCount(kHotseatGestureHistogramName, InAppShelfGestures::kSwipeUpToShow, 0); // Swipe up on the shelf to show the hotseat. SwipeUpOnShelf(); histogram_tester.ExpectBucketCount(kHotseatGestureHistogramName, InAppShelfGestures::kSwipeDownToHide, 0); histogram_tester.ExpectBucketCount(kHotseatGestureHistogramName, InAppShelfGestures::kSwipeUpToShow, 1); if (shelf_auto_hide_behavior() == ShelfAutoHideBehavior::kAlways) EXPECT_EQ(SHELF_AUTO_HIDE_SHOWN, GetPrimaryShelf()->GetAutoHideState()); // Gesture drag on the active window, the hotseat should hide. gfx::Rect display_bounds = display::Screen::GetScreen()->GetPrimaryDisplay().bounds(); gfx::Point start = display_bounds.bottom_center(); start.Offset(0, -200); gfx::Point end = start; end.Offset(0, -200); GetEventGenerator()->GestureScrollSequence(start, end, base::Milliseconds(10), 4); EXPECT_EQ(HotseatState::kHidden, GetShelfLayoutManager()->hotseat_state()); if (shelf_auto_hide_behavior() == ShelfAutoHideBehavior::kAlways) EXPECT_EQ(SHELF_AUTO_HIDE_HIDDEN, GetPrimaryShelf()->GetAutoHideState()); histogram_tester.ExpectBucketCount(kHotseatGestureHistogramName, InAppShelfGestures::kSwipeDownToHide, 0); histogram_tester.ExpectBucketCount(kHotseatGestureHistogramName, InAppShelfGestures::kSwipeUpToShow, 1); } // Tests that a swipe up on the shelf shows the hotseat while in split view. TEST_P(HotseatWidgetTest, SwipeUpOnShelfShowsHotseatInSplitView) { TabletModeControllerTestApi().EnterTabletMode(); std::unique_ptr window = AshTestBase::CreateTestWindow(gfx::Rect(0, 0, 400, 400)); wm::ActivateWindow(window.get()); std::unique_ptr window2 = AshTestBase::CreateTestWindow(gfx::Rect(0, 0, 400, 400)); base::HistogramTester histogram_tester; histogram_tester.ExpectBucketCount(kHotseatGestureHistogramName, InAppShelfGestures::kSwipeDownToHide, 0); histogram_tester.ExpectBucketCount(kHotseatGestureHistogramName, InAppShelfGestures::kSwipeUpToShow, 0); // Go into split view mode by first going into overview, and then snapping // the open window on one side. EnterOverview(); SplitViewController* split_view_controller = SplitViewController::Get(Shell::GetPrimaryRootWindow()); split_view_controller->SnapWindow(window.get(), SplitViewController::LEFT); split_view_controller->SnapWindow(window2.get(), SplitViewController::RIGHT); EXPECT_TRUE(split_view_controller->BothSnapped()); // We should still be able to drag up the hotseat. SwipeUpOnShelf(); EXPECT_EQ(HotseatState::kExtended, GetShelfLayoutManager()->hotseat_state()); histogram_tester.ExpectBucketCount(kHotseatGestureHistogramName, InAppShelfGestures::kSwipeDownToHide, 0); histogram_tester.ExpectBucketCount(kHotseatGestureHistogramName, InAppShelfGestures::kSwipeUpToShow, 1); } // Tests that HotseatTransitionAimationObserver starting and ending calls have a // 1:1 relation. This test verifies that behavior. TEST_P(HotseatWidgetTest, ObserverCallsMatch) { ui::ScopedAnimationDurationScaleMode non_zero( ui::ScopedAnimationDurationScaleMode::NON_ZERO_DURATION); // Enter tablet mode to show the home launcher. Hotseat state should be // kShownHomeLauncher. TabletModeControllerTestApi().EnterTabletMode(); ASSERT_EQ(HotseatState::kShownHomeLauncher, GetShelfLayoutManager()->hotseat_state()); // Create a window to transition to the in-app shelf. Hotseat state should be // kHidden. HotseatTransitionAnimationObserver observer( GetPrimaryShelf()->shelf_widget()->hotseat_transition_animator()); std::unique_ptr window = AshTestBase::CreateTestWindow(gfx::Rect(0, 0, 800, 800)); observer.Wait(); EXPECT_TRUE(observer.ObserverCountsEqual()); ASSERT_EQ(HotseatState::kHidden, GetShelfLayoutManager()->hotseat_state()); observer.Reset(); // Go to home launcher again. Hotseat state should be kShownHomeLauncher. ShowShelfAndGoHome(); observer.Wait(); EXPECT_TRUE(observer.ObserverCountsEqual()); ASSERT_EQ(HotseatState::kShownHomeLauncher, GetShelfLayoutManager()->hotseat_state()); observer.Reset(); // Go to overview and cancel immediately. Hotseat state should be // kShownHomeLauncher. StartOverview(); EXPECT_TRUE(Shell::Get()->overview_controller()->IsInStartAnimation()); // No animations should have been started so no animations are in progress // or aborted. EXPECT_TRUE(observer.ObserverCountsEqual()); EXPECT_EQ(0, observer.AnimationAbortedCalls()); EndOverview(); // No animations should have been started or aborted. EXPECT_EQ(0, observer.AnimationAbortedCalls()); EXPECT_TRUE(observer.ObserverCountsEqual()); ASSERT_EQ(HotseatState::kShownHomeLauncher, GetShelfLayoutManager()->hotseat_state()); observer.Reset(); // Go to overview. Hotseat state should be kExtended. StartOverview(); ASSERT_EQ(HotseatState::kShownHomeLauncher, GetShelfLayoutManager()->hotseat_state()); EXPECT_TRUE(observer.ObserverCountsEqual()); } // Tests that a swipe up on the shelf shows the hotseat while in split view. TEST_P(HotseatWidgetTest, DisableBlurDuringOverviewMode) { TabletModeControllerTestApi().EnterTabletMode(); ASSERT_EQ( ShelfConfig::Get()->shelf_blur_radius(), GetShelfWidget()->hotseat_widget()->GetHotseatBackgroundBlurForTest()); // Go into overview and check that at the end of the animation, background // blur is disabled. StartOverview(); WaitForOverviewAnimation(/*enter=*/true); EXPECT_EQ( 0, GetShelfWidget()->hotseat_widget()->GetHotseatBackgroundBlurForTest()); // Exit overview and check that at the end of the animation, background // blur is enabled again. EndOverview(); WaitForOverviewAnimation(/*enter=*/false); EXPECT_EQ( ShelfConfig::Get()->shelf_blur_radius(), GetShelfWidget()->hotseat_widget()->GetHotseatBackgroundBlurForTest()); } // Tests that releasing the hotseat gesture below the threshold results in a // kHidden hotseat when the shelf is shown. TEST_P(HotseatWidgetTest, ReleasingSlowDragBelowThreshold) { GetPrimaryShelf()->SetAutoHideBehavior(ShelfAutoHideBehavior::kNever); TabletModeControllerTestApi().EnterTabletMode(); std::unique_ptr window = AshTestBase::CreateTestWindow(gfx::Rect(0, 0, 400, 400)); wm::ActivateWindow(window.get()); base::HistogramTester histogram_tester; histogram_tester.ExpectBucketCount(kHotseatGestureHistogramName, InAppShelfGestures::kSwipeDownToHide, 0); histogram_tester.ExpectBucketCount(kHotseatGestureHistogramName, InAppShelfGestures::kSwipeUpToShow, 0); gfx::Rect display_bounds = display::Screen::GetScreen()->GetPrimaryDisplay().bounds(); const gfx::Point start(display_bounds.bottom_center()); const int hotseat_size = GetPrimaryShelf() ->shelf_widget() ->hotseat_widget() ->GetWindowBoundsInScreen() .height(); const gfx::Point end(start + gfx::Vector2d(0, -hotseat_size / 2 + 1)); const base::TimeDelta kTimeDelta = base::Milliseconds(1000); const int kNumScrollSteps = 4; GetEventGenerator()->GestureScrollSequence(start, end, kTimeDelta, kNumScrollSteps); EXPECT_EQ(HotseatState::kHidden, GetShelfLayoutManager()->hotseat_state()); histogram_tester.ExpectBucketCount(kHotseatGestureHistogramName, InAppShelfGestures::kSwipeDownToHide, 0); histogram_tester.ExpectBucketCount(kHotseatGestureHistogramName, InAppShelfGestures::kSwipeUpToShow, 0); } // Tests that releasing the hotseat gesture above the threshold results in a // kExtended hotseat. TEST_P(HotseatWidgetTest, ReleasingSlowDragAboveThreshold) { GetPrimaryShelf()->SetAutoHideBehavior(shelf_auto_hide_behavior()); TabletModeControllerTestApi().EnterTabletMode(); std::unique_ptr window = AshTestBase::CreateTestWindow(gfx::Rect(0, 0, 400, 400)); wm::ActivateWindow(window.get()); base::HistogramTester histogram_tester; histogram_tester.ExpectBucketCount(kHotseatGestureHistogramName, InAppShelfGestures::kSwipeDownToHide, 0); histogram_tester.ExpectBucketCount(kHotseatGestureHistogramName, InAppShelfGestures::kSwipeUpToShow, 0); gfx::Rect display_bounds = display::Screen::GetScreen()->GetPrimaryDisplay().bounds(); const gfx::Point start(display_bounds.bottom_center()); const int hotseat_size = GetPrimaryShelf() ->shelf_widget() ->hotseat_widget() ->GetWindowBoundsInScreen() .height(); const gfx::Point end(start + gfx::Vector2d(0, -hotseat_size * 3.0f / 2.0f)); const base::TimeDelta kTimeDelta = base::Milliseconds(1000); const int kNumScrollSteps = 4; GetEventGenerator()->GestureScrollSequence(start, end, kTimeDelta, kNumScrollSteps); EXPECT_EQ(HotseatState::kExtended, GetShelfLayoutManager()->hotseat_state()); if (shelf_auto_hide_behavior() == ShelfAutoHideBehavior::kAlways) EXPECT_EQ(SHELF_AUTO_HIDE_SHOWN, GetPrimaryShelf()->GetAutoHideState()); histogram_tester.ExpectBucketCount(kHotseatGestureHistogramName, InAppShelfGestures::kSwipeDownToHide, 0); histogram_tester.ExpectBucketCount(kHotseatGestureHistogramName, InAppShelfGestures::kSwipeUpToShow, 1); } // Tests that releasing the hotseat gesture when a stylus app is active has a // bigger thresehold than normal apps. TEST_P(HotseatWidgetTest, HotseatDragGestureForStylusApp) { GetPrimaryShelf()->SetAutoHideBehavior(shelf_auto_hide_behavior()); TabletModeControllerTestApi().EnterTabletMode(); // Taken from ShelfLayoutManager. const int kShelfPalmRejectionSwipeOffset = 80; const std::string stylus_app = "fhapgmpiiiigioilnjmkiohjhlegnceb"; ShelfModel* model = Shell::Get()->shelf_controller()->model(); const ShelfID test_stylus_app_id(stylus_app); std::unique_ptr window = AshTestBase::CreateTestWindow(gfx::Rect(0, 0, 400, 400)); window->SetProperty(kShelfIDKey, test_stylus_app_id.Serialize()); wm::ActivateWindow(window.get()); EXPECT_EQ(test_stylus_app_id, model->active_shelf_id()); base::HistogramTester histogram_tester; histogram_tester.ExpectBucketCount(kHotseatGestureHistogramName, InAppShelfGestures::kSwipeDownToHide, 0); histogram_tester.ExpectBucketCount(kHotseatGestureHistogramName, InAppShelfGestures::kSwipeUpToShow, 0); gfx::Rect display_bounds = display::Screen::GetScreen()->GetPrimaryDisplay().bounds(); const gfx::Point start(display_bounds.bottom_center()); const int hotseat_size = GetPrimaryShelf() ->shelf_widget() ->hotseat_widget() ->GetWindowBoundsInScreen() .height(); const gfx::Point normal_thereshold( start + gfx::Vector2d(0, -hotseat_size * 3.0f / 2.0f)); const base::TimeDelta kTimeDelta = base::Milliseconds(1000); const int kNumScrollSteps = 4; GetEventGenerator()->GestureScrollSequence(start, normal_thereshold, kTimeDelta, kNumScrollSteps); EXPECT_EQ(HotseatState::kHidden, GetShelfLayoutManager()->hotseat_state()); const gfx::Point offset_thereshold( normal_thereshold + gfx::Vector2d(0, -kShelfPalmRejectionSwipeOffset)); GetEventGenerator()->GestureScrollSequence(start, offset_thereshold, kTimeDelta, kNumScrollSteps); EXPECT_EQ(HotseatState::kExtended, GetShelfLayoutManager()->hotseat_state()); if (shelf_auto_hide_behavior() == ShelfAutoHideBehavior::kAlways) EXPECT_EQ(SHELF_AUTO_HIDE_SHOWN, GetPrimaryShelf()->GetAutoHideState()); histogram_tester.ExpectBucketCount(kHotseatGestureHistogramName, InAppShelfGestures::kSwipeDownToHide, 0); histogram_tester.ExpectBucketCount(kHotseatGestureHistogramName, InAppShelfGestures::kSwipeUpToShow, 1); } // Tests that showing overview after showing the hotseat results in only one // animation, to |kExtended|. TEST_P(HotseatWidgetTest, ShowingOverviewFromShownAnimatesOnce) { GetPrimaryShelf()->SetAutoHideBehavior(shelf_auto_hide_behavior()); TabletModeControllerTestApi().EnterTabletMode(); std::unique_ptr window = AshTestBase::CreateTestWindow(gfx::Rect(0, 0, 400, 400)); wm::ActivateWindow(window.get()); std::unique_ptr state_watcher_ = std::make_unique(GetShelfLayoutManager()); SwipeUpOnShelf(); ASSERT_EQ(HotseatState::kExtended, GetShelfLayoutManager()->hotseat_state()); StartOverview(); state_watcher_->CheckEqual({HotseatState::kExtended}); } // Tests that the hotseat is not flush with the bottom of the screen when home // launcher is showing. TEST_P(HotseatWidgetTest, HotseatNotFlushWhenHomeLauncherShowing) { GetPrimaryShelf()->SetAutoHideBehavior(shelf_auto_hide_behavior()); TabletModeControllerTestApi().EnterTabletMode(); const int display_height = display::Screen::GetScreen()->GetPrimaryDisplay().bounds().height(); const int hotseat_bottom = GetPrimaryShelf() ->shelf_widget() ->hotseat_widget() ->GetWindowBoundsInScreen() .bottom(); EXPECT_LT(hotseat_bottom, display_height); } // Tests that home -> overview results in only one hotseat state change. TEST_P(HotseatWidgetTest, HomeToOverviewChangesStateOnce) { GetPrimaryShelf()->SetAutoHideBehavior(shelf_auto_hide_behavior()); TabletModeControllerTestApi().EnterTabletMode(); // First, try with no windows open. { HotseatStateWatcher watcher(GetShelfLayoutManager()); StartOverview(); WaitForOverviewAnimation(/*enter=*/true); watcher.CheckEqual({/* shelf state should not change*/}); } // Open a window, then open the home launcher. std::unique_ptr window = AshTestBase::CreateTestWindow(gfx::Rect(0, 0, 400, 400)); wm::ActivateWindow(window.get()); ShowShelfAndGoHome(); GetAppListTestHelper()->CheckVisibility(true); // Activate overview and expect the hotseat only changes state to extended. { HotseatStateWatcher watcher(GetShelfLayoutManager()); StartOverview(); WaitForOverviewAnimation(/*enter=*/true); watcher.CheckEqual({/* shelf state should not change*/}); } } // Verifies that the hotseat widget and the status area widget are animated to // the target location when entering overview mode in home launcher // (https://crbug.com/1079347). TEST_P(HotseatWidgetTest, VerifyShelfAnimationWhenEnteringOverview) { GetPrimaryShelf()->SetAutoHideBehavior(shelf_auto_hide_behavior()); TabletModeControllerTestApi().EnterTabletMode(); ui::ScopedAnimationDurationScaleMode non_zero_duration_mode( ui::ScopedAnimationDurationScaleMode::NON_ZERO_DURATION); HotseatWidget* hotseat_widget = GetPrimaryShelf()->hotseat_widget(); ASSERT_EQ(HotseatState::kShownHomeLauncher, hotseat_widget->state()); ui::LayerAnimator* hotseat_layer_animator = hotseat_widget->GetNativeView()->layer()->GetAnimator(); ui::LayerAnimator* status_area_layer_animator = GetShelfWidget() ->status_area_widget() ->GetNativeView() ->layer() ->GetAnimator(); ASSERT_FALSE(hotseat_layer_animator->is_animating()); ASSERT_FALSE(status_area_layer_animator->is_animating()); StartOverview(); EXPECT_FALSE(hotseat_layer_animator->is_animating()); EXPECT_FALSE(status_area_layer_animator->is_animating()); ASSERT_EQ(HotseatState::kShownHomeLauncher, hotseat_widget->state()); } // Tests that home -> in-app results in only one state change. TEST_P(HotseatWidgetTest, HomeToInAppChangesStateOnce) { GetPrimaryShelf()->SetAutoHideBehavior(shelf_auto_hide_behavior()); TabletModeControllerTestApi().EnterTabletMode(); // Go to in-app, the hotseat should hide. HotseatStateWatcher watcher(GetShelfLayoutManager()); std::unique_ptr window = AshTestBase::CreateTestWindow(gfx::Rect(0, 0, 400, 400)); wm::ActivateWindow(window.get()); watcher.CheckEqual({HotseatState::kHidden}); } // Tests that in-app -> home via closing the only window, swiping from the // bottom of the shelf, and tapping the home launcher button results in only one // state change. TEST_P(HotseatWidgetTest, InAppToHomeChangesStateOnce) { GetPrimaryShelf()->SetAutoHideBehavior(shelf_auto_hide_behavior()); TabletModeControllerTestApi().EnterTabletMode(); // Go to in-app with an extended hotseat. std::unique_ptr window = AshTestBase::CreateTestWindow(gfx::Rect(0, 0, 400, 400)); wm::ActivateWindow(window.get()); SwipeUpOnShelf(); // Press the home button, the hotseat should transition directly to // kShownHomeLauncher. { HotseatStateWatcher watcher(GetShelfLayoutManager()); ShowShelfAndGoHome(); watcher.CheckEqual({HotseatState::kShownHomeLauncher}); } // Go to in-app. window->Show(); wm::ActivateWindow(window.get()); // Extend the hotseat, then Swipe up to go home, the hotseat should transition // directly to kShownHomeLauncher. SwipeUpOnShelf(); { ui::ScopedAnimationDurationScaleMode regular_animations( ui::ScopedAnimationDurationScaleMode::NON_ZERO_DURATION); HotseatStateWatcher watcher(GetShelfLayoutManager()); FlingUpOnShelf(); watcher.CheckEqual({HotseatState::kShownHomeLauncher}); // Wait for the window animation to complete, and verify the hotseat state // remained kShownHomeLauncher. ShellTestApi().WaitForWindowFinishAnimating(window.get()); watcher.CheckEqual({HotseatState::kShownHomeLauncher}); } // Nothing left to test for autohidden shelf. if (shelf_auto_hide_behavior() == ShelfAutoHideBehavior::kAlways) return; // Go to in-app and do not extend the hotseat. window->Show(); wm::ActivateWindow(window.get()); // TODO(manucornet): This is flaky when the shelf is always auto-hidden. // Investigate and fix (sometimes fails when the assistant is enabled, // sometimes not). if (shelf_auto_hide_behavior() == ShelfAutoHideBehavior::kNever) return; // Press the home button, the hotseat should transition directly to // kShownHomeLauncher. { HotseatStateWatcher watcher(GetShelfLayoutManager()); ShowShelfAndGoHome(); watcher.CheckEqual({HotseatState::kShownHomeLauncher}); } } // Tests that transitioning from overview to home while a transition from home // to overview is still in progress ends up with hotseat in kShownHomeLauncher // state (and in app shelf not visible). TEST_P(HotseatWidgetTest, HomeToOverviewAndBack) { GetPrimaryShelf()->SetAutoHideBehavior(shelf_auto_hide_behavior()); TabletModeControllerTestApi().EnterTabletMode(); std::unique_ptr window = AshTestBase::CreateTestWindow(gfx::Rect(0, 0, 400, 400)); WindowState::Get(window.get())->Minimize(); HotseatStateWatcher watcher(GetShelfLayoutManager()); // Start going to overview. { ui::ScopedAnimationDurationScaleMode regular_animations( ui::ScopedAnimationDurationScaleMode::NON_ZERO_DURATION); StartOverview(); watcher.CheckEqual({/*Hotseat state should not change*/}); } OverviewController* overview_controller = Shell::Get()->overview_controller(); EXPECT_TRUE(overview_controller->InOverviewSession()); ShowShelfAndGoHome(); GetAppListTestHelper()->CheckVisibility(true); EXPECT_FALSE(overview_controller->InOverviewSession()); EXPECT_FALSE(ShelfConfig::Get()->is_in_app()); watcher.CheckEqual({/*Hotseat state should not change*/}); } TEST_P(HotseatWidgetTest, InAppToOverviewAndBack) { GetPrimaryShelf()->SetAutoHideBehavior(shelf_auto_hide_behavior()); TabletModeControllerTestApi().EnterTabletMode(); std::unique_ptr window = AshTestBase::CreateTestWindow(gfx::Rect(0, 0, 400, 400)); wm::ActivateWindow(window.get()); // Start watching hotseat state before swipping up the shelf, so hotseat // change expectation match for both auto-hidden and always-shown shelf. HotseatStateWatcher watcher(GetShelfLayoutManager()); // Make sure shelf (and overview button) are visible - this is moves the // hotseat into kExtended state. if (shelf_auto_hide_behavior() == ShelfAutoHideBehavior::kAlways) SwipeUpOnShelf(); // Start going to overview - use non zero animation so transition is not // immediate. { ui::ScopedAnimationDurationScaleMode regular_animations( ui::ScopedAnimationDurationScaleMode::NON_ZERO_DURATION); StartOverview(); } OverviewController* overview_controller = Shell::Get()->overview_controller(); EXPECT_TRUE(overview_controller->InOverviewSession()); GetAppListTestHelper()->CheckVisibility(false); // Hotseat should be extended as overview is starting. watcher.CheckEqual({HotseatState::kExtended}); // Exit overview to go back to the app window. EndOverview(); EXPECT_FALSE(overview_controller->InOverviewSession()); EXPECT_TRUE(ShelfConfig::Get()->is_in_app()); // The hotseat is expected to be hidden. watcher.CheckEqual({HotseatState::kExtended, HotseatState::kHidden}); } // Tests transition to home screen initiated while transition from app window to // overview is in progress. TEST_P(HotseatWidgetTest, ShowShelfAndGoHomeDuringInAppToOverviewTransition) { GetPrimaryShelf()->SetAutoHideBehavior(shelf_auto_hide_behavior()); TabletModeControllerTestApi().EnterTabletMode(); std::unique_ptr window = AshTestBase::CreateTestWindow(gfx::Rect(0, 0, 400, 400)); wm::ActivateWindow(window.get()); // Start watching hotseat state before swipping up the shelf, so hotseat // change expectation match for both auto-hidden and always-shown shelf. HotseatStateWatcher watcher(GetShelfLayoutManager()); // Make sure shelf (and overview button) are visible - this is moves the // hotseat into kExtended state. if (shelf_auto_hide_behavior() == ShelfAutoHideBehavior::kAlways) SwipeUpOnShelf(); // Start going to overview - use non zero animation so transition is not // immediate. { ui::ScopedAnimationDurationScaleMode regular_animations( ui::ScopedAnimationDurationScaleMode::NON_ZERO_DURATION); StartOverview(); } OverviewController* overview_controller = Shell::Get()->overview_controller(); EXPECT_TRUE(overview_controller->InOverviewSession()); GetAppListTestHelper()->CheckVisibility(false); // Hotseat should be extended as overview is starting. watcher.CheckEqual({HotseatState::kExtended}); // Go home - expect transition to home (with hotseat in kShownHomeLauncher // state, and in app shelf hidden). ShowShelfAndGoHome(); GetAppListTestHelper()->CheckVisibility(true); EXPECT_FALSE(overview_controller->InOverviewSession()); EXPECT_FALSE(ShelfConfig::Get()->is_in_app()); watcher.CheckEqual( {HotseatState::kExtended, HotseatState::kShownHomeLauncher}); } // Tests that in-app -> overview results in only one state change with an // autohidden shelf. TEST_P(HotseatWidgetTest, InAppToOverviewChangesStateOnceAutohiddenShelf) { GetPrimaryShelf()->SetAutoHideBehavior(ShelfAutoHideBehavior::kAlways); TabletModeControllerTestApi().EnterTabletMode(); // Test going to overview mode using the controller from an autohide hidden // shelf. Go to in-app. std::unique_ptr window = AshTestBase::CreateTestWindow(gfx::Rect(0, 0, 400, 400)); wm::ActivateWindow(window.get()); { HotseatStateWatcher watcher(GetShelfLayoutManager()); // Enter overview by using the controller. EnterOverview(); WaitForOverviewAnimation(/*enter=*/true); watcher.CheckEqual({HotseatState::kExtended}); } ExitOverview(); WaitForOverviewAnimation(/*enter=*/false); // Test in-app -> overview again with the autohide shown shelf. EXPECT_TRUE(ShelfConfig::Get()->is_in_app()); EXPECT_EQ(ShelfAutoHideState::SHELF_AUTO_HIDE_HIDDEN, GetShelfLayoutManager()->auto_hide_state()); SwipeUpOnShelf(); { HotseatStateWatcher watcher(GetShelfLayoutManager()); // Enter overview by using the controller. EnterOverview(); WaitForOverviewAnimation(/*enter=*/true); watcher.CheckEqual({}); EXPECT_EQ(HotseatState::kExtended, GetShelfLayoutManager()->hotseat_state()); } } // Tests that going between Applist and overview in tablet mode with no windows // results in no work area change. TEST_P(HotseatWidgetTest, WorkAreaDoesNotUpdateAppListToFromOverviewWithNoWindow) { TabletModeControllerTestApi().EnterTabletMode(); DisplayWorkAreaChangeCounter counter; EnterOverview(); WaitForOverviewAnimation(/*enter=*/true); EXPECT_EQ(0, counter.count()); EnterOverview(); WaitForOverviewAnimation(/*enter=*/true); EXPECT_EQ(0, counter.count()); } // Tests that switching between AppList and overview with a window results in no // work area change. TEST_P(HotseatWidgetTest, WorkAreaDoesNotUpdateAppListToFromOverviewWithWindow) { DisplayWorkAreaChangeCounter counter; TabletModeControllerTestApi().EnterTabletMode(); std::unique_ptr window = AshTestBase::CreateTestWindow(gfx::Rect(0, 0, 400, 400)); wm::ActivateWindow(window.get()); ASSERT_EQ(1, counter.count()); ShowShelfAndGoHome(); StartOverview(); WaitForOverviewAnimation(/*enter=*/true); EXPECT_EQ(1, counter.count()); EndOverview(); WaitForOverviewAnimation(/*enter=*/false); EXPECT_EQ(1, counter.count()); } // Tests that switching between AppList and an active window does not update the // work area. TEST_P(HotseatWidgetTest, WorkAreaDoesNotUpdateOpenWindowToFromAppList) { TabletModeControllerTestApi().EnterTabletMode(); std::unique_ptr window = AshTestBase::CreateTestWindow(gfx::Rect(0, 0, 400, 400)); wm::ActivateWindow(window.get()); ASSERT_TRUE(ShelfConfig::Get()->is_in_app()); // Go to the home launcher, work area should not update. DisplayWorkAreaChangeCounter counter; ShowShelfAndGoHome(); GetAppListTestHelper()->CheckVisibility(true); EXPECT_EQ(0, counter.count()); // Go back to the window, work area should not update. wm::ActivateWindow(window.get()); EXPECT_TRUE(ShelfConfig::Get()->is_in_app()); EXPECT_EQ(0, counter.count()); } // Tests that switching between overview and an active window does not update // the work area. TEST_P(HotseatWidgetTest, WorkAreaDoesNotUpdateOpenWindowToFromOverview) { TabletModeControllerTestApi().EnterTabletMode(); std::unique_ptr window = AshTestBase::CreateTestWindow(gfx::Rect(0, 0, 400, 400)); wm::ActivateWindow(window.get()); ASSERT_TRUE(ShelfConfig::Get()->is_in_app()); // Go to overview, there should not be a work area update. DisplayWorkAreaChangeCounter counter; StartOverview(); WaitForOverviewAnimation(/*enter=*/true); EXPECT_EQ(0, counter.count()); // Go back to the app, there should not be a work area update. wm::ActivateWindow(window.get()); EXPECT_TRUE(ShelfConfig::Get()->is_in_app()); EXPECT_EQ(0, counter.count()); } // Tests that the shelf opaque background is properly updated after a tablet // mode transition with no apps. TEST_P(HotseatWidgetTest, ShelfBackgroundNotVisibleInTabletModeNoApps) { TabletModeControllerTestApi().EnterTabletMode(); EXPECT_FALSE(GetShelfWidget()->GetOpaqueBackground()->visible()); } // Tests that the shelf opaque background is properly updated after a tablet // mode transition with no apps with dense shelf. TEST_P(HotseatWidgetTest, DenseShelfBackgroundNotVisibleInTabletModeNoApps) { UpdateDisplay("300x1000"); TabletModeControllerTestApi().EnterTabletMode(); EXPECT_FALSE(GetShelfWidget()->GetOpaqueBackground()->visible()); } // Tests that the hotseat is extended if focused with a keyboard. TEST_P(HotseatWidgetTest, ExtendHotseatIfFocusedWithKeyboard) { TabletModeControllerTestApi().EnterTabletMode(); std::unique_ptr window = AshTestBase::CreateTestWindow(gfx::Rect(0, 0, 400, 400)); wm::ActivateWindow(window.get()); ASSERT_EQ(HotseatState::kHidden, GetShelfLayoutManager()->hotseat_state()); // Focus the shelf. Hotseat should now show extended. GetPrimaryShelf()->shelf_focus_cycler()->FocusShelf(false /* last_element */); EXPECT_EQ(HotseatState::kExtended, GetShelfLayoutManager()->hotseat_state()); // Focus the status area. Hotseat should now hide, as it was // automatically extended by focusing it. GetPrimaryShelf()->shelf_focus_cycler()->FocusStatusArea( false /* last_element */); EXPECT_EQ(HotseatState::kHidden, GetShelfLayoutManager()->hotseat_state()); // Now swipe up to show the shelf and then focus it with the keyboard. Hotseat // should keep extended. SwipeUpOnShelf(); GetPrimaryShelf()->shelf_focus_cycler()->FocusShelf(false /* last_element */); EXPECT_EQ(HotseatState::kExtended, GetShelfLayoutManager()->hotseat_state()); // Now focus the status area widget again. Hotseat should remain shown, as it // was manually extended. GetPrimaryShelf()->shelf_focus_cycler()->FocusStatusArea( false /* last_element */); EXPECT_EQ(HotseatState::kExtended, GetShelfLayoutManager()->hotseat_state()); } // Tests that if the hotseat was hidden while being focused, doing a traversal // focus on the next element brings it up again. TEST_P(HotseatWidgetTest, SwipeDownOnFocusedHotseat) { TabletModeControllerTestApi().EnterTabletMode(); std::unique_ptr window = AshTestBase::CreateTestWindow(gfx::Rect(0, 0, 400, 400)); wm::ActivateWindow(window.get()); ShelfTestUtil::AddAppShortcut("app_id_1", TYPE_APP); ShelfTestUtil::AddAppShortcut("app_id_2", TYPE_APP); ASSERT_EQ(HotseatState::kHidden, GetShelfLayoutManager()->hotseat_state()); // Focus the shelf, then swipe down on the shelf to hide it. Hotseat should be // hidden. GetPrimaryShelf()->shelf_focus_cycler()->FocusShelf(false /* last_element */); gfx::Rect hotseat_bounds = GetPrimaryShelf()->hotseat_widget()->GetWindowBoundsInScreen(); gfx::Point start = hotseat_bounds.top_center(); gfx::Point end = start + gfx::Vector2d(0, 80); GetEventGenerator()->GestureScrollSequence( start, end, base::Milliseconds(100), 4 /*scroll_steps*/); EXPECT_EQ(HotseatState::kHidden, GetShelfLayoutManager()->hotseat_state()); // Focus to the next element in the hotseat. The hotseat should show again. PressAndReleaseKey(ui::VKEY_TAB); EXPECT_EQ(HotseatState::kExtended, GetShelfLayoutManager()->hotseat_state()); } // Tests that in overview, we can still exit by clicking on the hotseat if the // point is not on the visible area. TEST_P(HotseatWidgetTest, ExitOverviewWithClickOnHotseat) { std::unique_ptr window1 = AshTestBase::CreateTestWindow(); ShelfTestUtil::AddAppShortcut("app_id_1", TYPE_APP); TabletModeControllerTestApi().EnterTabletMode(); ASSERT_TRUE(TabletModeControllerTestApi().IsTabletModeStarted()); ASSERT_FALSE(WindowState::Get(window1.get())->IsMinimized()); // Enter overview, hotseat is visible. Choose the point to the farthest left. // This point will not be visible. auto* overview_controller = Shell::Get()->overview_controller(); auto* hotseat_widget = GetPrimaryShelf()->hotseat_widget(); EnterOverview(); ASSERT_TRUE(overview_controller->InOverviewSession()); ASSERT_EQ(HotseatState::kExtended, GetShelfLayoutManager()->hotseat_state()); gfx::Point far_left_point = hotseat_widget->GetWindowBoundsInScreen().left_center(); // Tests that on clicking, we exit overview and all windows are minimized. GetEventGenerator()->set_current_screen_location(far_left_point); GetEventGenerator()->ClickLeftButton(); EXPECT_EQ(HotseatState::kShownHomeLauncher, GetShelfLayoutManager()->hotseat_state()); EXPECT_TRUE(WindowState::Get(window1.get())->IsMinimized()); EXPECT_FALSE(overview_controller->InOverviewSession()); } // Hides the hotseat if the hotseat is in kExtendedMode and the system tray // is about to show (see https://crbug.com/1028321). TEST_P(HotseatWidgetTest, DismissHotseatWhenSystemTrayShows) { GetPrimaryShelf()->SetAutoHideBehavior(shelf_auto_hide_behavior()); TabletModeControllerTestApi().EnterTabletMode(); std::unique_ptr window = AshTestBase::CreateTestWindow(gfx::Rect(0, 0, 400, 400)); wm::ActivateWindow(window.get()); SwipeUpOnShelf(); ASSERT_EQ(HotseatState::kExtended, GetShelfLayoutManager()->hotseat_state()); // Activates the system tray when hotseat is in kExtended mode and waits for // the update in system tray to finish. StatusAreaWidget* status_area_widget = GetShelfWidget()->status_area_widget(); const gfx::Point status_area_widget_center = status_area_widget->GetNativeView()->GetBoundsInScreen().CenterPoint(); GetEventGenerator()->GestureTapAt(status_area_widget_center); base::RunLoop().RunUntilIdle(); // Expects that the system tray shows and the hotseat is hidden. EXPECT_EQ(HotseatState::kHidden, GetShelfLayoutManager()->hotseat_state()); EXPECT_TRUE(status_area_widget->unified_system_tray()->IsBubbleShown()); // Early out since the remaining code is only meaningful for auto-hide shelf. if (GetPrimaryShelf()->auto_hide_behavior() != ShelfAutoHideBehavior::kAlways) { return; } // Auto-hide shelf should show when opening the system tray. EXPECT_EQ(ShelfAutoHideState::SHELF_AUTO_HIDE_SHOWN, GetShelfLayoutManager()->auto_hide_state()); // Auto-hide shelf should hide when closing the system tray. GetEventGenerator()->GestureTapAt(status_area_widget_center); // Waits for the system tray to be closed. base::RunLoop().RunUntilIdle(); EXPECT_EQ(ShelfAutoHideState::SHELF_AUTO_HIDE_HIDDEN, GetShelfLayoutManager()->auto_hide_state()); } // Tests that the hotseat hides when it is in kExtendedMode and a status area // tray bubble is shown. TEST_P(HotseatWidgetTest, DismissHotseatWhenStatusAreaTrayShows) { TabletModeControllerTestApi().EnterTabletMode(); std::unique_ptr window = AshTestBase::CreateTestWindow(gfx::Rect(0, 0, 400, 400)); wm::ActivateWindow(window.get()); StatusAreaWidget* status_area_widget = GetShelfWidget()->status_area_widget(); status_area_widget->ime_menu_tray()->SetVisiblePreferred(true); // Show the hotseat. SwipeUpOnShelf(); ASSERT_EQ(HotseatState::kExtended, GetShelfLayoutManager()->hotseat_state()); EXPECT_FALSE(status_area_widget->ime_menu_tray()->GetBubbleView()); // Show the ime menu tray bubble, and wait for the hotseat to be hidden. GetEventGenerator()->GestureTapAt( status_area_widget->ime_menu_tray()->GetBoundsInScreen().CenterPoint()); base::RunLoop().RunUntilIdle(); // The hotseat should be hidden and the tray bubble should be shown. EXPECT_EQ(HotseatState::kHidden, GetShelfLayoutManager()->hotseat_state()); EXPECT_TRUE(status_area_widget->ime_menu_tray()->GetBubbleView()); // Swiping up on the shelf should hide the tray bubble and extend the hotseat. SwipeUpOnShelf(); EXPECT_FALSE(status_area_widget->ime_menu_tray()->GetBubbleView()); EXPECT_EQ(HotseatState::kExtended, GetShelfLayoutManager()->hotseat_state()); } // Tests that the work area updates once each when going to/from tablet mode // with no windows open. TEST_P(HotseatWidgetTest, WorkAreaUpdatesClamshellToFromHomeLauncherNoWindows) { DisplayWorkAreaChangeCounter counter; TabletModeControllerTestApi().EnterTabletMode(); EXPECT_EQ(1, counter.count()); TabletModeControllerTestApi().LeaveTabletMode(); EXPECT_EQ(2, counter.count()); } // Tests that the work area changes just once when opening a window in tablet // mode. TEST_P(HotseatWidgetTest, OpenWindowInTabletModeChangesWorkArea) { DisplayWorkAreaChangeCounter counter; TabletModeControllerTestApi().EnterTabletMode(); ASSERT_EQ(1, counter.count()); std::unique_ptr window = AshTestBase::CreateTestWindow(gfx::Rect(0, 0, 400, 400)); wm::ActivateWindow(window.get()); EXPECT_EQ(1, counter.count()); } // Tests that going to and from tablet mode with an open window results in a // work area change. TEST_P(HotseatWidgetTest, ToFromTabletModeWithWindowChangesWorkArea) { DisplayWorkAreaChangeCounter counter; std::unique_ptr window = AshTestBase::CreateTestWindow(gfx::Rect(0, 0, 400, 400)); wm::ActivateWindow(window.get()); TabletModeControllerTestApi().EnterTabletMode(); EXPECT_EQ(1, counter.count()); TabletModeControllerTestApi().LeaveTabletMode(); EXPECT_EQ(2, counter.count()); } // Tests that the work area changes when fullscreening the active window or // autohiding the shelf. TEST_P(HotseatWidgetTest, ShelfVisibilityChangeChangesWorkArea) { UpdateDisplay("800x603"); TabletModeControllerTestApi().EnterTabletMode(); auto window = AshTestBase::CreateTestWindow(gfx::Rect(400, 400)); // The expected work area is 3 pixels smaller to leave space to swipe the auto // hide shelf up. const gfx::Rect expected_auto_hide_work_area(800, 600); const gfx::Rect expected_in_app_work_area( 800, 603 - ShelfConfig::Get()->in_app_shelf_size()); auto get_work_area = []() -> gfx::Rect { return WorkAreaInsets::ForWindow(Shell::GetPrimaryRootWindow()) ->user_work_area_bounds(); }; DisplayWorkAreaChangeCounter counter; WMEvent toggle_fullscreen(WM_EVENT_TOGGLE_FULLSCREEN); WindowState::Get(window.get())->OnWMEvent(&toggle_fullscreen); EXPECT_EQ(expected_auto_hide_work_area, get_work_area()); EXPECT_EQ(1, counter.count()); WindowState::Get(window.get())->OnWMEvent(&toggle_fullscreen); EXPECT_EQ(expected_in_app_work_area, get_work_area()); EXPECT_EQ(2, counter.count()); GetPrimaryShelf()->SetAutoHideBehavior(ShelfAutoHideBehavior::kAlways); EXPECT_EQ(expected_auto_hide_work_area, get_work_area()); EXPECT_EQ(3, counter.count()); GetPrimaryShelf()->SetAutoHideBehavior(ShelfAutoHideBehavior::kNever); EXPECT_EQ(expected_in_app_work_area, get_work_area()); EXPECT_EQ(4, counter.count()); } // Tests that the hotseat is flush with the bottom of the screen when in // clamshell mode and the shelf is oriented on the bottom. TEST_P(HotseatWidgetTest, HotseatFlushWithScreenBottomInClamshell) { GetPrimaryShelf()->SetAutoHideBehavior(shelf_auto_hide_behavior()); const int display_height = display::Screen::GetScreen()->GetPrimaryDisplay().bounds().height(); const int hotseat_bottom = GetPrimaryShelf() ->shelf_widget() ->hotseat_widget() ->GetWindowBoundsInScreen() .bottom(); EXPECT_EQ(hotseat_bottom, display_height); } // Tests that upward drag gesture from the shelf in tablet mode affects the // active window presentation. TEST_P(HotseatWidgetTest, DragActiveWindowInTabletMode) { GetPrimaryShelf()->SetAutoHideBehavior(shelf_auto_hide_behavior()); TabletModeControllerTestApi().EnterTabletMode(); std::unique_ptr window = AshTestBase::CreateTestWindow(gfx::Rect(0, 0, 400, 400)); wm::ActivateWindow(window.get()); // Swipe up to bring up the hotseat first. SwipeUpOnShelf(); ASSERT_EQ(HotseatState::kExtended, GetShelfLayoutManager()->hotseat_state()); // Now swipe up again to start drag the active window. ui::test::EventGenerator* generator = GetEventGenerator(); const gfx::Rect bottom_shelf_bounds = GetShelfWidget()->GetWindowBoundsInScreen(); generator->MoveMouseTo(bottom_shelf_bounds.CenterPoint()); generator->PressTouch(); EXPECT_TRUE(window->layer()->transform().IsIdentity()); // Drag upward, test the window transform changes. const gfx::Rect display_bounds = display::Screen::GetScreen()->GetPrimaryDisplay().bounds(); generator->MoveTouch(display_bounds.CenterPoint()); const gfx::Transform upward_transform = window->layer()->transform(); EXPECT_FALSE(upward_transform.IsIdentity()); // Drag downwad, test the window tranfrom changes. generator->MoveTouch(display_bounds.bottom_center()); const gfx::Transform downward_transform = window->layer()->transform(); EXPECT_NE(upward_transform, downward_transform); generator->ReleaseTouch(); EXPECT_TRUE(window->layer()->transform().IsIdentity()); } // Tests that when hotseat and drag-window-to-overview features are both // enabled, hotseat is not extended after dragging a window to overview, and // then activating the window. TEST_P(HotseatWidgetTest, ExitingOverviewHidesHotseat) { const ShelfAutoHideBehavior auto_hide_behavior = shelf_auto_hide_behavior(); GetPrimaryShelf()->SetAutoHideBehavior(auto_hide_behavior); TabletModeControllerTestApi().EnterTabletMode(); std::unique_ptr window = AshTestBase::CreateTestWindow(gfx::Rect(0, 0, 400, 400)); wm::ActivateWindow(window.get()); // If the shelf is auto-hidden, swipe up to bring up shelf and hotseat first // (otherwise, the window drag to overview will not be handled). if (auto_hide_behavior == ShelfAutoHideBehavior::kAlways) { SwipeUpOnShelf(); ASSERT_EQ(HotseatState::kExtended, GetShelfLayoutManager()->hotseat_state()); } // Swipe up to start dragging the active window. const gfx::Rect bottom_shelf_bounds = GetShelfWidget()->GetWindowBoundsInScreen(); StartScroll(bottom_shelf_bounds.CenterPoint()); // Ensure swipe goes past the top of the hotseat first to activate the window // drag controller. UpdateScroll( -GetPrimaryShelf()->hotseat_widget()->GetHotseatFullDragAmount()); // Drag upward, to the center of the screen, and release (this should enter // the overview). const gfx::Rect display_bounds = display::Screen::GetScreen()->GetPrimaryDisplay().bounds(); UpdateScroll(display_bounds.CenterPoint().y() - bottom_shelf_bounds.CenterPoint().y()); // Small scroll update, to simulate the user holding the pointer. UpdateScroll(2); DragWindowFromShelfController* window_drag_controller = GetShelfLayoutManager()->window_drag_controller_for_testing(); ASSERT_TRUE(window_drag_controller); DragWindowFromShelfControllerTestApi test_api; test_api.WaitUntilOverviewIsShown(window_drag_controller); EndScroll(/*is_fling=*/false, 0.f); OverviewController* overview_controller = Shell::Get()->overview_controller(); EXPECT_EQ(HotseatState::kExtended, GetShelfLayoutManager()->hotseat_state()); EXPECT_TRUE(overview_controller->InOverviewSession()); // Activate the window - the overview session should exit, and hotseat should // be hidden. wm::ActivateWindow(window.get()); EXPECT_FALSE(overview_controller->InOverviewSession()); EXPECT_EQ(HotseatState::kHidden, GetShelfLayoutManager()->hotseat_state()); } // Tests that failing to drag the maximized window to overview mode results in // an extended hotseat. TEST_P(HotseatWidgetTest, FailingOverviewDragResultsInExtendedHotseat) { const ShelfAutoHideBehavior auto_hide_behavior = shelf_auto_hide_behavior(); GetPrimaryShelf()->SetAutoHideBehavior(auto_hide_behavior); TabletModeControllerTestApi().EnterTabletMode(); std::unique_ptr window = AshTestBase::CreateTestWindow(gfx::Rect(0, 0, 400, 400)); wm::ActivateWindow(window.get()); // If the shelf is auto-hidden, swipe up to bring up shelf and hotseat first // (otherwise, the window drag to overview will not be handled). if (auto_hide_behavior == ShelfAutoHideBehavior::kAlways) { SwipeUpOnShelf(); ASSERT_EQ(HotseatState::kExtended, GetShelfLayoutManager()->hotseat_state()); } // Swipe up to start dragging the active window. const gfx::Rect bottom_shelf_bounds = GetShelfWidget()->GetWindowBoundsInScreen(); StartScroll(bottom_shelf_bounds.top_center()); const int extended_hotseat_distance_from_top_of_shelf = ShelfConfig::Get()->hotseat_bottom_padding() + GetPrimaryShelf()->hotseat_widget()->GetHotseatSize(); // Overview is triggered when the bottom of the dragged window goes past the // top of the hotseat. The window scaling and translation are handled slightly // differently for if the hotseat is extended or not. if (HotseatState::kExtended == GetShelfLayoutManager()->hotseat_state()) { // Drag upward, a bit below the hotseat extended height, to ensure that the // bottom of the dragged window doesn't go past the top of the hotseat, so // that it doesn't go into overview. UpdateScroll(-extended_hotseat_distance_from_top_of_shelf + 20); } else { // Drag upward, a bit past the hotseat extended height so that the window // drag controller is activated, but not enough to go to overview. UpdateScroll(-extended_hotseat_distance_from_top_of_shelf - 30); } EndScroll(/*is_fling=*/false, 0.f); ASSERT_FALSE(Shell::Get()->overview_controller()->InOverviewSession()); EXPECT_EQ(HotseatState::kExtended, GetShelfLayoutManager()->hotseat_state()); } // Tests that hotseat remains in extended state while in overview mode when // flinging the shelf up or down. TEST_P(HotseatWidgetTest, SwipeOnHotseatInOverview) { GetPrimaryShelf()->SetAutoHideBehavior(shelf_auto_hide_behavior()); TabletModeControllerTestApi().EnterTabletMode(); std::unique_ptr window = AshTestBase::CreateTestWindow(gfx::Rect(0, 0, 400, 400)); wm::ActivateWindow(window.get()); OverviewController* overview_controller = Shell::Get()->overview_controller(); EnterOverview(); Shelf* const shelf = GetPrimaryShelf(); SwipeUpOnShelf(); EXPECT_TRUE(overview_controller->InOverviewSession()); EXPECT_EQ(HotseatState::kExtended, GetShelfLayoutManager()->hotseat_state()); if (shelf_auto_hide_behavior() == ShelfAutoHideBehavior::kAlways) { EXPECT_EQ(SHELF_AUTO_HIDE, shelf->GetVisibilityState()); EXPECT_EQ(SHELF_AUTO_HIDE_SHOWN, shelf->GetAutoHideState()); } else { EXPECT_EQ(SHELF_VISIBLE, shelf->GetVisibilityState()); } // Drag from the hotseat to the bezel, the hotseat should remain in extended // state. DragHotseatDownToBezel(); EXPECT_TRUE(overview_controller->InOverviewSession()); EXPECT_EQ(HotseatState::kExtended, GetShelfLayoutManager()->hotseat_state()); if (shelf_auto_hide_behavior() == ShelfAutoHideBehavior::kAlways) { EXPECT_EQ(SHELF_AUTO_HIDE, shelf->GetVisibilityState()); EXPECT_EQ(SHELF_AUTO_HIDE_SHOWN, shelf->GetAutoHideState()); } else { EXPECT_EQ(SHELF_VISIBLE, shelf->GetVisibilityState()); } SwipeUpOnShelf(); EXPECT_TRUE(overview_controller->InOverviewSession()); EXPECT_EQ(HotseatState::kExtended, GetShelfLayoutManager()->hotseat_state()); if (shelf_auto_hide_behavior() == ShelfAutoHideBehavior::kAlways) { EXPECT_EQ(SHELF_AUTO_HIDE, shelf->GetVisibilityState()); EXPECT_EQ(SHELF_AUTO_HIDE_SHOWN, shelf->GetAutoHideState()); } else { EXPECT_EQ(SHELF_VISIBLE, shelf->GetVisibilityState()); } } TEST_P(HotseatWidgetTest, SwipeOnHotseatInSplitViewWithOverview) { Shelf* const shelf = GetPrimaryShelf(); shelf->SetAutoHideBehavior(shelf_auto_hide_behavior()); TabletModeControllerTestApi().EnterTabletMode(); std::unique_ptr window = AshTestBase::CreateTestWindow(gfx::Rect(0, 0, 400, 400)); wm::ActivateWindow(window.get()); OverviewController* overview_controller = Shell::Get()->overview_controller(); EnterOverview(); SplitViewController* split_view_controller = SplitViewController::Get(Shell::GetPrimaryRootWindow()); split_view_controller->SnapWindow(window.get(), SplitViewController::LEFT); SwipeUpOnShelf(); EXPECT_TRUE(split_view_controller->InSplitViewMode()); EXPECT_TRUE(overview_controller->InOverviewSession()); EXPECT_EQ(HotseatState::kExtended, GetShelfLayoutManager()->hotseat_state()); if (shelf_auto_hide_behavior() == ShelfAutoHideBehavior::kAlways) { EXPECT_EQ(SHELF_AUTO_HIDE, shelf->GetVisibilityState()); EXPECT_EQ(SHELF_AUTO_HIDE_SHOWN, shelf->GetAutoHideState()); } else { EXPECT_EQ(SHELF_VISIBLE, shelf->GetVisibilityState()); } DragHotseatDownToBezel(); EXPECT_TRUE(split_view_controller->InSplitViewMode()); EXPECT_TRUE(overview_controller->InOverviewSession()); EXPECT_EQ(HotseatState::kHidden, GetShelfLayoutManager()->hotseat_state()); if (shelf_auto_hide_behavior() == ShelfAutoHideBehavior::kAlways) { EXPECT_EQ(SHELF_AUTO_HIDE, shelf->GetVisibilityState()); EXPECT_EQ(SHELF_AUTO_HIDE_SHOWN, shelf->GetAutoHideState()); } else { EXPECT_EQ(SHELF_VISIBLE, shelf->GetVisibilityState()); } SwipeUpOnShelf(); EXPECT_TRUE(split_view_controller->InSplitViewMode()); EXPECT_TRUE(overview_controller->InOverviewSession()); EXPECT_EQ(HotseatState::kExtended, GetShelfLayoutManager()->hotseat_state()); if (shelf_auto_hide_behavior() == ShelfAutoHideBehavior::kAlways) { EXPECT_EQ(SHELF_AUTO_HIDE, shelf->GetVisibilityState()); EXPECT_EQ(SHELF_AUTO_HIDE_SHOWN, shelf->GetAutoHideState()); } else { EXPECT_EQ(SHELF_VISIBLE, shelf->GetVisibilityState()); } } TEST_P(HotseatWidgetTest, SwipeOnHotseatInSplitView) { Shelf* const shelf = GetPrimaryShelf(); shelf->SetAutoHideBehavior(shelf_auto_hide_behavior()); TabletModeControllerTestApi().EnterTabletMode(); std::unique_ptr window1 = AshTestBase::CreateTestWindow(gfx::Rect(0, 0, 400, 400)); std::unique_ptr window2 = AshTestBase::CreateTestWindow(gfx::Rect(0, 0, 400, 400)); wm::ActivateWindow(window1.get()); SplitViewController* split_view_controller = SplitViewController::Get(Shell::GetPrimaryRootWindow()); split_view_controller->SnapWindow(window1.get(), SplitViewController::LEFT); split_view_controller->SnapWindow(window2.get(), SplitViewController::RIGHT); EXPECT_TRUE(split_view_controller->InSplitViewMode()); SwipeUpOnShelf(); EXPECT_TRUE(split_view_controller->InSplitViewMode()); EXPECT_EQ(HotseatState::kExtended, GetShelfLayoutManager()->hotseat_state()); if (shelf_auto_hide_behavior() == ShelfAutoHideBehavior::kAlways) { EXPECT_EQ(SHELF_AUTO_HIDE, shelf->GetVisibilityState()); EXPECT_EQ(SHELF_AUTO_HIDE_SHOWN, shelf->GetAutoHideState()); } else { EXPECT_EQ(SHELF_VISIBLE, shelf->GetVisibilityState()); } DragHotseatDownToBezel(); EXPECT_TRUE(split_view_controller->InSplitViewMode()); EXPECT_EQ(HotseatState::kHidden, GetShelfLayoutManager()->hotseat_state()); if (shelf_auto_hide_behavior() == ShelfAutoHideBehavior::kAlways) { EXPECT_EQ(SHELF_AUTO_HIDE, shelf->GetVisibilityState()); EXPECT_EQ(SHELF_AUTO_HIDE_HIDDEN, shelf->GetAutoHideState()); } else { EXPECT_EQ(SHELF_VISIBLE, shelf->GetVisibilityState()); } SwipeUpOnShelf(); EXPECT_TRUE(split_view_controller->InSplitViewMode()); EXPECT_EQ(HotseatState::kExtended, GetShelfLayoutManager()->hotseat_state()); if (shelf_auto_hide_behavior() == ShelfAutoHideBehavior::kAlways) { EXPECT_EQ(SHELF_AUTO_HIDE, shelf->GetVisibilityState()); EXPECT_EQ(SHELF_AUTO_HIDE_SHOWN, shelf->GetAutoHideState()); } else { EXPECT_EQ(SHELF_VISIBLE, shelf->GetVisibilityState()); } } // Tests that swiping downward, towards the bezel, from a variety of points // results in hiding the hotseat. TEST_P(HotseatWidgetTest, HotseatHidesWhenSwipedToBezel) { // Go to in-app shelf and extend the hotseat. TabletModeControllerTestApi().EnterTabletMode(); std::unique_ptr window = AshTestBase::CreateTestWindow(gfx::Rect(0, 0, 400, 400)); wm::ActivateWindow(window.get()); SwipeUpOnShelf(); // Drag from the hotseat to the bezel, the hotseat should hide. DragHotseatDownToBezel(); EXPECT_EQ(HotseatState::kHidden, GetShelfLayoutManager()->hotseat_state()); // Reset the hotseat and swipe from the center of the hotseat, it should hide. SwipeUpOnShelf(); gfx::Rect shelf_widget_bounds = GetShelfWidget()->GetWindowBoundsInScreen(); gfx::Rect hotseat_bounds = GetPrimaryShelf()->hotseat_widget()->GetWindowBoundsInScreen(); gfx::Point start = hotseat_bounds.CenterPoint(); const gfx::Point end = gfx::Point(shelf_widget_bounds.x() + shelf_widget_bounds.width() / 2, shelf_widget_bounds.bottom() + 1); const base::TimeDelta kTimeDelta = base::Milliseconds(100); const int kNumScrollSteps = 4; GetEventGenerator()->GestureScrollSequence(start, end, kTimeDelta, kNumScrollSteps); EXPECT_EQ(HotseatState::kHidden, GetShelfLayoutManager()->hotseat_state()); // Reset the hotseat and swipe from the bottom of the hotseat, it should hide. SwipeUpOnShelf(); start = hotseat_bounds.bottom_center(); start.Offset(0, -1); GetEventGenerator()->GestureScrollSequence(start, end, kTimeDelta, kNumScrollSteps); EXPECT_EQ(HotseatState::kHidden, GetShelfLayoutManager()->hotseat_state()); // Reset the hotseat and swipe from the center of the in-app shelf, it should // hide. SwipeUpOnShelf(); start = shelf_widget_bounds.CenterPoint(); GetEventGenerator()->GestureScrollSequence(start, end, kTimeDelta, kNumScrollSteps); EXPECT_EQ(HotseatState::kHidden, GetShelfLayoutManager()->hotseat_state()); // Reset the hotseat and swipe from the bottom of the in-app shelf, it should // hide. SwipeUpOnShelf(); EXPECT_EQ(HotseatState::kExtended, GetShelfLayoutManager()->hotseat_state()); start = shelf_widget_bounds.bottom_center(); // The first few events which get sent to ShelfLayoutManager are // ui::ET_TAP_DOWN, and ui::ET_GESTURE_START. After a few px we get // ui::ET_GESTURE_SCROLL_UPDATE. Add 6 px of slop to get the first events out // of the way, and 1 extra px to ensure we are not on the bottom edge of the // display. start.Offset(0, -7); GetEventGenerator()->GestureScrollSequence(start, end, kTimeDelta, kNumScrollSteps); EXPECT_EQ(HotseatState::kHidden, GetShelfLayoutManager()->hotseat_state()); } // Tests that flinging up the in-app shelf should show the hotseat. TEST_P(HotseatWidgetTest, FlingUpHotseatWithShortFling) { TabletModeControllerTestApi().EnterTabletMode(); std::unique_ptr window = AshTestBase::CreateTestWindow(gfx::Rect(0, 0, 400, 400)); wm::ActivateWindow(window.get()); GetAppListTestHelper()->CheckVisibility(false); base::HistogramTester histogram_tester; histogram_tester.ExpectBucketCount(kHotseatGestureHistogramName, InAppShelfGestures::kSwipeUpToShow, 0); // Scrolls the hotseat by a distance not sufficuent to trigger the action of // entering home screen from the in-app shelf. gfx::Rect display_bounds = display::Screen::GetScreen()->GetPrimaryDisplay().bounds(); const gfx::Point start(display_bounds.bottom_center()); const gfx::Point end(start + gfx::Vector2d(0, -20)); const int fling_speed = DragWindowFromShelfController::kVelocityToHomeScreenThreshold + 1; const int scroll_steps = 20; base::TimeDelta scroll_time = GetEventGenerator()->CalculateScrollDurationForFlingVelocity( start, end, fling_speed, scroll_steps); GetEventGenerator()->GestureScrollSequence(start, end, scroll_time, scroll_steps); base::RunLoop().RunUntilIdle(); EXPECT_EQ(HotseatState::kExtended, GetShelfLayoutManager()->hotseat_state()); GetAppListTestHelper()->CheckVisibility(false); histogram_tester.ExpectBucketCount(kHotseatGestureHistogramName, InAppShelfGestures::kSwipeUpToShow, 1); } // Tests that flinging up the in-app shelf should show the home launcher if the // gesture distance is long enough. TEST_P(HotseatWidgetTest, FlingUpHotseatWithLongFling) { TabletModeControllerTestApi().EnterTabletMode(); std::unique_ptr window = AshTestBase::CreateTestWindow(gfx::Rect(0, 0, 400, 400)); wm::ActivateWindow(window.get()); GetAppListTestHelper()->CheckVisibility(false); base::HistogramTester histogram_tester; histogram_tester.ExpectBucketCount( kHotseatGestureHistogramName, InAppShelfGestures::kFlingUpToShowHomeScreen, 0); // Scrolls the hotseat by the sufficient distance to trigger the action of // entering home screen from the in-app shelf. gfx::Rect display_bounds = display::Screen::GetScreen()->GetPrimaryDisplay().bounds(); const gfx::Point start(display_bounds.bottom_center()); const gfx::Point end(start + gfx::Vector2d(0, -200)); const int fling_speed = DragWindowFromShelfController::kVelocityToHomeScreenThreshold + 1; const int scroll_steps = 20; base::TimeDelta scroll_time = GetEventGenerator()->CalculateScrollDurationForFlingVelocity( start, end, fling_speed, scroll_steps); GetEventGenerator()->GestureScrollSequence(start, end, scroll_time, scroll_steps); base::RunLoop().RunUntilIdle(); EXPECT_EQ(HotseatState::kShownHomeLauncher, GetShelfLayoutManager()->hotseat_state()); GetAppListTestHelper()->CheckVisibility(true); histogram_tester.ExpectBucketCount( kHotseatGestureHistogramName, InAppShelfGestures::kFlingUpToShowHomeScreen, 1); } // Tests that UpdateVisibilityState is ignored during a shelf drag. This // prevents drag from getting interrupted. TEST_P(HotseatWidgetTest, NoVisibilityStateUpdateDuringDrag) { // Autohide the shelf, then start a shelf drag. GetPrimaryShelf()->SetAutoHideBehavior(ShelfAutoHideBehavior::kAlways); std::unique_ptr window1 = AshTestBase::CreateTestWindow(gfx::Rect(0, 0, 400, 400)); wm::ActivateWindow(window1.get()); ASSERT_EQ(SHELF_AUTO_HIDE_HIDDEN, GetPrimaryShelf()->GetAutoHideState()); // Drag the autohidden shelf up a bit, then open a new window and activate it // during the drag. The shelf state should not change. gfx::Point start_drag = GetVisibleShelfWidgetBoundsInScreen().top_center(); GetEventGenerator()->set_current_screen_location(start_drag); GetEventGenerator()->PressTouch(); GetEventGenerator()->MoveTouchBy(0, -2); auto shelf_state_watcher = std::make_unique(); std::unique_ptr window2 = AshTestBase::CreateTestWindow(gfx::Rect(0, 0, 400, 400)); wm::ActivateWindow(window2.get()); window2->SetBounds(gfx::Rect(0, 0, 200, 200)); EXPECT_EQ(0, shelf_state_watcher->state_change_count()); } // Tests that when tablet mode has ended, the hotseat background is no longer // visible. See crbug/1050383 TEST_P(HotseatWidgetTest, HotseatBackgroundDisappearsAfterTabletModeEnd) { HotseatWidget* hotseat = GetPrimaryShelf()->hotseat_widget(); EXPECT_FALSE(hotseat->GetIsTranslucentBackgroundVisibleForTest()); TabletModeControllerTestApi().EnterTabletMode(); EXPECT_TRUE(hotseat->GetIsTranslucentBackgroundVisibleForTest()); TabletModeControllerTestApi().LeaveTabletMode(); EXPECT_FALSE(hotseat->GetIsTranslucentBackgroundVisibleForTest()); } // Tests that popups don't activate the hotseat. (crbug.com/1018266) TEST_P(HotseatWidgetTest, HotseatRemainsHiddenIfPopupLaunched) { // Go to in-app shelf and extend the hotseat. TabletModeControllerTestApi().EnterTabletMode(); std::unique_ptr window = AshTestBase::CreateTestWindow(gfx::Rect(0, 0, 400, 400)); wm::ActivateWindow(window.get()); SwipeUpOnShelf(); EXPECT_EQ(HotseatState::kExtended, GetShelfLayoutManager()->hotseat_state()); // Hide hotseat by clicking outside its bounds. gfx::Rect hotseat_bounds = GetPrimaryShelf()->hotseat_widget()->GetWindowBoundsInScreen(); gfx::Point start = hotseat_bounds.top_center(); GetEventGenerator()->GestureTapAt(gfx::Point(start.x() + 1, start.y() - 1)); EXPECT_EQ(HotseatState::kHidden, GetShelfLayoutManager()->hotseat_state()); // Create a popup window and wait until all actions finish. The hotseat should // remain hidden. aura::Window* window_2 = CreateTestWindowInParent(window.get()); window_2->SetBounds(gfx::Rect(201, 0, 100, 100)); window_2->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_NORMAL); window_2->Show(); GetAppListTestHelper()->WaitUntilIdle(); EXPECT_EQ(HotseatState::kHidden, GetShelfLayoutManager()->hotseat_state()); } // Tests that blur is not showing during animations. TEST_P(HotseatWidgetTest, NoBlurDuringAnimations) { TabletModeControllerTestApi().EnterTabletMode(); ASSERT_EQ( ShelfConfig::Get()->shelf_blur_radius(), GetShelfWidget()->hotseat_widget()->GetHotseatBackgroundBlurForTest()); ui::ScopedAnimationDurationScaleMode regular_animations( ui::ScopedAnimationDurationScaleMode::NORMAL_DURATION); // Open a window, as the hotseat animates to kHidden, it should lose its blur. std::unique_ptr window = AshTestBase::CreateTestWindow(gfx::Rect(0, 0, 400, 400)); wm::ActivateWindow(window.get()); EXPECT_EQ( 0, GetShelfWidget()->hotseat_widget()->GetHotseatBackgroundBlurForTest()); // Wait for the animation to finish, hotseat blur should return. ShellTestApi().WaitForWindowFinishAnimating(window.get()); EXPECT_EQ( ShelfConfig::Get()->shelf_blur_radius(), GetShelfWidget()->hotseat_widget()->GetHotseatBackgroundBlurForTest()); } // Tests that hotseat bounds don't jump when transitioning from drag to // animation. TEST_P(HotseatWidgetTest, AnimationAfterDrag) { TabletModeControllerTestApi().EnterTabletMode(); // Add an app to shelf - the app will be used to track the shelf view position // throughout the test. ShelfTestUtil::AddAppShortcut("fake_app", TYPE_PINNED_APP); // Open a window so the hotseat transitions to hidden state. std::unique_ptr window = AshTestBase::CreateTestWindow(gfx::Rect(0, 0, 400, 400)); wm::ActivateWindow(window.get()); ui::ScopedAnimationDurationScaleMode animation_duration( ui::ScopedAnimationDurationScaleMode::NON_ZERO_DURATION); HotseatWidget* const hotseat_widget = GetPrimaryShelf()->hotseat_widget(); gfx::Point last_app_views_position = hotseat_widget->GetWindowBoundsInScreen().origin(); // Returns whether the hotseat vertical position has changed comapred to // |last_hotseat_y|, and updates |last_hotseat_y| to match the current hotseat // position. auto app_views_moved = [&last_app_views_position, &hotseat_widget]() -> bool { gfx::Point app_views_position = ShelfViewTestAPI(hotseat_widget->scrollable_shelf_view()->shelf_view()) .GetViewAt(0) ->GetBoundsInScreen() .origin(); hotseat_widget->GetLayer()->transform().TransformPoint(&app_views_position); const bool position_changed = app_views_position != last_app_views_position; last_app_views_position = app_views_position; return position_changed; }; ui::test::EventGenerator* generator = GetEventGenerator(); const gfx::Rect display_bounds = display::Screen::GetScreen()->GetPrimaryDisplay().bounds(); // Drag upwards from the bottom of the screen to bring up hotseat - this // should request presentation time metric to be reported. generator->PressTouch(display_bounds.bottom_center()); // Drag the hotseat upward, by the height of the shelf. const int shelf_height = GetShelfWidget()->GetWindowBoundsInScreen().height(); generator->MoveTouchBy(0, -shelf_height); ASSERT_TRUE(app_views_moved()); // Release touch, and verify the hotseat position remained the same. generator->ReleaseTouch(); EXPECT_FALSE(app_views_moved()); // Wait for the hotseat to animate to extended state. ShellTestApi().WaitForWindowFinishAnimating( hotseat_widget->GetNativeWindow()); ASSERT_EQ(HotseatState::kExtended, GetShelfLayoutManager()->hotseat_state()); EXPECT_TRUE(app_views_moved()); // Drag hotseat down. generator->PressTouch(hotseat_widget->GetWindowBoundsInScreen().top_center()); generator->MoveTouchBy(0, shelf_height); ASSERT_TRUE(app_views_moved()); // Release touch, and verify the hotseat position remained the same. generator->ReleaseTouch(); EXPECT_FALSE(app_views_moved()); // Wait for the hotseat to animate to extended state. ShellTestApi().WaitForWindowFinishAnimating( hotseat_widget->GetNativeWindow()); ASSERT_EQ(HotseatState::kHidden, GetShelfLayoutManager()->hotseat_state()); ASSERT_TRUE(app_views_moved()); // Drag the hotseat back up, and release so animation to extended state // starts. generator->PressTouch(display_bounds.bottom_center()); generator->MoveTouchBy(0, -shelf_height); ASSERT_TRUE(app_views_moved()); generator->ReleaseTouch(); EXPECT_FALSE(app_views_moved()); // Close the test widget to start transition to the shown state. window.reset(); // The apparent hotseat bound should remain the same as the transition to show // state starts. ASSERT_EQ(HotseatState::kShownHomeLauncher, GetShelfLayoutManager()->hotseat_state()); EXPECT_FALSE(app_views_moved()); // Finish the animation to shown state. ShellTestApi().WaitForWindowFinishAnimating( hotseat_widget->GetNativeWindow()); ASSERT_EQ(HotseatState::kShownHomeLauncher, GetShelfLayoutManager()->hotseat_state()); EXPECT_TRUE(app_views_moved()); } // Tests that hotseat bounds don't jump when the hotseat widget is translated // when a transitionj animation starts. TEST_P(HotseatWidgetTest, InitialAnimationPositionWithNonIdentityTransform) { TabletModeControllerTestApi().EnterTabletMode(); // Add an app to shelf - the app will be used to track the shelf view position // throughout the test. ShelfTestUtil::AddAppShortcut("fake_app", TYPE_PINNED_APP); // Open a window so the hotseat transitions to hidden state. std::unique_ptr window = AshTestBase::CreateTestWindow(gfx::Rect(0, 0, 400, 400)); wm::ActivateWindow(window.get()); // Make sure that all shelf item views complete their bounds animations // before starting the test (tests depend on the first item bounds within the // shelf view). auto* shelf_view = GetPrimaryShelf()->GetShelfViewForTesting(); ShelfViewTestAPI(shelf_view).RunMessageLoopUntilAnimationsDone(); HotseatWidget* const hotseat_widget = GetPrimaryShelf()->hotseat_widget(); gfx::Point last_app_views_position = hotseat_widget->GetWindowBoundsInScreen().origin(); // Returns whether the hotseat vertical position has changed comapred to // |last_hotseat_y|, and updates |last_hotseat_y| to match the current hotseat // position. auto app_views_moved = [&last_app_views_position, &hotseat_widget]() -> bool { gfx::Point app_views_position = ShelfViewTestAPI(hotseat_widget->scrollable_shelf_view()->shelf_view()) .GetViewAt(0) ->GetBoundsInScreen() .origin(); hotseat_widget->GetLayer()->transform().TransformPoint(&app_views_position); const bool position_changed = app_views_position != last_app_views_position; last_app_views_position = app_views_position; return position_changed; }; ui::test::EventGenerator* generator = GetEventGenerator(); const gfx::Rect display_bounds = display::Screen::GetScreen()->GetPrimaryDisplay().bounds(); // Drag upwards from the bottom of the screen to bring up hotseat - this // should request presentation time metric to be reported. generator->PressTouch(display_bounds.bottom_center()); // Drag the hotseat upward, to transition hotseat to the extended state. const int shelf_height = GetShelfWidget()->GetWindowBoundsInScreen().height(); generator->MoveTouchBy(0, -shelf_height); // Release touch, and verify the hotseat position remained the same. generator->ReleaseTouch(); ASSERT_TRUE(app_views_moved()); ui::ScopedAnimationDurationScaleMode animation_duration( ui::ScopedAnimationDurationScaleMode::NON_ZERO_DURATION); auto set_animated_transform = [](ui::Layer* layer, const gfx::Vector2d& initial_offset) { // Set translate animation on the hotseat widget, to simulate a state which // the widget may have while a transform animation is in progress. gfx::Transform initial_transform; initial_transform.Translate(initial_offset.x(), initial_offset.y()); layer->SetTransform(initial_transform); // Set up an animation to identity transform. ui::ScopedLayerAnimationSettings animation(layer->GetAnimator()); animation.SetTransitionDuration(base::Milliseconds(300)); layer->SetTransform(gfx::Transform()); }; set_animated_transform(hotseat_widget->GetLayer(), gfx::Vector2d(0, -10)); ASSERT_TRUE(app_views_moved()); // Tap in the middle of the screen to initiate transition to hidden state. generator->GestureTapAt(display_bounds.CenterPoint()); ASSERT_EQ(HotseatState::kHidden, GetShelfLayoutManager()->hotseat_state()); // Verify the current hotseat position remains unchanged. EXPECT_FALSE(app_views_moved()); // Wait for the hotseat animation to hidden state to finish. ShellTestApi().WaitForWindowFinishAnimating( hotseat_widget->GetNativeWindow()); ASSERT_EQ(HotseatState::kHidden, GetShelfLayoutManager()->hotseat_state()); EXPECT_TRUE(app_views_moved()); // Set the hotseat widget transform again. set_animated_transform(hotseat_widget->GetLayer(), gfx::Vector2d(0, -10)); ASSERT_TRUE(app_views_moved()); // CLose the window to transition to the shown state. window.reset(); ASSERT_EQ(HotseatState::kShownHomeLauncher, GetShelfLayoutManager()->hotseat_state()); // The apparent hotseat bound should remain the same as the transition to show // state starts. EXPECT_FALSE(app_views_moved()); // Finish the animation to shown state. ShellTestApi().WaitForWindowFinishAnimating( hotseat_widget->GetNativeWindow()); EXPECT_TRUE(app_views_moved()); // Open another widow, and move the hotseat to extended state. window = AshTestBase::CreateTestWindow(gfx::Rect(0, 0, 400, 400)); wm::ActivateWindow(window.get()); generator->PressTouch(display_bounds.bottom_center()); generator->MoveTouchBy(0, -shelf_height); generator->ReleaseTouch(); ShellTestApi().WaitForWindowFinishAnimating( hotseat_widget->GetNativeWindow()); ASSERT_EQ(HotseatState::kExtended, GetShelfLayoutManager()->hotseat_state()); set_animated_transform(hotseat_widget->GetLayer(), gfx::Vector2d(0, -10)); ASSERT_TRUE(app_views_moved()); // Close the window to transition to shown hotseat state. window.reset(); ASSERT_EQ(HotseatState::kShownHomeLauncher, GetShelfLayoutManager()->hotseat_state()); // The apparent hotseat bound should remain the same as the transition to show // state starts. EXPECT_FALSE(app_views_moved()); // Finish the animation to shown state. ShellTestApi().WaitForWindowFinishAnimating( hotseat_widget->GetNativeWindow()); EXPECT_TRUE(app_views_moved()); } TEST_P(HotseatWidgetTest, PresentationTimeMetricDuringDrag) { ui::PresentationTimeRecorder::SetReportPresentationTimeImmediatelyForTest( true); std::unique_ptr window = AshTestBase::CreateTestWindow(gfx::Rect(0, 0, 400, 400)); wm::ActivateWindow(window.get()); GetPrimaryShelf()->SetAutoHideBehavior(shelf_auto_hide_behavior()); TabletModeControllerTestApi().EnterTabletMode(); ui::test::EventGenerator* generator = GetEventGenerator(); const gfx::Rect display_bounds = display::Screen::GetScreen()->GetPrimaryDisplay().bounds(); // Drag upwards from the bottom of the screen to bring up hotseat - this // should request presentation time metric to be reported. generator->PressTouch(display_bounds.bottom_center()); HotseatWidget* const hotseat_widget = GetPrimaryShelf()->hotseat_widget(); int last_hotseat_y = hotseat_widget->GetWindowBoundsInScreen().y(); // Returns whether the hotseat vertical position has changed comapred to // |last_hotseat_y|, and updates |last_hotseat_y| to match the current hotseat // position. auto hotseat_moved = [&last_hotseat_y, &hotseat_widget]() -> bool { const int hotseat_y = hotseat_widget->GetWindowBoundsInScreen().y(); const bool y_changed = hotseat_y != last_hotseat_y; last_hotseat_y = hotseat_y; return y_changed; }; base::HistogramTester histogram_tester; auto check_bucket_size = [&histogram_tester](int expected_size) { histogram_tester.ExpectTotalCount( "Ash.HotseatTransition.Drag.PresentationTime", expected_size); histogram_tester.ExpectTotalCount( "Ash.HotseatTransition.Drag.PresentationTime.MaxLatency", 0); }; int expected_bucket_size = 0; { SCOPED_TRACE("Initial state"); check_bucket_size(expected_bucket_size); } const int shelf_height = GetShelfWidget()->GetWindowBoundsInScreen().height(); { SCOPED_TRACE("Upward drag with move - 1"); generator->MoveTouchBy(0, -shelf_height / 2); ASSERT_TRUE(hotseat_moved()); check_bucket_size(++expected_bucket_size); } { SCOPED_TRACE("Upward drag with move - 2"); generator->MoveTouchBy(0, -shelf_height / 2); ASSERT_TRUE(hotseat_moved()); check_bucket_size(++expected_bucket_size); } { SCOPED_TRACE("Downward drag with move"); generator->MoveTouchBy(0, shelf_height / 2); ASSERT_TRUE(hotseat_moved()); check_bucket_size(++expected_bucket_size); } { SCOPED_TRACE("Upward drag with move - 3"); generator->MoveTouchBy(0, -shelf_height / 2); ASSERT_TRUE(hotseat_moved()); check_bucket_size(++expected_bucket_size); } const int hotseat_height = GetPrimaryShelf()->hotseat_widget()->GetWindowBoundsInScreen().height(); { SCOPED_TRACE("Upward drag with move above shelf - 1"); generator->MoveTouchBy(0, -hotseat_height / 2); ASSERT_TRUE(hotseat_moved()); check_bucket_size(++expected_bucket_size); } { SCOPED_TRACE("Upward drag with move above shelf - 2"); generator->MoveTouchBy(0, -hotseat_height / 2); ASSERT_TRUE(hotseat_moved()); check_bucket_size(++expected_bucket_size); } { SCOPED_TRACE("Downward drag with move above shelf"); generator->MoveTouchBy(0, hotseat_height / 2); ASSERT_TRUE(hotseat_moved()); check_bucket_size(++expected_bucket_size); } { SCOPED_TRACE("Upward drag with move above shelf - 3"); generator->MoveTouchBy(0, -hotseat_height - hotseat_height / 2); ASSERT_TRUE(hotseat_moved()); check_bucket_size(++expected_bucket_size); } // Once the hotseat has been fully extended, presentation time metric should // stop being reported, as the hotseat is expected to stop moving. { SCOPED_TRACE("Upward drag without moving - 1"); generator->MoveTouchBy(0, -hotseat_height); ASSERT_FALSE(hotseat_moved()); check_bucket_size(expected_bucket_size); } { SCOPED_TRACE("Upward drag without moving - 2"); generator->MoveTouchBy(0, -hotseat_height / 2); ASSERT_FALSE(hotseat_moved()); check_bucket_size(expected_bucket_size); } // Move hotseat downwards - the presentation time should not get reported // until the hotseat starts moving downwards. { SCOPED_TRACE("Downward drag without moving - 1"); generator->MoveTouchBy(0, hotseat_height / 2); ASSERT_FALSE(hotseat_moved()); check_bucket_size(expected_bucket_size); } { SCOPED_TRACE("Downward drag without moving - 2"); generator->MoveTouchBy(0, hotseat_height); ASSERT_FALSE(hotseat_moved()); check_bucket_size(expected_bucket_size); } generator->ReleaseTouch(); window.reset(); { SCOPED_TRACE("Drag ended."); histogram_tester.ExpectTotalCount( "Ash.HotseatTransition.Drag.PresentationTime", expected_bucket_size); histogram_tester.ExpectTotalCount( "Ash.HotseatTransition.Drag.PresentationTime.MaxLatency", 1); } } // TODO(manucornet): Enable this test once the new API for layer animation // sequence observers is available. TEST_P(HotseatWidgetTest, DISABLED_OverviewToHomeAnimationAndBackIsSmooth) { // Go into tablet mode and make sure animations are over. HotseatWidget* hotseat = GetPrimaryShelf()->hotseat_widget(); views::WidgetAnimationWaiter waiter(hotseat); TabletModeControllerTestApi().EnterTabletMode(); waiter.WaitForAnimation(); ui::ScopedAnimationDurationScaleMode regular_animations( ui::ScopedAnimationDurationScaleMode::NORMAL_DURATION); // Go into overview and back to know what to expect in terms of bounds. const gfx::Rect shown_hotseat_bounds = hotseat->GetWindowBoundsInScreen(); { views::WidgetAnimationWaiter waiter(hotseat); StartOverview(); waiter.WaitForAnimation(); } const gfx::Rect extended_hotseat_bounds = hotseat->GetWindowBoundsInScreen(); { views::WidgetAnimationWaiter waiter(hotseat); EndOverview(); waiter.WaitForAnimation(); } // The extended hotseat should be higher (lower value of Y) than the // shown hotseat. EXPECT_GT(shown_hotseat_bounds.y(), extended_hotseat_bounds.y()); // We should start with the hotseat in its shown position again. EXPECT_EQ(shown_hotseat_bounds, hotseat->GetWindowBoundsInScreen()); { WidgetAnimationSmoothnessInspector inspector(hotseat); views::WidgetAnimationWaiter waiter(hotseat); StartOverview(); waiter.WaitForAnimation(); EXPECT_TRUE(inspector.CheckAnimation(4)); } // The hotseat should now be extended. EXPECT_EQ(extended_hotseat_bounds, hotseat->GetWindowBoundsInScreen()); { WidgetAnimationSmoothnessInspector inspector(hotseat); views::WidgetAnimationWaiter waiter(hotseat); EndOverview(); waiter.WaitForAnimation(); EXPECT_TRUE(inspector.CheckAnimation(4)); } // And we should now be back where we started. EXPECT_EQ(shown_hotseat_bounds, hotseat->GetWindowBoundsInScreen()); } class HotseatWidgetRTLTest : public ShelfLayoutManagerTestBase, public testing::WithParamInterface { public: // Use MOCK_TIME to increase number of commits during animation. HotseatWidgetRTLTest() : ShelfLayoutManagerTestBase( base::test::TaskEnvironment::TimeSource::MOCK_TIME), scoped_locale_(GetParam() ? "ar" : "") {} HotseatWidgetRTLTest(const HotseatWidgetRTLTest&) = delete; HotseatWidgetRTLTest& operator=(const HotseatWidgetRTLTest&) = delete; ~HotseatWidgetRTLTest() override = default; private: base::test::ScopedRestoreICUDefaultLocale scoped_locale_; }; INSTANTIATE_TEST_SUITE_P(RTL, HotseatWidgetRTLTest, testing::Bool()); // The test to verify the hotseat transition animation from the extended state // to the home launcher state. // TODO(https://crbug.com/1210530): Disable this test due to flakiness. TEST_P(HotseatWidgetRTLTest, DISABLED_VerifyTransitionFromExtendedModeToHomeLauncher) { TabletModeControllerTestApi().EnterTabletMode(); const auto app_id = ShelfTestUtil::AddAppShortcut("fake_app", TYPE_PINNED_APP); // Open a window so the hotseat transitions to hidden state. std::unique_ptr window = AshTestBase::CreateTestWindow(gfx::Rect(0, 0, 400, 400)); wm::ActivateWindow(window.get()); // Swipe the hotseat up to enter the extended mode. SwipeUpOnShelf(); EXPECT_EQ(HotseatState::kExtended, GetShelfLayoutManager()->hotseat_state()); // Animation should be long enough in order to collect sufficient data. // TODO(https://crbug.com/1208651): remove this line when we solve that issue. ui::ScopedAnimationDurationScaleMode animation_duration( ui::ScopedAnimationDurationScaleMode::NORMAL_DURATION); // Wait until shelf animation completes. auto* shelf_view = GetPrimaryShelf()->GetShelfViewForTesting(); ShelfViewTestAPI shelf_test_api(shelf_view); shelf_test_api.RunMessageLoopUntilAnimationsDone(); // Observe a shelf icon. auto* observed_view = shelf_view->GetShelfAppButton(app_id.id); LayerAnimationVerifier verifier( GetPrimaryShelf()->hotseat_widget()->GetNativeView()->layer(), observed_view); // Transit the hotseat from the extended state to the home launcher state. // Wait until the transition animation finishes. views::WidgetAnimationWaiter waiter(GetPrimaryShelf()->hotseat_widget()); FlingUpOnShelf(); waiter.WaitForAnimation(); // Verify the hotseat state at the end of the animation. EXPECT_EQ(HotseatState::kShownHomeLauncher, GetShelfLayoutManager()->hotseat_state()); } } // namespace ash