| // 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/wm/gestures/back_gesture/back_gesture_event_handler.h" |
| |
| #include "ash/accelerators/accelerator_controller_impl.h" |
| #include "ash/app_list/test/app_list_test_helper.h" |
| #include "ash/app_list/views/app_list_view.h" |
| #include "ash/app_list/views/search_box_view.h" |
| #include "ash/display/screen_orientation_controller.h" |
| #include "ash/display/screen_orientation_controller_test_api.h" |
| #include "ash/home_screen/home_screen_controller.h" |
| #include "ash/keyboard/ui/test/keyboard_test_util.h" |
| #include "ash/public/cpp/ash_features.h" |
| #include "ash/public/cpp/keyboard/keyboard_controller.h" |
| #include "ash/screen_util.h" |
| #include "ash/session/session_controller_impl.h" |
| #include "ash/shelf/hotseat_widget.h" |
| #include "ash/shelf/shelf.h" |
| #include "ash/shell.h" |
| #include "ash/system/model/system_tray_model.h" |
| #include "ash/system/model/virtual_keyboard_model.h" |
| #include "ash/test/ash_test_base.h" |
| #include "ash/test_shell_delegate.h" |
| #include "ash/wm/overview/overview_controller.h" |
| #include "ash/wm/splitview/split_view_divider.h" |
| #include "ash/wm/tablet_mode/tablet_mode_controller_test_api.h" |
| #include "ash/wm/window_state.h" |
| #include "ash/wm/window_util.h" |
| #include "ash/wm/wm_event.h" |
| #include "ash/wm/workspace/backdrop_controller.h" |
| #include "ash/wm/workspace/workspace_layout_manager.h" |
| #include "ash/wm/workspace_controller.h" |
| #include "base/test/scoped_feature_list.h" |
| #include "ui/aura/client/aura_constants.h" |
| #include "ui/base/accelerators/accelerator.h" |
| #include "ui/base/accelerators/test_accelerator_target.h" |
| #include "ui/display/test/display_manager_test_api.h" |
| |
| namespace ash { |
| |
| class BackGestureEventHandlerTest : public AshTestBase { |
| public: |
| // Distance that swiping from left edge to let the affordance achieve |
| // activated state. |
| static constexpr int kSwipingDistanceForGoingBack = 80; |
| |
| explicit BackGestureEventHandlerTest(bool can_go_back = true) |
| : can_go_back_(can_go_back) {} |
| BackGestureEventHandlerTest(const BackGestureEventHandlerTest&) = delete; |
| BackGestureEventHandlerTest& operator=(const BackGestureEventHandlerTest&) = |
| delete; |
| ~BackGestureEventHandlerTest() override = default; |
| |
| void SetUp() override { |
| std::unique_ptr<TestShellDelegate> delegate; |
| if (!can_go_back_) { |
| delegate = std::make_unique<TestShellDelegate>(); |
| delegate->SetCanGoBack(false); |
| } |
| AshTestBase::SetUp(std::move(delegate)); |
| |
| feature_list_.InitAndEnableFeature(features::kSwipingFromLeftEdgeToGoBack); |
| RecreateTopWindow(AppType::BROWSER); |
| TabletModeControllerTestApi().EnterTabletMode(); |
| } |
| |
| void TearDown() override { |
| top_window_.reset(); |
| AshTestBase::TearDown(); |
| } |
| |
| void RegisterBackPressAndRelease(ui::TestAcceleratorTarget* back_press, |
| ui::TestAcceleratorTarget* back_release) { |
| AcceleratorControllerImpl* controller = |
| Shell::Get()->accelerator_controller(); |
| |
| // Register an accelerator that looks for back presses. |
| ui::Accelerator accelerator_back_press(ui::VKEY_BROWSER_BACK, ui::EF_NONE); |
| accelerator_back_press.set_key_state(ui::Accelerator::KeyState::PRESSED); |
| controller->Register({accelerator_back_press}, back_press); |
| |
| // Register an accelerator that looks for back releases. |
| ui::Accelerator accelerator_back_release(ui::VKEY_BROWSER_BACK, |
| ui::EF_NONE); |
| accelerator_back_release.set_key_state(ui::Accelerator::KeyState::RELEASED); |
| controller->Register({accelerator_back_release}, back_release); |
| } |
| |
| // Send touch event with |type| to the toplevel window event handler. |
| void SendTouchEvent(const gfx::Point& position, ui::EventType type) { |
| ui::TouchEvent event = ui::TouchEvent( |
| type, position, base::TimeTicks::Now(), |
| ui::PointerDetails(ui::EventPointerType::POINTER_TYPE_TOUCH, |
| /*pointer_id=*/5, /*radius_x=*/5.0f, |
| /*radius_y=*/5.0, /*force=*/1.0f)); |
| ui::Event::DispatcherApi(&event).set_target(top_window_.get()); |
| Shell::Get()->back_gesture_event_handler()->OnTouchEvent(&event); |
| } |
| |
| void RecreateTopWindow(AppType app_type) { |
| top_window_ = CreateAppWindow(gfx::Rect(), app_type); |
| } |
| |
| // Generates a scroll sequence that will create a back gesture. |
| void GenerateBackSequence() { |
| GetEventGenerator()->GestureScrollSequence( |
| gfx::Point(0, 100), gfx::Point(kSwipingDistanceForGoingBack + 10, 100), |
| base::TimeDelta::FromMilliseconds(100), 3); |
| } |
| |
| aura::Window* top_window() { return top_window_.get(); } |
| |
| private: |
| bool can_go_back_; |
| base::test::ScopedFeatureList feature_list_; |
| std::unique_ptr<aura::Window> top_window_; |
| }; |
| |
| class BackGestureEventHandlerTestCantGoBack |
| : public BackGestureEventHandlerTest { |
| public: |
| BackGestureEventHandlerTestCantGoBack() |
| : BackGestureEventHandlerTest(false) {} |
| }; |
| |
| TEST_F(BackGestureEventHandlerTest, SwipingFromLeftEdgeToGoBack) { |
| ui::TestAcceleratorTarget target_back_press, target_back_release; |
| RegisterBackPressAndRelease(&target_back_press, &target_back_release); |
| |
| // Tests that swiping from the left less than |kSwipingDistanceForGoingBack| |
| // should not go to previous page. |
| ui::test::EventGenerator* generator = GetEventGenerator(); |
| const gfx::Point start(0, 100); |
| generator->GestureScrollSequence( |
| start, gfx::Point(kSwipingDistanceForGoingBack - 10, 100), |
| base::TimeDelta::FromMilliseconds(100), 3); |
| EXPECT_EQ(0, target_back_press.accelerator_count()); |
| EXPECT_EQ(0, target_back_release.accelerator_count()); |
| |
| // Tests that swiping from the left more than |kSwipingDistanceForGoingBack| |
| // should go to previous page. |
| generator->GestureScrollSequence( |
| start, gfx::Point(kSwipingDistanceForGoingBack + 10, 100), |
| base::TimeDelta::FromMilliseconds(100), 3); |
| EXPECT_EQ(1, target_back_press.accelerator_count()); |
| EXPECT_EQ(1, target_back_release.accelerator_count()); |
| } |
| |
| TEST_F(BackGestureEventHandlerTest, FlingFromLeftEdgeToGoBack) { |
| ui::TestAcceleratorTarget target_back_press, target_back_release; |
| RegisterBackPressAndRelease(&target_back_press, &target_back_release); |
| |
| // Tests that fling from the left with velocity smaller than |
| // |kFlingVelocityForGoingBack| should not go to previous page. |
| // Drag further than |touch_slop| in GestureDetector to trigger scroll |
| // sequence. Note, |touch_slop| equals to 15.05, which is the value of |
| // |max_touch_move_in_pixels_for_click_| + |kSlopEpsilon|. Generate the scroll |
| // sequence with short duration and only one step for FLING scroll gestures. |
| // X-velocity here will be 800 dips/seconds. |
| ui::test::EventGenerator* generator = GetEventGenerator(); |
| generator->GestureScrollSequence(gfx::Point(0, 0), gfx::Point(16, 0), |
| base::TimeDelta::FromMilliseconds(20), |
| /*steps=*/1); |
| EXPECT_EQ(0, target_back_press.accelerator_count()); |
| EXPECT_EQ(0, target_back_release.accelerator_count()); |
| |
| // Tests that fling from the left with velocity larger than |
| // |kFlingVelocityForGoingBack| should go to previous page. X-velocity here |
| // will be 1600 dips/seconds. |
| generator->GestureScrollSequence(gfx::Point(0, 0), gfx::Point(16, 0), |
| base::TimeDelta::FromMilliseconds(1), |
| /*steps=*/1); |
| EXPECT_EQ(1, target_back_press.accelerator_count()); |
| EXPECT_EQ(1, target_back_release.accelerator_count()); |
| |
| // Tests that fling from the left with velocity smaller than |
| // |kFlingVelocityForGoingBack| but dragged further enough to trigger |
| // activated affordance should still go back to previous page. X-velocity here |
| // will be 800 dips/seconds and drag distance is 160, which is larger than |
| // |kSwipingDistanceForGoingBack|. |
| generator->GestureScrollSequence(gfx::Point(0, 0), gfx::Point(160, 0), |
| base::TimeDelta::FromMilliseconds(200), |
| /*steps=*/1); |
| EXPECT_EQ(2, target_back_press.accelerator_count()); |
| EXPECT_EQ(2, target_back_release.accelerator_count()); |
| } |
| |
| TEST_F(BackGestureEventHandlerTestCantGoBack, GoBackInOverviewMode) { |
| ui::TestAcceleratorTarget target_back_press, target_back_release; |
| RegisterBackPressAndRelease(&target_back_press, &target_back_release); |
| |
| ASSERT_FALSE(WindowState::Get(top_window())->IsMinimized()); |
| ASSERT_TRUE(window_util::ShouldMinimizeTopWindowOnBack()); |
| GenerateBackSequence(); |
| // Should trigger window minimize instead of go back. |
| EXPECT_EQ(0, target_back_release.accelerator_count()); |
| EXPECT_TRUE(WindowState::Get(top_window())->IsMinimized()); |
| |
| WindowState::Get(top_window())->Unminimize(); |
| ASSERT_FALSE(WindowState::Get(top_window())->IsMinimized()); |
| auto* shell = Shell::Get(); |
| shell->overview_controller()->StartOverview(); |
| ASSERT_TRUE(shell->overview_controller()->InOverviewSession()); |
| GenerateBackSequence(); |
| // Should trigger go back instead of minimize the window since it is in |
| // overview mode. |
| EXPECT_EQ(1, target_back_release.accelerator_count()); |
| } |
| |
| TEST_F(BackGestureEventHandlerTest, DonotStartGoingBack) { |
| ui::TestAcceleratorTarget target_back_press, target_back_release; |
| RegisterBackPressAndRelease(&target_back_press, &target_back_release); |
| |
| auto* shell = Shell::Get(); |
| |
| // Should not go back if it is not in ACTIVE session. |
| ASSERT_FALSE(shell->overview_controller()->InOverviewSession()); |
| ASSERT_FALSE(shell->home_screen_controller()->IsHomeScreenVisible()); |
| GetSessionControllerClient()->SetSessionState( |
| session_manager::SessionState::LOCKED); |
| GenerateBackSequence(); |
| EXPECT_EQ(0, target_back_press.accelerator_count()); |
| EXPECT_EQ(0, target_back_release.accelerator_count()); |
| |
| // Should not go back if home screen is visible and in |kFullscreenAllApps| |
| // state. |
| GetSessionControllerClient()->SetSessionState( |
| session_manager::SessionState::ACTIVE); |
| shell->home_screen_controller()->GoHome(GetPrimaryDisplay().id()); |
| ASSERT_TRUE(shell->home_screen_controller()->IsHomeScreenVisible()); |
| GetAppListTestHelper()->CheckState(AppListViewState::kFullscreenAllApps); |
| GenerateBackSequence(); |
| EXPECT_EQ(0, target_back_press.accelerator_count()); |
| EXPECT_EQ(0, target_back_release.accelerator_count()); |
| |
| // Should exit |kFullscreenSearch| to enter |kFullscreenAllApps| state while |
| // home screen search result page is opened. |
| GetEventGenerator()->GestureTapAt(GetAppListTestHelper() |
| ->GetAppListView() |
| ->search_box_view() |
| ->GetBoundsInScreen() |
| .CenterPoint()); |
| GetAppListTestHelper()->CheckState(AppListViewState::kFullscreenSearch); |
| GenerateBackSequence(); |
| EXPECT_EQ(1, target_back_release.accelerator_count()); |
| GetAppListTestHelper()->CheckState(AppListViewState::kFullscreenAllApps); |
| } |
| |
| TEST_F(BackGestureEventHandlerTest, CancelOnScreenRotation) { |
| UpdateDisplay("807x407"); |
| int64_t display_id = display::Screen::GetScreen()->GetPrimaryDisplay().id(); |
| display::DisplayManager* display_manager = Shell::Get()->display_manager(); |
| display::test::ScopedSetInternalDisplayId set_internal(display_manager, |
| display_id); |
| ScreenOrientationControllerTestApi test_api( |
| Shell::Get()->screen_orientation_controller()); |
| ui::TestAcceleratorTarget target_back_press, target_back_release; |
| RegisterBackPressAndRelease(&target_back_press, &target_back_release); |
| |
| // Set the screen orientation to LANDSCAPE_PRIMARY. |
| test_api.SetDisplayRotation(display::Display::ROTATE_0, |
| display::Display::RotationSource::ACTIVE); |
| EXPECT_EQ(test_api.GetCurrentOrientation(), |
| OrientationLockType::kLandscapePrimary); |
| |
| gfx::Point start(0, 100); |
| gfx::Point update_and_end(200, 100); |
| SendTouchEvent(start, ui::ET_TOUCH_PRESSED); |
| SendTouchEvent(update_and_end, ui::ET_TOUCH_MOVED); |
| // Rotate the screen by 270 degree during drag. |
| test_api.SetDisplayRotation(display::Display::ROTATE_270, |
| display::Display::RotationSource::ACTIVE); |
| EXPECT_EQ(test_api.GetCurrentOrientation(), |
| OrientationLockType::kPortraitPrimary); |
| SendTouchEvent(update_and_end, ui::ET_TOUCH_RELEASED); |
| // Left edge swipe back should be cancelled due to screen rotation, so the |
| // fling event with velocity larger than |kFlingVelocityForGoingBack| above |
| // will not trigger actual going back. |
| EXPECT_EQ(0, target_back_press.accelerator_count()); |
| EXPECT_EQ(0, target_back_release.accelerator_count()); |
| } |
| |
| // Tests back gesture while in split view mode. |
| TEST_F(BackGestureEventHandlerTest, DragFromSplitViewDivider) { |
| std::unique_ptr<aura::Window> window1 = CreateTestWindow(); |
| std::unique_ptr<aura::Window> window2 = CreateTestWindow(); |
| ui::TestAcceleratorTarget target_back_press, target_back_release; |
| gfx::Rect display_bounds = |
| screen_util::GetDisplayWorkAreaBoundsInScreenForActiveDeskContainer( |
| window1.get()); |
| RegisterBackPressAndRelease(&target_back_press, &target_back_release); |
| |
| auto* split_view_controller = |
| SplitViewController::Get(Shell::GetPrimaryRootWindow()); |
| split_view_controller->SnapWindow(window1.get(), SplitViewController::LEFT); |
| split_view_controller->SnapWindow(window2.get(), SplitViewController::RIGHT); |
| ASSERT_TRUE(split_view_controller->InSplitViewMode()); |
| ASSERT_EQ(SplitViewController::State::kBothSnapped, |
| split_view_controller->state()); |
| |
| gfx::Rect divider_bounds = |
| split_view_controller->split_view_divider()->GetDividerBoundsInScreen( |
| false); |
| ui::test::EventGenerator* generator = GetEventGenerator(); |
| // Drag from the splitview divider's non-resizable area with larger than |
| // |kSwipingDistanceForGoingBack| distance should trigger back gesture. The |
| // snapped window should go to previous page and divider's position will not |
| // be changed. |
| gfx::Point start(divider_bounds.x(), 10); |
| gfx::Point end(start.x() + kSwipingDistanceForGoingBack + 10, 10); |
| EXPECT_GT(split_view_controller->divider_position(), |
| 0.33f * display_bounds.width()); |
| EXPECT_LE(split_view_controller->divider_position(), |
| 0.5f * display_bounds.width()); |
| generator->GestureScrollSequence(start, end, |
| base::TimeDelta::FromMilliseconds(100), 3); |
| EXPECT_EQ(SplitViewController::State::kBothSnapped, |
| split_view_controller->state()); |
| EXPECT_EQ(1, target_back_press.accelerator_count()); |
| EXPECT_EQ(1, target_back_release.accelerator_count()); |
| EXPECT_GT(split_view_controller->divider_position(), |
| 0.33f * display_bounds.width()); |
| EXPECT_LE(split_view_controller->divider_position(), |
| 0.5f * display_bounds.width()); |
| |
| // Drag from the divider's resizable area should trigger splitview resizing. |
| // Divider's position will be changed and back gesture should not be |
| // triggered. |
| start = divider_bounds.CenterPoint(); |
| end = gfx::Point(0.67f * display_bounds.width(), start.y()); |
| generator->GestureScrollSequence(start, end, |
| base::TimeDelta::FromMilliseconds(100), 3); |
| EXPECT_EQ(1, target_back_press.accelerator_count()); |
| EXPECT_EQ(1, target_back_release.accelerator_count()); |
| EXPECT_GT(split_view_controller->divider_position(), |
| 0.5f * display_bounds.width()); |
| EXPECT_LE(split_view_controller->divider_position(), |
| 0.67f * display_bounds.width()); |
| split_view_controller->EndSplitView(); |
| } |
| |
| // Tests that in different screen orientations should always activate the |
| // snapped window in splitview that is underneath the finger. And should be the |
| // snapped window that is underneath to go back to the previous page. |
| TEST_F(BackGestureEventHandlerTest, BackInSplitViewMode) { |
| int64_t display_id = display::Screen::GetScreen()->GetPrimaryDisplay().id(); |
| display::DisplayManager* display_manager = Shell::Get()->display_manager(); |
| display::test::ScopedSetInternalDisplayId set_internal(display_manager, |
| display_id); |
| ScreenOrientationControllerTestApi test_api( |
| Shell::Get()->screen_orientation_controller()); |
| ui::TestAcceleratorTarget target_back_press, target_back_release; |
| RegisterBackPressAndRelease(&target_back_press, &target_back_release); |
| |
| std::unique_ptr<aura::Window> left_window = CreateTestWindow(); |
| std::unique_ptr<aura::Window> right_window = CreateTestWindow(); |
| auto* split_view_controller = |
| SplitViewController::Get(Shell::GetPrimaryRootWindow()); |
| split_view_controller->SnapWindow(left_window.get(), |
| SplitViewController::LEFT); |
| split_view_controller->SnapWindow(right_window.get(), |
| SplitViewController::RIGHT); |
| |
| // Set the screen orientation to LANDSCAPE_PRIMARY. |
| test_api.SetDisplayRotation(display::Display::ROTATE_0, |
| display::Display::RotationSource::ACTIVE); |
| EXPECT_EQ(test_api.GetCurrentOrientation(), |
| OrientationLockType::kLandscapePrimary); |
| |
| ASSERT_EQ(right_window.get(), window_util::GetActiveWindow()); |
| gfx::Point start(0, 10); |
| gfx::Point update_and_end(kSwipingDistanceForGoingBack + 10, 10); |
| SendTouchEvent(start, ui::ET_TOUCH_PRESSED); |
| SendTouchEvent(update_and_end, ui::ET_TOUCH_MOVED); |
| SendTouchEvent(update_and_end, ui::ET_TOUCH_RELEASED); |
| // Swiping from the left of the display in LandscapePrimary further than |
| // |kSwipingDistanceForGoingBack| should activate the physically left snapped |
| // window, which is |left_window| and it should go back to the previous page. |
| EXPECT_EQ(left_window.get(), window_util::GetActiveWindow()); |
| EXPECT_EQ(1, target_back_press.accelerator_count()); |
| EXPECT_EQ(1, target_back_release.accelerator_count()); |
| |
| gfx::Rect divider_bounds = |
| split_view_controller->split_view_divider()->GetDividerBoundsInScreen( |
| false); |
| start = gfx::Point(divider_bounds.x(), 10); |
| update_and_end = |
| gfx::Point(divider_bounds.x() + kSwipingDistanceForGoingBack + 10, 10); |
| SendTouchEvent(start, ui::ET_TOUCH_PRESSED); |
| SendTouchEvent(update_and_end, ui::ET_TOUCH_MOVED); |
| SendTouchEvent(update_and_end, ui::ET_TOUCH_RELEASED); |
| // Swiping from the split view divider in LandscapePrimary further than |
| // |kSwipingDistanceForGoingBack| should activate the physically right snapped |
| // window, which is |right_window| and it should go back to the previous page. |
| EXPECT_EQ(right_window.get(), window_util::GetActiveWindow()); |
| EXPECT_EQ(2, target_back_press.accelerator_count()); |
| EXPECT_EQ(2, target_back_release.accelerator_count()); |
| |
| // Rotate the screen by 180 degree. |
| test_api.SetDisplayRotation(display::Display::ROTATE_180, |
| display::Display::RotationSource::ACTIVE); |
| EXPECT_EQ(test_api.GetCurrentOrientation(), |
| OrientationLockType::kLandscapeSecondary); |
| |
| SendTouchEvent(start, ui::ET_TOUCH_PRESSED); |
| SendTouchEvent(update_and_end, ui::ET_TOUCH_MOVED); |
| SendTouchEvent(update_and_end, ui::ET_TOUCH_RELEASED); |
| // Swiping from the split view divider in LandscapeSecondary further than |
| // |kSwipingDistanceForGoingBack| should activate the physically right snapped |
| // window, which is |left_window| and it should go back to the previous page. |
| EXPECT_EQ(left_window.get(), window_util::GetActiveWindow()); |
| EXPECT_EQ(3, target_back_press.accelerator_count()); |
| EXPECT_EQ(3, target_back_release.accelerator_count()); |
| |
| start = gfx::Point(0, 10); |
| update_and_end = gfx::Point(kSwipingDistanceForGoingBack + 10, 10); |
| SendTouchEvent(start, ui::ET_TOUCH_PRESSED); |
| SendTouchEvent(update_and_end, ui::ET_TOUCH_MOVED); |
| SendTouchEvent(update_and_end, ui::ET_TOUCH_RELEASED); |
| // Swiping from the left of the display in LandscapeSecondary further than |
| // |kSwipingDistanceForGoingBack| should activate the physically left snapped |
| // window, which is |right_window| and it should go back to the previous page. |
| EXPECT_EQ(right_window.get(), window_util::GetActiveWindow()); |
| EXPECT_EQ(4, target_back_press.accelerator_count()); |
| EXPECT_EQ(4, target_back_release.accelerator_count()); |
| |
| // Rotate the screen by 270 degree. |
| test_api.SetDisplayRotation(display::Display::ROTATE_270, |
| display::Display::RotationSource::ACTIVE); |
| EXPECT_EQ(test_api.GetCurrentOrientation(), |
| OrientationLockType::kPortraitPrimary); |
| |
| SendTouchEvent(start, ui::ET_TOUCH_PRESSED); |
| SendTouchEvent(update_and_end, ui::ET_TOUCH_MOVED); |
| SendTouchEvent(update_and_end, ui::ET_TOUCH_RELEASED); |
| // Swiping from the left of the top half of the display in PortraitPrimary |
| // further than |kSwipingDistanceForGoingBack| should activate the physically |
| // top snapped window, which is |right_window|, and it should go back to the |
| // previous page. |
| EXPECT_EQ(left_window.get(), window_util::GetActiveWindow()); |
| EXPECT_EQ(5, target_back_press.accelerator_count()); |
| EXPECT_EQ(5, target_back_release.accelerator_count()); |
| |
| divider_bounds = |
| split_view_controller->split_view_divider()->GetDividerBoundsInScreen( |
| false); |
| start = gfx::Point(0, divider_bounds.bottom() + 10); |
| update_and_end = gfx::Point(kSwipingDistanceForGoingBack + 10, start.y()); |
| SendTouchEvent(start, ui::ET_TOUCH_PRESSED); |
| SendTouchEvent(update_and_end, ui::ET_TOUCH_MOVED); |
| SendTouchEvent(update_and_end, ui::ET_TOUCH_RELEASED); |
| // Swiping from the left of the bottom half of the display in PortraitPrimary |
| // further than |kSwipingDistanceForGoingBack| should activate the physically |
| // bottom snapped window, which is |right_window|, and it should go back to |
| // the previous page. |
| EXPECT_EQ(right_window.get(), window_util::GetActiveWindow()); |
| EXPECT_EQ(6, target_back_press.accelerator_count()); |
| EXPECT_EQ(6, target_back_release.accelerator_count()); |
| |
| // Rotate the screen by 90 degree. |
| test_api.SetDisplayRotation(display::Display::ROTATE_90, |
| display::Display::RotationSource::ACTIVE); |
| EXPECT_EQ(test_api.GetCurrentOrientation(), |
| OrientationLockType::kPortraitSecondary); |
| |
| SendTouchEvent(start, ui::ET_TOUCH_PRESSED); |
| SendTouchEvent(update_and_end, ui::ET_TOUCH_MOVED); |
| SendTouchEvent(update_and_end, ui::ET_TOUCH_RELEASED); |
| // Swiping from the left of the bottom half of the display in |
| // PortraitSecondary further than |kSwipingDistanceForGoingBack| should |
| // activate the physically bottom snapped window, which is |left_window|, and |
| // it should go back to the previous page. |
| EXPECT_EQ(left_window.get(), window_util::GetActiveWindow()); |
| EXPECT_EQ(7, target_back_press.accelerator_count()); |
| EXPECT_EQ(7, target_back_release.accelerator_count()); |
| |
| start = gfx::Point(0, 10); |
| update_and_end = gfx::Point(kSwipingDistanceForGoingBack + 10, 10); |
| SendTouchEvent(start, ui::ET_TOUCH_PRESSED); |
| SendTouchEvent(update_and_end, ui::ET_TOUCH_MOVED); |
| SendTouchEvent(update_and_end, ui::ET_TOUCH_RELEASED); |
| // Swiping from the left of the top half of the display in PortraitSecondary |
| // further than |kSwipingDistanceForGoingBack| should activate the physically |
| // top snapped window, which is |right_window| and it should go back to the |
| // previous page. |
| EXPECT_EQ(right_window.get(), window_util::GetActiveWindow()); |
| EXPECT_EQ(8, target_back_press.accelerator_count()); |
| EXPECT_EQ(8, target_back_release.accelerator_count()); |
| } |
| |
| // Tests the back gesture behavior on a non-ARC fullscreened window. |
| TEST_F(BackGestureEventHandlerTest, FullscreenedWindow) { |
| ui::TestAcceleratorTarget target_back_press, target_back_release; |
| RegisterBackPressAndRelease(&target_back_press, &target_back_release); |
| |
| WindowState* window_state = WindowState::Get(top_window()); |
| const WMEvent fullscreen_event(WM_EVENT_TOGGLE_FULLSCREEN); |
| window_state->OnWMEvent(&fullscreen_event); |
| EXPECT_TRUE(window_state->IsFullscreen()); |
| |
| GenerateBackSequence(); |
| // First back gesture should let the window exit fullscreen mode instead of |
| // triggering go back. |
| EXPECT_FALSE(window_state->IsFullscreen()); |
| EXPECT_EQ(0, target_back_press.accelerator_count()); |
| EXPECT_EQ(0, target_back_release.accelerator_count()); |
| |
| GenerateBackSequence(); |
| // Second back gesture should trigger go back. |
| EXPECT_EQ(1, target_back_press.accelerator_count()); |
| EXPECT_EQ(1, target_back_release.accelerator_count()); |
| } |
| |
| // Tests the back gesture behavior on a ARC fullscreened window. |
| TEST_F(BackGestureEventHandlerTest, ARCFullscreenedWindow) { |
| ui::TestAcceleratorTarget target_back_press, target_back_release; |
| RegisterBackPressAndRelease(&target_back_press, &target_back_release); |
| |
| RecreateTopWindow(AppType::ARC_APP); |
| |
| WindowState* window_state = WindowState::Get(top_window()); |
| const WMEvent fullscreen_event(WM_EVENT_TOGGLE_FULLSCREEN); |
| window_state->OnWMEvent(&fullscreen_event); |
| ASSERT_TRUE(window_state->IsFullscreen()); |
| |
| auto shelf_visible_hotseat_extended = [this]() -> bool { |
| auto* shelf = Shelf::ForWindow(top_window()); |
| const bool shelf_visible = shelf->GetVisibilityState() == SHELF_VISIBLE; |
| const bool hotseat_extended = |
| shelf->hotseat_widget()->state() == HotseatState::kExtended; |
| return shelf_visible && hotseat_extended; |
| }; |
| |
| GenerateBackSequence(); |
| // First back gesture should show the shelf instead of triggering go back. The |
| // app should remain fullscreened. |
| EXPECT_TRUE(window_state->IsFullscreen()); |
| EXPECT_EQ(0, target_back_press.accelerator_count()); |
| EXPECT_EQ(0, target_back_release.accelerator_count()); |
| EXPECT_TRUE(shelf_visible_hotseat_extended()); |
| |
| // Tapping on a point on the screen should hide the shelf and hotseat. |
| GetEventGenerator()->GestureTapAt(gfx::Point(100, 100)); |
| EXPECT_FALSE(shelf_visible_hotseat_extended()); |
| |
| // Send another back gesture to bring up the shelf and hotseat. |
| GenerateBackSequence(); |
| EXPECT_TRUE(window_state->IsFullscreen()); |
| EXPECT_EQ(0, target_back_press.accelerator_count()); |
| EXPECT_EQ(0, target_back_release.accelerator_count()); |
| EXPECT_TRUE(shelf_visible_hotseat_extended()); |
| |
| GenerateBackSequence(); |
| // Second back gesture in a row should trigger go back. Fullscreen will be |
| // dependent on how the app choses to handle the back event. |
| EXPECT_EQ(1, target_back_press.accelerator_count()); |
| EXPECT_EQ(1, target_back_release.accelerator_count()); |
| } |
| |
| // Tests the back gesture behavior when a Chrome OS IME is visible. |
| TEST_F(BackGestureEventHandlerTest, BackGestureWithCrosKeyboardTest) { |
| ui::TestAcceleratorTarget target_back_press, target_back_release; |
| RegisterBackPressAndRelease(&target_back_press, &target_back_release); |
| |
| KeyboardController* keyboard_controller = KeyboardController::Get(); |
| keyboard_controller->SetEnableFlag( |
| keyboard::KeyboardEnableFlag::kExtensionEnabled); |
| // The keyboard needs to be in a loaded state before being shown. |
| ASSERT_TRUE(keyboard::test::WaitUntilLoaded()); |
| keyboard_controller->ShowKeyboard(); |
| EXPECT_TRUE(keyboard_controller->IsKeyboardVisible()); |
| |
| GenerateBackSequence(); |
| // First back gesture should hide the virtual keyboard. |
| EXPECT_FALSE(keyboard_controller->IsKeyboardVisible()); |
| EXPECT_EQ(0, target_back_press.accelerator_count()); |
| EXPECT_EQ(0, target_back_release.accelerator_count()); |
| |
| GenerateBackSequence(); |
| // Second back gesture should trigger go back. |
| EXPECT_EQ(1, target_back_press.accelerator_count()); |
| EXPECT_EQ(1, target_back_release.accelerator_count()); |
| } |
| |
| // Tests the back gesture behavior when an Android IME is visible. Due to the |
| // way the Android IME is implemented, a lot of this test is fake behavior, but |
| // it will help catch regressions. |
| TEST_F(BackGestureEventHandlerTest, BackGestureWithAndroidKeyboardTest) { |
| ui::TestAcceleratorTarget target_back_press, target_back_release; |
| RegisterBackPressAndRelease(&target_back_press, &target_back_release); |
| |
| WindowState* window_state = WindowState::Get(top_window()); |
| ASSERT_FALSE(window_state->IsMinimized()); |
| |
| VirtualKeyboardModel* keyboard = |
| Shell::Get()->system_tray_model()->virtual_keyboard(); |
| ASSERT_TRUE(keyboard); |
| // Fakes showing the keyboard. |
| keyboard->OnArcInputMethodSurfaceBoundsChanged(gfx::Rect(400, 400)); |
| EXPECT_TRUE(keyboard->visible()); |
| |
| // Unfortunately we cannot hook this all the wall up to see if the Android IME |
| // is hidden, but we can check that back key events are generated and the top |
| // window is not minimized. |
| GenerateBackSequence(); |
| EXPECT_EQ(1, target_back_press.accelerator_count()); |
| EXPECT_EQ(1, target_back_release.accelerator_count()); |
| EXPECT_FALSE(window_state->IsMinimized()); |
| } |
| |
| // Tests that swiping on the backdrop to minimize a non-resizable app will not |
| // cause a crash. Regression test for http://crbug.com/1064618. |
| TEST_F(BackGestureEventHandlerTestCantGoBack, NonResizableApp) { |
| // Make the top window non-resizable and set its bounds so that the backdrop |
| // will take the gesture events. |
| top_window()->SetProperty(aura::client::kResizeBehaviorKey, |
| aura::client::kResizeBehaviorCanMinimize); |
| |
| WindowState* window_state = WindowState::Get(top_window()); |
| window_state->Restore(); |
| SetBoundsWMEvent bounds_event(gfx::Rect(200, 100, 300, 300)); |
| window_state->OnWMEvent(&bounds_event); |
| ASSERT_FALSE(window_state->IsMinimized()); |
| |
| // Check that the backdrop is visible. |
| WorkspaceController* workspace_controller = |
| GetWorkspaceControllerForContext(top_window()); |
| WorkspaceLayoutManager* layout_manager = |
| workspace_controller->layout_manager(); |
| BackdropController* backdrop_controller = |
| layout_manager->backdrop_controller(); |
| aura::Window* backdrop_window = backdrop_controller->backdrop_window(); |
| ASSERT_TRUE(backdrop_window); |
| ASSERT_TRUE(backdrop_window->IsVisible()); |
| |
| // Generate a back seqeuence. There should be no crash. |
| GenerateBackSequence(); |
| EXPECT_TRUE(window_state->IsMinimized()); |
| } |
| |
| } // namespace ash |