// Copyright 2020 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "ash/app_list/test/app_list_test_helper.h" #include "ash/constants/ash_features.h" #include "ash/constants/ash_pref_names.h" #include "ash/controls/contextual_tooltip.h" #include "ash/public/cpp/shelf_types.h" #include "ash/session/session_controller_impl.h" #include "ash/shelf/drag_handle.h" #include "ash/shelf/drag_window_from_shelf_controller.h" #include "ash/shelf/drag_window_from_shelf_controller_test_api.h" #include "ash/shelf/shelf_layout_manager.h" #include "ash/shelf/test/shelf_layout_manager_test_base.h" #include "ash/shell.h" #include "ash/test/ash_test_base.h" #include "ash/wm/tablet_mode/tablet_mode_controller_test_api.h" #include "base/bind.h" #include "base/test/metrics/histogram_tester.h" #include "base/test/scoped_feature_list.h" #include "base/test/simple_test_clock.h" #include "ui/compositor/scoped_animation_duration_scale_mode.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 // Test base for unit test related to drag handle contextual nudges. class DragHandleContextualNudgeTest : public ShelfLayoutManagerTestBase { public: DragHandleContextualNudgeTest() { scoped_feature_list_.InitWithFeatures( {ash::features::kContextualNudges, ash::features::kHideShelfControlsInTabletMode}, {}); } ~DragHandleContextualNudgeTest() override = default; DragHandleContextualNudgeTest(const DragHandleContextualNudgeTest& other) = delete; DragHandleContextualNudgeTest& operator=( const DragHandleContextualNudgeTest& other) = delete; // ShelfLayoutManagerTestBase: void SetUp() override { ShelfLayoutManagerTestBase::SetUp(); test_clock_.Advance(base::Hours(2)); contextual_tooltip::OverrideClockForTesting(&test_clock_); } void TearDown() override { contextual_tooltip::ClearClockOverrideForTesting(); AshTestBase::TearDown(); } base::SimpleTestClock test_clock_; private: base::test::ScopedFeatureList scoped_feature_list_; }; class DragHandleContextualNudgeTestA11yPrefs : public DragHandleContextualNudgeTest, public ::testing::WithParamInterface {}; INSTANTIATE_TEST_SUITE_P( All, DragHandleContextualNudgeTestA11yPrefs, testing::Values(prefs::kAccessibilityAutoclickEnabled, prefs::kAccessibilitySpokenFeedbackEnabled, prefs::kAccessibilitySwitchAccessEnabled)); TEST_F(DragHandleContextualNudgeTest, ShowDragHandleNudgeWithTimer) { // Creates a widget to put shelf into in-app state. views::Widget* widget = CreateTestWidget(); widget->Maximize(); TabletModeControllerTestApi().EnterTabletMode(); EXPECT_EQ(ShelfBackgroundType::kInApp, GetShelfWidget()->GetBackgroundType()); // The drag handle should be showing but the nudge should not. A timer to show // the nudge should be initialized. EXPECT_TRUE(GetShelfWidget()->GetDragHandle()->GetVisible()); EXPECT_FALSE( GetShelfWidget()->GetDragHandle()->gesture_nudge_target_visibility()); // Firing the timer should show the drag handle nudge. GetShelfWidget()->GetDragHandle()->fire_show_drag_handle_timer_for_testing(); EXPECT_TRUE(GetShelfWidget()->GetDragHandle()->GetVisible()); EXPECT_TRUE( GetShelfWidget()->GetDragHandle()->gesture_nudge_target_visibility()); } TEST_F(DragHandleContextualNudgeTest, HideDragHandleNudgeHiddenOnMinimize) { base::HistogramTester histogram_tester; // Creates a test window to put shelf into in-app state. views::Widget* widget = CreateTestWidget(); widget->Maximize(); TabletModeControllerTestApi().EnterTabletMode(); EXPECT_EQ(ShelfBackgroundType::kInApp, GetShelfWidget()->GetBackgroundType()); // The drag handle and nudge should be showing after the timer fires. GetShelfWidget()->GetDragHandle()->fire_show_drag_handle_timer_for_testing(); EXPECT_TRUE(GetShelfWidget()->GetDragHandle()->GetVisible()); EXPECT_TRUE( GetShelfWidget()->GetDragHandle()->gesture_nudge_target_visibility()); // Minimizing the widget should hide the drag handle and nudge. widget->Minimize(); EXPECT_FALSE(GetShelfWidget()->GetDragHandle()->GetVisible()); EXPECT_FALSE( GetShelfWidget()->GetDragHandle()->gesture_nudge_target_visibility()); } // Tests that the drag handle nudge nudge is hidden when closing the widget and // setting the ShelfBackgroundType to kHomeLauncher. TEST_F(DragHandleContextualNudgeTest, DragHandleNudgeHiddenOnClose) { // Creates a widget to put shelf into in-app state. views::Widget* widget = CreateTestWidget(); widget->Maximize(); TabletModeControllerTestApi().EnterTabletMode(); EXPECT_EQ(ShelfBackgroundType::kInApp, GetShelfWidget()->GetBackgroundType()); DragHandle* const drag_handle = GetShelfWidget()->GetDragHandle(); ASSERT_TRUE(drag_handle->has_show_drag_handle_timer_for_testing()); drag_handle->fire_show_drag_handle_timer_for_testing(); EXPECT_TRUE(drag_handle->gesture_nudge_target_visibility()); // Close the widget. widget->CloseWithReason(views::Widget::ClosedReason::kCloseButtonClicked); EXPECT_FALSE(drag_handle->GetVisible()); EXPECT_FALSE(drag_handle->gesture_nudge_target_visibility()); } // Checks that the shelf cannot be auto hidden while animating shelf drag handle // nudge. TEST_F(DragHandleContextualNudgeTest, HideDragHandleDoesNotInteruptShowNudgeAnimation) { GetPrimaryShelf()->SetAutoHideBehavior(ShelfAutoHideBehavior::kAlways); // Creates a widget to put shelf into in-app state. views::Widget* widget = CreateTestWidget(); widget->Maximize(); TabletModeControllerTestApi().EnterTabletMode(); EXPECT_EQ(ShelfBackgroundType::kInApp, GetShelfWidget()->GetBackgroundType()); ui::ScopedAnimationDurationScaleMode normal_animation_duration( ui::ScopedAnimationDurationScaleMode::SLOW_DURATION); GetShelfWidget()->GetDragHandle()->MaybeShowDragHandleNudge(); EXPECT_TRUE(GetShelfWidget()->GetDragHandle()->GetVisible()); EXPECT_TRUE( GetShelfWidget()->GetDragHandle()->show_nudge_animation_in_progress()); GetShelfLayoutManager()->UpdateAutoHideState(); // Shelf auto hide should not interrupt animations to show drag handle nudge. // Showing the nudge while hiding the shelf is not intended behavior. EXPECT_TRUE(GetShelfWidget()->GetDragHandle()->GetVisible()); EXPECT_TRUE( GetShelfWidget()->GetDragHandle()->show_nudge_animation_in_progress()); } // Checks that the drag handle nudge is not shown when entering kInApp with // shelf autohide turned on. TEST_F(DragHandleContextualNudgeTest, DragHandleNotShownForAutoHideShelf) { GetPrimaryShelf()->SetAutoHideBehavior(ShelfAutoHideBehavior::kAlways); // Creates a widget to put shelf into in-app state. views::Widget* widget = CreateTestWidget(); widget->Maximize(); TabletModeControllerTestApi().EnterTabletMode(); EXPECT_FALSE( GetShelfWidget()->GetDragHandle()->show_nudge_animation_in_progress()); EXPECT_FALSE( GetShelfWidget()->GetDragHandle()->gesture_nudge_target_visibility()); } TEST_F(DragHandleContextualNudgeTest, DoNotShowNudgeWithoutDragHandle) { // Creates a widget to put shelf into in-app state. views::Widget* widget = CreateTestWidget(); widget->Maximize(); TabletModeControllerTestApi().EnterTabletMode(); EXPECT_EQ(ShelfBackgroundType::kInApp, GetShelfWidget()->GetBackgroundType()); // Minimizing the widget should hide the drag handle and nudge. widget->Minimize(); EXPECT_FALSE(GetShelfWidget()->GetDragHandle()->GetVisible()); EXPECT_FALSE( GetShelfWidget()->GetDragHandle()->gesture_nudge_target_visibility()); } TEST_F(DragHandleContextualNudgeTest, ContinueShowingDragHandleNudgeOnActiveWidgetChanged) { // Creates a widget to put shelf into in-app state. views::Widget* widget = CreateTestWidget(); widget->Maximize(); TabletModeControllerTestApi().EnterTabletMode(); EXPECT_EQ(ShelfBackgroundType::kInApp, GetShelfWidget()->GetBackgroundType()); GetShelfWidget()->GetDragHandle()->fire_show_drag_handle_timer_for_testing(); EXPECT_TRUE(GetShelfWidget()->GetDragHandle()->GetVisible()); EXPECT_TRUE( GetShelfWidget()->GetDragHandle()->gesture_nudge_target_visibility()); // Maximizing and showing a different widget should not hide the drag handle // or nudge. views::Widget* new_widget = CreateTestWidget(); new_widget->Maximize(); EXPECT_TRUE(GetShelfWidget()->GetDragHandle()->GetVisible()); EXPECT_TRUE( GetShelfWidget()->GetDragHandle()->gesture_nudge_target_visibility()); } TEST_F(DragHandleContextualNudgeTest, DragHandleNudgeShownInAppShelf) { base::HistogramTester histogram_tester; // Creates a widget to put shelf into in-app state. views::Widget* widget = CreateTestWidget(); widget->Maximize(); // Drag handle and nudge should not be shown in clamshell mode. EXPECT_FALSE(GetShelfWidget()->GetDragHandle()->GetVisible()); EXPECT_FALSE( GetShelfWidget()->GetDragHandle()->gesture_nudge_target_visibility()); // Test that the first time a user transitions into tablet mode with a // maximized window will show the drag nudge immedietly. The drag handle nudge // should not be visible yet and the timer to show it should be set. TabletModeControllerTestApi().EnterTabletMode(); EXPECT_EQ(ShelfBackgroundType::kInApp, GetShelfWidget()->GetBackgroundType()); EXPECT_TRUE(GetShelfWidget()->GetDragHandle()->GetVisible()); EXPECT_FALSE( GetShelfWidget()->GetDragHandle()->gesture_nudge_target_visibility()); EXPECT_TRUE(GetShelfWidget() ->GetDragHandle() ->has_show_drag_handle_timer_for_testing()); // Firing the timer should show the nudge for the first time. The nudge should // remain visible until the shelf state changes so the timer to hide it should // not be set. GetShelfWidget()->GetDragHandle()->fire_show_drag_handle_timer_for_testing(); EXPECT_TRUE( GetShelfWidget()->GetDragHandle()->gesture_nudge_target_visibility()); EXPECT_FALSE(GetShelfWidget() ->GetDragHandle() ->has_hide_drag_handle_timer_for_testing()); // Leaving tablet mode should hide the nudge. TabletModeControllerTestApi().LeaveTabletMode(); EXPECT_FALSE(GetShelfWidget()->GetDragHandle()->GetVisible()); EXPECT_FALSE( GetShelfWidget()->GetDragHandle()->gesture_nudge_target_visibility()); // Reentering tablet mode should show the drag handle but the nudge should // not. No timer should be set to show the nudge. TabletModeControllerTestApi().EnterTabletMode(); EXPECT_TRUE(GetShelfWidget()->GetDragHandle()->GetVisible()); EXPECT_FALSE( GetShelfWidget()->GetDragHandle()->gesture_nudge_target_visibility()); EXPECT_FALSE(GetShelfWidget() ->GetDragHandle() ->has_show_drag_handle_timer_for_testing()); // Advance time for more than a day (which should enable the nudge again). test_clock_.Advance(base::Hours(25)); // Reentering tablet mode with a maximized widget should immedietly show the // drag handle and set a timer to show the nudge. TabletModeControllerTestApi().LeaveTabletMode(); TabletModeControllerTestApi().EnterTabletMode(); EXPECT_TRUE(GetShelfWidget()->GetDragHandle()->GetVisible()); EXPECT_FALSE( GetShelfWidget()->GetDragHandle()->gesture_nudge_target_visibility()); // Firing the timer should show the nudge. EXPECT_TRUE(GetShelfWidget() ->GetDragHandle() ->has_show_drag_handle_timer_for_testing()); GetShelfWidget()->GetDragHandle()->fire_show_drag_handle_timer_for_testing(); EXPECT_TRUE( GetShelfWidget()->GetDragHandle()->gesture_nudge_target_visibility()); EXPECT_FALSE(GetShelfWidget() ->GetDragHandle() ->has_show_drag_handle_timer_for_testing()); // On subsequent shows, the nudge should be hidden after a timeout. EXPECT_TRUE(GetShelfWidget() ->GetDragHandle() ->has_hide_drag_handle_timer_for_testing()); } TEST_F(DragHandleContextualNudgeTest, DragHandleNudgeShownOnTap) { // Creates a widget to put shelf into in-app state. views::Widget* widget = CreateTestWidget(); widget->Maximize(); TabletModeControllerTestApi().EnterTabletMode(); EXPECT_EQ(ShelfBackgroundType::kInApp, GetShelfWidget()->GetBackgroundType()); EXPECT_TRUE(GetShelfWidget()->GetDragHandle()->GetVisible()); EXPECT_FALSE( GetShelfWidget()->GetDragHandle()->gesture_nudge_target_visibility()); EXPECT_TRUE(GetShelfWidget() ->GetDragHandle() ->has_show_drag_handle_timer_for_testing()); GetShelfWidget()->GetDragHandle()->fire_show_drag_handle_timer_for_testing(); EXPECT_TRUE( GetShelfWidget()->GetDragHandle()->gesture_nudge_target_visibility()); // Exiting and re-entering tablet should hide the nudge and put the shelf into // the default kInApp shelf state. TabletModeControllerTestApi().LeaveTabletMode(); TabletModeControllerTestApi().EnterTabletMode(); EXPECT_TRUE(GetShelfWidget()->GetDragHandle()->GetVisible()); EXPECT_FALSE( GetShelfWidget()->GetDragHandle()->gesture_nudge_target_visibility()); // Tapping the drag handle should show the drag handle nudge immedietly and // the show nudge timer should be set. GetEventGenerator()->GestureTapAt( GetShelfWidget()->GetDragHandle()->GetBoundsInScreen().CenterPoint()); EXPECT_FALSE(GetShelfWidget() ->GetDragHandle() ->has_show_drag_handle_timer_for_testing()); EXPECT_TRUE(GetShelfWidget()->GetDragHandle()->GetVisible()); EXPECT_TRUE( GetShelfWidget()->GetDragHandle()->gesture_nudge_target_visibility()); EXPECT_TRUE(GetShelfWidget() ->GetDragHandle() ->has_hide_drag_handle_timer_for_testing()); } TEST_F(DragHandleContextualNudgeTest, DragHandleNudgeNotShownForHiddenShelf) { GetPrimaryShelf()->SetAutoHideBehavior(ShelfAutoHideBehavior::kAlways); TabletModeControllerTestApi().EnterTabletMode(); // Creates a widget to put shelf into in-app state. views::Widget* widget = CreateTestWidget(); widget->Maximize(); ShelfWidget* const shelf_widget = GetShelfWidget(); DragHandle* const drag_handle = shelf_widget->GetDragHandle(); // The shelf is hidden, so the drag handle nudge should not be shown. EXPECT_TRUE(drag_handle->GetVisible()); EXPECT_FALSE(drag_handle->gesture_nudge_target_visibility()); EXPECT_FALSE(drag_handle->has_show_drag_handle_timer_for_testing()); PrefService* const prefs = Shell::Get()->session_controller()->GetLastActiveUserPrefService(); // Back gesture nudge should be allowed if the shelf is hidden. EXPECT_TRUE(contextual_tooltip::ShouldShowNudge( prefs, contextual_tooltip::TooltipType::kBackGesture, nullptr)); // Swipe up to show the shelf - this should schedule the drag handle nudge. SwipeUpOnShelf(); // Back gesture nudge should be disallowed at this time, given that the drag // handle nudge can be shown. EXPECT_FALSE(contextual_tooltip::ShouldShowNudge( prefs, contextual_tooltip::TooltipType::kBackGesture, nullptr)); ASSERT_TRUE(drag_handle->has_show_drag_handle_timer_for_testing()); drag_handle->fire_show_drag_handle_timer_for_testing(); EXPECT_TRUE(drag_handle->gesture_nudge_target_visibility()); } // Tapping the drag handle nudge when auto hide shelf is enabled should hide the // drag handle nudge but should not hide the shelf or hotseat. TEST_F(DragHandleContextualNudgeTest, DragHandleNudgeTapDoesNotHideAutoHiddenShelf) { // Sets shelf auto hide behavior. GetPrimaryShelf()->SetAutoHideBehavior(ShelfAutoHideBehavior::kAlways); TabletModeControllerTestApi().EnterTabletMode(); // Creates a widget to put shelf into in-app state. views::Widget* widget = CreateTestWidget(); widget->Maximize(); ShelfWidget* const shelf_widget = GetShelfWidget(); DragHandle* const drag_handle = shelf_widget->GetDragHandle(); // The shelf and drag handle should be hidden and the nudge should not be // scheduled because shelf auto hide is set. EXPECT_TRUE(GetPrimaryShelf()->GetAutoHideState() == ShelfAutoHideState::SHELF_AUTO_HIDE_HIDDEN); EXPECT_FALSE(drag_handle->gesture_nudge_target_visibility()); EXPECT_FALSE(drag_handle->has_show_drag_handle_timer_for_testing()); // Swipe up to show the shelf. This should show the shelf, extend the hotseat, // and schedule the drag handle nudge. SwipeUpOnShelf(); EXPECT_TRUE(GetPrimaryShelf()->GetAutoHideState() == ShelfAutoHideState::SHELF_AUTO_HIDE_SHOWN); EXPECT_EQ(HotseatState::kExtended, GetShelfLayoutManager()->hotseat_state()); ASSERT_TRUE(drag_handle->has_show_drag_handle_timer_for_testing()); // Firing the show timer should create the nudge set the target visibility for // animations.. drag_handle->fire_show_drag_handle_timer_for_testing(); EXPECT_TRUE(drag_handle->gesture_nudge_target_visibility()); EXPECT_TRUE(drag_handle->drag_handle_nudge() != nullptr); // Tapping the drag handle nudge should hide the nudge but not affect the // visibility of the shelf or hotseat. GetEventGenerator()->GestureTapAt( drag_handle->drag_handle_nudge()->GetBoundsInScreen().CenterPoint()); EXPECT_FALSE(drag_handle->gesture_nudge_target_visibility()); EXPECT_TRUE(GetPrimaryShelf()->GetAutoHideState() == ShelfAutoHideState::SHELF_AUTO_HIDE_SHOWN); EXPECT_EQ(HotseatState::kExtended, GetShelfLayoutManager()->hotseat_state()); // Swiping down on shelf should hide the shelf and hotseat. SwipeDownOnShelf(); EXPECT_EQ(HotseatState::kHidden, GetShelfLayoutManager()->hotseat_state()); EXPECT_TRUE(GetPrimaryShelf()->GetAutoHideState() == ShelfAutoHideState::SHELF_AUTO_HIDE_HIDDEN); // Swiping up on shelf should show the shelf and drag handle but not the // nudge or hotseat. SwipeUpOnShelf(); EXPECT_TRUE(GetPrimaryShelf()->GetAutoHideState() == ShelfAutoHideState::SHELF_AUTO_HIDE_SHOWN); EXPECT_FALSE(drag_handle->has_show_drag_handle_timer_for_testing()); EXPECT_TRUE(drag_handle->drag_handle_nudge() == nullptr); EXPECT_EQ(HotseatState::kExtended, GetShelfLayoutManager()->hotseat_state()); } // Tests that drag handle show is canceled when the shelf is hidden while the // drag handle is scheduled to be shown. TEST_F(DragHandleContextualNudgeTest, HidingShelfCancelsDragHandleShow) { GetPrimaryShelf()->SetAutoHideBehavior(ShelfAutoHideBehavior::kAlways); TabletModeControllerTestApi().EnterTabletMode(); // Creates a widget to put shelf into in-app state. views::Widget* widget = CreateTestWidget(); widget->Maximize(); ShelfWidget* const shelf_widget = GetShelfWidget(); DragHandle* const drag_handle = shelf_widget->GetDragHandle(); // The shelf is hidden, so the drag handle nudge should not be shown. EXPECT_TRUE(drag_handle->GetVisible()); EXPECT_FALSE(drag_handle->gesture_nudge_target_visibility()); EXPECT_FALSE(drag_handle->has_show_drag_handle_timer_for_testing()); // Swipe up to show the shelf - this should schedule the drag handle nudge. SwipeUpOnShelf(); EXPECT_TRUE(drag_handle->has_show_drag_handle_timer_for_testing()); // Hide the shelf, and verify the drag handle show is canceled. SwipeDownOnShelf(); EXPECT_FALSE(drag_handle->has_show_drag_handle_timer_for_testing()); EXPECT_FALSE(drag_handle->gesture_nudge_target_visibility()); PrefService* const prefs = Shell::Get()->session_controller()->GetLastActiveUserPrefService(); // Back gesture nudge should be allowed if the shelf is hidden. EXPECT_TRUE(contextual_tooltip::ShouldShowNudge( prefs, contextual_tooltip::TooltipType::kBackGesture, nullptr)); } // Tests that the drag handle nudge is not hidden when the user extends the // hotseat. TEST_F(DragHandleContextualNudgeTest, DragHandleNudgeNotHiddenByExtendingHotseat) { TabletModeControllerTestApi().EnterTabletMode(); // Creates a widget to put shelf into in-app state. views::Widget* widget = CreateTestWidget(); widget->Maximize(); ShelfWidget* const shelf_widget = GetShelfWidget(); DragHandle* const drag_handle = shelf_widget->GetDragHandle(); ASSERT_TRUE(drag_handle->has_show_drag_handle_timer_for_testing()); drag_handle->fire_show_drag_handle_timer_for_testing(); EXPECT_TRUE(drag_handle->gesture_nudge_target_visibility()); // Swipe up to extend the hotseat - verify that the drag handle remain // visible. SwipeUpOnShelf(); EXPECT_TRUE(drag_handle->GetVisible()); EXPECT_TRUE(drag_handle->gesture_nudge_target_visibility()); } // Tests that the drag handle nudge is horizontally centered in screen, and // drawn above the shelf drag handle, even after display bounds are updated. TEST_F(DragHandleContextualNudgeTest, DragHandleNudgeBoundsInScreen) { UpdateDisplay("675x1200"); TabletModeControllerTestApi().EnterTabletMode(); views::Widget* widget = CreateTestWidget(); widget->Maximize(); ShelfWidget* const shelf_widget = GetShelfWidget(); DragHandle* const drag_handle = shelf_widget->GetDragHandle(); EXPECT_TRUE(drag_handle->GetVisible()); ASSERT_TRUE(drag_handle->has_show_drag_handle_timer_for_testing()); drag_handle->fire_show_drag_handle_timer_for_testing(); EXPECT_TRUE(drag_handle->gesture_nudge_target_visibility()); // Calculates absolute difference between horizontal margins of |inner| rect // within |outer| rect. auto margin_diff = [](const gfx::Rect& inner, const gfx::Rect& outer) -> int { const int left = inner.x() - outer.x(); EXPECT_GE(left, 0); const int right = outer.right() - inner.right(); EXPECT_GE(right, 0); return std::abs(left - right); }; // Verify that nudge widget is centered in shelf. gfx::Rect shelf_bounds = shelf_widget->GetWindowBoundsInScreen(); gfx::Rect nudge_bounds = drag_handle->drag_handle_nudge()->label()->GetBoundsInScreen(); EXPECT_LE(margin_diff(nudge_bounds, shelf_bounds), 1); // Verify that the nudge vertical bounds - within the shelf bounds, and above // the drag handle. gfx::Rect drag_handle_bounds = drag_handle->GetBoundsInScreen(); EXPECT_LE(shelf_bounds.y(), nudge_bounds.y()); EXPECT_LE(nudge_bounds.bottom(), drag_handle_bounds.y()); // Change the display bounds, and verify the updated drag handle bounds. UpdateDisplay("1200x675"); EXPECT_TRUE( GetShelfWidget()->GetDragHandle()->gesture_nudge_target_visibility()); // Verify that nudge widget is centered in shelf. shelf_bounds = shelf_widget->GetWindowBoundsInScreen(); nudge_bounds = drag_handle->drag_handle_nudge()->label()->GetBoundsInScreen(); EXPECT_LE(margin_diff(nudge_bounds, shelf_bounds), 1); // Verify that the nudge vertical bounds - within the shelf bounds, and above // the drag handle. drag_handle_bounds = drag_handle->GetBoundsInScreen(); EXPECT_LE(shelf_bounds.y(), nudge_bounds.y()); EXPECT_LE(nudge_bounds.bottom(), drag_handle_bounds.y()); } // Tests that drag handle does not hide during the window drag from shelf // gesture. TEST_F(DragHandleContextualNudgeTest, DragHandleNudgeNotHiddenDuringWindowDragFromShelf) { TabletModeControllerTestApi().EnterTabletMode(); // Creates a widget to put shelf into in-app state. views::Widget* widget = CreateTestWidget(); widget->Maximize(); ShelfWidget* const shelf_widget = GetShelfWidget(); DragHandle* const drag_handle = shelf_widget->GetDragHandle(); ASSERT_TRUE(drag_handle->has_show_drag_handle_timer_for_testing()); drag_handle->fire_show_drag_handle_timer_for_testing(); EXPECT_TRUE(drag_handle->gesture_nudge_target_visibility()); TabletModeControllerTestApi().LeaveTabletMode(); // Advance time for more than a day (which should enable the nudge again). test_clock_.Advance(base::Hours(25)); TabletModeControllerTestApi().EnterTabletMode(); EXPECT_TRUE(drag_handle->has_show_drag_handle_timer_for_testing()); drag_handle->fire_show_drag_handle_timer_for_testing(); EXPECT_TRUE(drag_handle->has_hide_drag_handle_timer_for_testing()); const gfx::Point start = drag_handle->GetBoundsInScreen().CenterPoint(); // Simulates window drag from shelf gesture, and verifies that the timer to // hide the drag handle nudge is canceled when the window drag from shelf // starts. GetEventGenerator()->GestureScrollSequenceWithCallback( start, start + gfx::Vector2d(0, -200), base::Milliseconds(50), /*num_steps = */ 6, base::BindRepeating( [](DragHandle* drag_handle, ui::EventType type, const gfx::Vector2dF& offset) { DragWindowFromShelfController* window_drag_controller = GetShelfLayoutManager()->window_drag_controller_for_testing(); if (window_drag_controller && window_drag_controller->dragged_window()) { EXPECT_FALSE( drag_handle->has_hide_drag_handle_timer_for_testing()); } const bool scroll_end = type == ui::ET_GESTURE_SCROLL_END; EXPECT_EQ(!scroll_end, drag_handle->gesture_nudge_target_visibility()); }, drag_handle)); // The nudge should be hidden when the gesture completes. EXPECT_FALSE(drag_handle->gesture_nudge_target_visibility()); } // Tests that window drag from shelf cancels drag handle contextual nudge from // showing. TEST_F(DragHandleContextualNudgeTest, DragHandleNudgeNotShownDuringWindowDragFromShelf) { TabletModeControllerTestApi().EnterTabletMode(); // Creates a widget to put shelf into in-app state. views::Widget* widget = CreateTestWidget(); widget->Maximize(); ShelfWidget* const shelf_widget = GetShelfWidget(); DragHandle* const drag_handle = shelf_widget->GetDragHandle(); EXPECT_TRUE(drag_handle->has_show_drag_handle_timer_for_testing()); const gfx::Point start = GetShelfWidget()->GetWindowBoundsInScreen().CenterPoint(); // Simulates window drag from shelf gesture, and verifies that the timer to // show the drag handle nudge is canceled when the window drag from shelf // starts. GetEventGenerator()->GestureScrollSequenceWithCallback( start, start + gfx::Vector2d(0, -200), base::Milliseconds(50), /*num_steps = */ 6, base::BindRepeating( [](DragHandle* drag_handle, ui::EventType type, const gfx::Vector2dF& offset) { DragWindowFromShelfController* window_drag_controller = GetShelfLayoutManager()->window_drag_controller_for_testing(); if (window_drag_controller && window_drag_controller->dragged_window()) { EXPECT_FALSE( drag_handle->has_show_drag_handle_timer_for_testing()); // Attempt to schedule the nudge should fail. if (type != ui::ET_GESTURE_SCROLL_END) { drag_handle->ScheduleShowDragHandleNudge(); EXPECT_FALSE( drag_handle->has_show_drag_handle_timer_for_testing()); } } EXPECT_FALSE(drag_handle->gesture_nudge_target_visibility()); }, drag_handle)); // The nudge should be hidden when the gesture completes. EXPECT_FALSE(drag_handle->gesture_nudge_target_visibility()); } TEST_F(DragHandleContextualNudgeTest, GestureSwipeHidesDragHandleNudge) { base::HistogramTester histogram_tester; TabletModeControllerTestApi().EnterTabletMode(); // Creates a widget to put shelf into in-app state. views::Widget* widget = CreateTestWidget(); widget->Maximize(); ShelfWidget* const shelf_widget = GetShelfWidget(); DragHandle* const drag_handle = shelf_widget->GetDragHandle(); ASSERT_TRUE(drag_handle->has_show_drag_handle_timer_for_testing()); drag_handle->fire_show_drag_handle_timer_for_testing(); EXPECT_TRUE(drag_handle->gesture_nudge_target_visibility()); const gfx::Point start = drag_handle->GetBoundsInScreen().CenterPoint(); // Simulates a swipe up from the drag handle to perform the in app to home // gesture. GetEventGenerator()->GestureScrollSequence( start, start + gfx::Vector2d(0, -300), base::Milliseconds(10), /*num_steps = */ 5); // The nudge should be hidden when the gesture completes. EXPECT_FALSE(drag_handle->gesture_nudge_target_visibility()); GetAppListTestHelper()->CheckVisibility(true); } // Tests that drag handle nudge gets hidden when the user performs window drag // from shelf to home. TEST_F(DragHandleContextualNudgeTest, FlingFromShelfToHomeHidesTheNudge) { TabletModeControllerTestApi().EnterTabletMode(); // Creates a widget to put shelf into in-app state. views::Widget* widget = CreateTestWidget(); widget->Maximize(); ShelfWidget* const shelf_widget = GetShelfWidget(); DragHandle* const drag_handle = shelf_widget->GetDragHandle(); ASSERT_TRUE(drag_handle->has_show_drag_handle_timer_for_testing()); drag_handle->fire_show_drag_handle_timer_for_testing(); EXPECT_TRUE(drag_handle->gesture_nudge_target_visibility()); const gfx::Point start = drag_handle->GetBoundsInScreen().CenterPoint(); // Simulates window drag from shelf gesture, and verifies that the timer to // hide the drag handle nudge is canceled when the window drag from shelf // starts. GetEventGenerator()->GestureScrollSequenceWithCallback( start, start + gfx::Vector2d(0, -300), base::Milliseconds(10), /*num_steps = */ 6, base::BindRepeating( [](DragHandle* drag_handle, ui::EventType type, const gfx::Vector2dF& offset) { const bool scroll_end = type == ui::ET_GESTURE_SCROLL_END; EXPECT_EQ(!scroll_end, drag_handle->gesture_nudge_target_visibility()); }, drag_handle)); // The nudge should be hidden when the gesture completes. EXPECT_FALSE(drag_handle->gesture_nudge_target_visibility()); GetAppListTestHelper()->CheckVisibility(true); } // Tests that drag handle nudge gets hidden when the user performs window drag // from shelf, even if the gesture does not end up going home. TEST_F(DragHandleContextualNudgeTest, DragFromShelfToHomeHidesTheNudge) { TabletModeControllerTestApi().EnterTabletMode(); // Creates a widget to put shelf into in-app state. views::Widget* widget = CreateTestWidget(); widget->Maximize(); ShelfWidget* const shelf_widget = GetShelfWidget(); DragHandle* const drag_handle = shelf_widget->GetDragHandle(); ASSERT_TRUE(drag_handle->has_show_drag_handle_timer_for_testing()); drag_handle->fire_show_drag_handle_timer_for_testing(); EXPECT_TRUE(drag_handle->gesture_nudge_target_visibility()); const gfx::Point start = drag_handle->GetBoundsInScreen().CenterPoint(); // Simulates window drag from shelf gesture, and verifies that the timer to // hide the drag handle nudge is canceled when the window drag from shelf // starts. GetEventGenerator()->GestureScrollSequenceWithCallback( start, start + gfx::Vector2d(0, -150), base::Milliseconds(500), /*num_steps = */ 20, base::BindRepeating( [](DragHandle* drag_handle, ui::EventType type, const gfx::Vector2dF& offset) { DragWindowFromShelfController* window_drag_controller = GetShelfLayoutManager()->window_drag_controller_for_testing(); if (window_drag_controller && window_drag_controller->dragged_window()) { DragWindowFromShelfControllerTestApi().WaitUntilOverviewIsShown( window_drag_controller); } const bool scroll_end = type == ui::ET_GESTURE_SCROLL_END; EXPECT_EQ(!scroll_end, drag_handle->gesture_nudge_target_visibility()); }, drag_handle)); // The nudge should be hidden when the gesture completes. EXPECT_FALSE(drag_handle->gesture_nudge_target_visibility()); GetAppListTestHelper()->CheckVisibility(false); EXPECT_TRUE(Shell::Get()->overview_controller()->InOverviewSession()); } // Tests that scheduled drag handle nudge show is canceled when overview starts. TEST_F(DragHandleContextualNudgeTest, OverviewCancelsNudgeShow) { TabletModeControllerTestApi().EnterTabletMode(); // Creates a widget to put shelf into in-app state. views::Widget* widget = CreateTestWidget(); widget->Maximize(); ShelfWidget* const shelf_widget = GetShelfWidget(); DragHandle* const drag_handle = shelf_widget->GetDragHandle(); ASSERT_TRUE(drag_handle->has_show_drag_handle_timer_for_testing()); EnterOverview(); ASSERT_FALSE(drag_handle->has_show_drag_handle_timer_for_testing()); } // Tests that tapping the drag handle can shown drag handle nudge in overview. TEST_F(DragHandleContextualNudgeTest, DragHandleTapShowNudgeInOverview) { TabletModeControllerTestApi().EnterTabletMode(); // Creates a widget to put shelf into in-app state. views::Widget* widget = CreateTestWidget(); widget->Maximize(); ShelfWidget* const shelf_widget = GetShelfWidget(); DragHandle* const drag_handle = shelf_widget->GetDragHandle(); ASSERT_TRUE(drag_handle->has_show_drag_handle_timer_for_testing()); drag_handle->fire_show_drag_handle_timer_for_testing(); EXPECT_TRUE(drag_handle->gesture_nudge_target_visibility()); TabletModeControllerTestApi().LeaveTabletMode(); TabletModeControllerTestApi().EnterTabletMode(); EnterOverview(); ASSERT_FALSE(drag_handle->has_show_drag_handle_timer_for_testing()); GetEventGenerator()->GestureTapAt( drag_handle->GetBoundsInScreen().CenterPoint()); EXPECT_TRUE(drag_handle->GetVisible()); EXPECT_TRUE(drag_handle->gesture_nudge_target_visibility()); EXPECT_TRUE(drag_handle->has_hide_drag_handle_timer_for_testing()); drag_handle->fire_hide_drag_handle_timer_for_testing(); EXPECT_FALSE(drag_handle->gesture_nudge_target_visibility()); // Tapping the drag handle again will show the nudge again. GetEventGenerator()->GestureTapAt( drag_handle->GetBoundsInScreen().CenterPoint()); EXPECT_TRUE(drag_handle->gesture_nudge_target_visibility()); } // Tests that tapping the drag handle in split screen does not show nudge. TEST_F(DragHandleContextualNudgeTest, DragHandleTapDoesNotShowNudgeForSplitScreen) { TabletModeControllerTestApi().EnterTabletMode(); std::unique_ptr window = AshTestBase::CreateTestWindow(gfx::Rect(0, 0, 400, 400)); wm::ActivateWindow(window.get()); ShelfWidget* const shelf_widget = GetShelfWidget(); DragHandle* const drag_handle = shelf_widget->GetDragHandle(); // 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(shelf_widget->GetNativeWindow()); split_view_controller->SnapWindow(window.get(), SplitViewController::LEFT); EXPECT_TRUE(split_view_controller->InSplitViewMode()); // Tapping the drag handle will not show the drag handle. GetEventGenerator()->GestureTapAt( drag_handle->GetBoundsInScreen().CenterPoint()); EXPECT_FALSE(drag_handle->gesture_nudge_target_visibility()); } // Tests that entering split screen hides the drag handle nudge. TEST_F(DragHandleContextualNudgeTest, DragHandleNudgeHiddenOnSplitScreen) { TabletModeControllerTestApi().EnterTabletMode(); std::unique_ptr window = AshTestBase::CreateTestWindow(gfx::Rect(0, 0, 400, 400)); wm::ActivateWindow(window.get()); ShelfWidget* const shelf_widget = GetShelfWidget(); DragHandle* const drag_handle = shelf_widget->GetDragHandle(); // Tapping the drag handle should show the drag handle. GetEventGenerator()->GestureTapAt( drag_handle->GetBoundsInScreen().CenterPoint()); EXPECT_TRUE(drag_handle->gesture_nudge_target_visibility()); // 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(shelf_widget->GetNativeWindow()); split_view_controller->SnapWindow(window.get(), SplitViewController::LEFT); EXPECT_TRUE(split_view_controller->InSplitViewMode()); // The drag handle nudge should no longer be visible. EXPECT_FALSE(drag_handle->gesture_nudge_target_visibility()); } TEST_P(DragHandleContextualNudgeTestA11yPrefs, HideNudgesForShelfControls) { SCOPED_TRACE(testing::Message() << "Pref=" << GetParam()); // Creates a widget to put shelf into in-app state. views::Widget* widget = CreateTestWidget(); widget->Maximize(); TabletModeControllerTestApi().EnterTabletMode(); EXPECT_EQ(ShelfBackgroundType::kInApp, GetShelfWidget()->GetBackgroundType()); // The drag handle should be showing but the nudge should not. A timer to show // the nudge should be initialized. EXPECT_TRUE(GetShelfWidget()->GetDragHandle()->GetVisible()); EXPECT_FALSE( GetShelfWidget()->GetDragHandle()->gesture_nudge_target_visibility()); // Firing the timer should show the drag handle nudge. GetShelfWidget()->GetDragHandle()->fire_show_drag_handle_timer_for_testing(); EXPECT_TRUE(GetShelfWidget()->GetDragHandle()->GetVisible()); EXPECT_TRUE( GetShelfWidget()->GetDragHandle()->gesture_nudge_target_visibility()); // Enabling accessibility auto click disables gestures and enables shelf // control buttons. In app to home nudge should be hidden. Shell::Get() ->session_controller() ->GetLastActiveUserPrefService() ->SetBoolean(GetParam(), true); EXPECT_TRUE(GetShelfWidget()->GetDragHandle()->GetVisible()); EXPECT_FALSE( GetShelfWidget()->GetDragHandle()->gesture_nudge_target_visibility()); } TEST_P(DragHandleContextualNudgeTestA11yPrefs, DisableNudgesForShelfControls) { SCOPED_TRACE(testing::Message() << "Pref=" << GetParam()); // Turn on accessibility settings to enable shelf controls. Shell::Get() ->session_controller() ->GetLastActiveUserPrefService() ->SetBoolean(GetParam(), true); // Creates a widget to put shelf into in-app state. views::Widget* widget = CreateTestWidget(); widget->Maximize(); TabletModeControllerTestApi().EnterTabletMode(); EXPECT_EQ(ShelfBackgroundType::kInApp, GetShelfWidget()->GetBackgroundType()); // The drag handle should be showing but the nudge should not. A timer to show // the nudge should not be initialized because shelf controls are on. EXPECT_TRUE(GetShelfWidget()->GetDragHandle()->GetVisible()); EXPECT_FALSE( GetShelfWidget()->GetDragHandle()->gesture_nudge_target_visibility()); EXPECT_FALSE(GetShelfWidget() ->GetDragHandle() ->has_show_drag_handle_timer_for_testing()); } } // namespace ash