123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415 |
- // Copyright 2022 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/views/apps_container_view.h"
- #include "ash/app_list/app_list_controller_impl.h"
- #include "ash/app_list/model/app_list_test_model.h"
- #include "ash/app_list/test/app_list_test_helper.h"
- #include "ash/app_list/views/apps_grid_view_test_api.h"
- #include "ash/app_list/views/continue_section_view.h"
- #include "ash/app_list/views/recent_apps_view.h"
- #include "ash/app_list/views/search_box_view.h"
- #include "ash/constants/ash_features.h"
- #include "ash/public/cpp/tablet_mode.h"
- #include "ash/shell.h"
- #include "ash/test/ash_test_base.h"
- #include "ash/test/layer_animation_stopped_waiter.h"
- #include "base/test/scoped_feature_list.h"
- #include "ui/compositor/layer.h"
- #include "ui/compositor/layer_animator.h"
- #include "ui/compositor/scoped_animation_duration_scale_mode.h"
- #include "ui/events/test/event_generator.h"
- #include "ui/views/accessibility/view_accessibility.h"
- #include "ui/views/controls/textfield/textfield.h"
- namespace ash {
- namespace {
- class TransitionWaiter : public PaginationModelObserver {
- public:
- explicit TransitionWaiter(PaginationModel* model) : model_(model) {
- model_->AddObserver(this);
- }
- TransitionWaiter(const TransitionWaiter&) = delete;
- TransitionWaiter& operator=(const TransitionWaiter&) = delete;
- ~TransitionWaiter() override { model_->RemoveObserver(this); }
- void Wait() {
- ui_run_loop_ = std::make_unique<base::RunLoop>();
- ui_run_loop_->Run();
- }
- private:
- // PaginationModelObserver:
- void TransitionEnded() override { ui_run_loop_->QuitWhenIdle(); }
- std::unique_ptr<base::RunLoop> ui_run_loop_;
- PaginationModel* model_ = nullptr;
- };
- } // namespace
- class AppsContainerViewTest : public AshTestBase {
- public:
- AppsContainerViewTest() {
- // These tests primarily exercise the "hide continue section" behavior.
- features_.InitWithFeatures({features::kProductivityLauncher,
- features::kLauncherHideContinueSection},
- {});
- }
- ~AppsContainerViewTest() override = default;
- // testing::Test:
- void SetUp() override {
- AshTestBase::SetUp();
- app_list_test_model_ = std::make_unique<test::AppListTestModel>();
- search_model_ = std::make_unique<SearchModel>();
- Shell::Get()->app_list_controller()->SetActiveModel(
- /*profile_id=*/1, app_list_test_model_.get(), search_model_.get());
- }
- void AddFolderWithApps(int count) {
- app_list_test_model_->CreateAndPopulateFolderWithApps(count);
- }
- AppListToastContainerView* GetToastContainerView() {
- return GetAppListTestHelper()
- ->GetAppsContainerView()
- ->GetToastContainerView();
- }
- void PressDown() {
- ui::test::EventGenerator generator(Shell::GetPrimaryRootWindow());
- generator.PressAndReleaseKey(ui::KeyboardCode::VKEY_DOWN);
- }
- int GetSelectedPage() {
- return GetAppListTestHelper()
- ->GetRootPagedAppsGridView()
- ->pagination_model()
- ->selected_page();
- }
- int GetTotalPages() {
- return GetAppListTestHelper()
- ->GetRootPagedAppsGridView()
- ->pagination_model()
- ->total_pages();
- }
- bool HasGradientMask() {
- return GetAppListTestHelper()
- ->GetAppsContainerView()
- ->scrollable_container_for_test()
- ->layer()
- ->layer_mask_layer();
- }
- private:
- base::test::ScopedFeatureList features_;
- std::unique_ptr<test::AppListTestModel> app_list_test_model_;
- std::unique_ptr<SearchModel> search_model_;
- };
- TEST_F(AppsContainerViewTest, ContinueSectionVisibleByDefault) {
- // Show the app list with enough items to make the continue section and
- // recent apps visible.
- auto* helper = GetAppListTestHelper();
- helper->AddContinueSuggestionResults(4);
- helper->AddRecentApps(5);
- helper->AddAppItems(5);
- TabletMode::Get()->SetEnabledForTest(true);
- // The continue section and recent apps are visible.
- EXPECT_TRUE(helper->GetFullscreenContinueSectionView()->GetVisible());
- EXPECT_TRUE(helper->GetFullscreenRecentAppsView()->GetVisible());
- EXPECT_TRUE(helper->GetAppsContainerView()->separator()->GetVisible());
- }
- TEST_F(AppsContainerViewTest, CanHideContinueSection) {
- // Show the app list with enough items to make the continue section and
- // recent apps visible.
- auto* helper = GetAppListTestHelper();
- helper->AddContinueSuggestionResults(4);
- helper->AddRecentApps(5);
- helper->AddAppItems(5);
- TabletMode::Get()->SetEnabledForTest(true);
- // Hide the continue section.
- Shell::Get()->app_list_controller()->SetHideContinueSection(true);
- // Continue section and recent apps are hidden.
- EXPECT_FALSE(helper->GetFullscreenContinueSectionView()->GetVisible());
- EXPECT_FALSE(helper->GetFullscreenRecentAppsView()->GetVisible());
- EXPECT_FALSE(helper->GetAppsContainerView()->separator()->GetVisible());
- }
- TEST_F(AppsContainerViewTest, HideContinueSectionPlaysAnimation) {
- // Show the app list without animation.
- ASSERT_EQ(ui::ScopedAnimationDurationScaleMode::duration_multiplier(),
- ui::ScopedAnimationDurationScaleMode::ZERO_DURATION);
- auto* helper = GetAppListTestHelper();
- helper->AddContinueSuggestionResults(4);
- helper->AddRecentApps(5);
- const int item_count = 5;
- helper->AddAppItems(item_count);
- TabletMode::Get()->SetEnabledForTest(true);
- // Enable animations.
- ui::ScopedAnimationDurationScaleMode duration(
- ui::ScopedAnimationDurationScaleMode::NON_ZERO_DURATION);
- // Hide the continue section.
- Shell::Get()->app_list_controller()->SetHideContinueSection(true);
- // Animation status is updated.
- auto* apps_grid_view = helper->GetRootPagedAppsGridView();
- EXPECT_EQ(apps_grid_view->grid_animation_status_for_test(),
- AppListGridAnimationStatus::kHideContinueSection);
- // Individial app items are animating their transforms.
- for (int i = 0; i < item_count; ++i) {
- SCOPED_TRACE(testing::Message() << "Item " << i);
- AppListItemView* item = apps_grid_view->GetItemViewAt(i);
- ASSERT_TRUE(item->layer());
- EXPECT_TRUE(item->layer()->GetAnimator()->is_animating());
- EXPECT_TRUE(item->layer()->GetAnimator()->IsAnimatingProperty(
- ui::LayerAnimationElement::TRANSFORM));
- }
- // Wait for the last item's animation to complete.
- AppListItemView* last_item = apps_grid_view->GetItemViewAt(item_count - 1);
- LayerAnimationStoppedWaiter().Wait(last_item->layer());
- // Animation status is updated.
- EXPECT_EQ(apps_grid_view->grid_animation_status_for_test(),
- AppListGridAnimationStatus::kEmpty);
- // Layers have been removed for all items.
- for (int i = 0; i < item_count; ++i) {
- SCOPED_TRACE(testing::Message() << "Item " << i);
- AppListItemView* item = apps_grid_view->GetItemViewAt(i);
- EXPECT_FALSE(item->layer());
- }
- }
- TEST_F(AppsContainerViewTest, CanShowContinueSection) {
- // Simulate a user with the continue section hidden on startup.
- Shell::Get()->app_list_controller()->SetHideContinueSection(true);
- // Show the app list with enough items to make the continue section and
- // recent apps visible.
- auto* helper = GetAppListTestHelper();
- helper->AddContinueSuggestionResults(4);
- helper->AddRecentApps(5);
- helper->AddAppItems(5);
- TabletMode::Get()->SetEnabledForTest(true);
- // Continue section and recent apps are hidden.
- EXPECT_FALSE(helper->GetFullscreenContinueSectionView()->GetVisible());
- EXPECT_FALSE(helper->GetFullscreenRecentAppsView()->GetVisible());
- EXPECT_FALSE(helper->GetAppsContainerView()->separator()->GetVisible());
- // Show the continue section.
- Shell::Get()->app_list_controller()->SetHideContinueSection(false);
- // The continue section and recent apps are visible.
- EXPECT_TRUE(helper->GetFullscreenContinueSectionView()->GetVisible());
- EXPECT_TRUE(helper->GetFullscreenRecentAppsView()->GetVisible());
- EXPECT_TRUE(helper->GetAppsContainerView()->separator()->GetVisible());
- }
- TEST_F(AppsContainerViewTest, ShowContinueSectionPlaysAnimation) {
- // Simulate a user with the continue section hidden on startup.
- Shell::Get()->app_list_controller()->SetHideContinueSection(true);
- // Show the app list with enough items to make the continue section and
- // recent apps visible.
- auto* helper = GetAppListTestHelper();
- helper->AddContinueSuggestionResults(4);
- helper->AddRecentApps(5);
- helper->AddAppItems(5);
- TabletMode::Get()->SetEnabledForTest(true);
- // Enable animations.
- ui::ScopedAnimationDurationScaleMode duration(
- ui::ScopedAnimationDurationScaleMode::NON_ZERO_DURATION);
- // Show the continue section.
- Shell::Get()->app_list_controller()->SetHideContinueSection(false);
- // Continue section is fading in.
- auto* continue_section = helper->GetFullscreenContinueSectionView();
- ASSERT_TRUE(continue_section->layer());
- EXPECT_TRUE(continue_section->layer()->GetAnimator()->is_animating());
- EXPECT_EQ(continue_section->layer()->opacity(), 0.0f);
- EXPECT_EQ(continue_section->layer()->GetTargetOpacity(), 1.0f);
- // Recent apps view is fading in.
- auto* recent_apps = helper->GetFullscreenRecentAppsView();
- ASSERT_TRUE(recent_apps->layer());
- EXPECT_TRUE(recent_apps->layer()->GetAnimator()->is_animating());
- EXPECT_EQ(recent_apps->layer()->opacity(), 0.0f);
- EXPECT_EQ(recent_apps->layer()->GetTargetOpacity(), 1.0f);
- // Separator view is fading in.
- auto* separator = helper->GetAppsContainerView()->separator();
- ASSERT_TRUE(separator->layer());
- EXPECT_TRUE(separator->layer()->GetAnimator()->is_animating());
- EXPECT_EQ(separator->layer()->opacity(), 0.0f);
- EXPECT_EQ(separator->layer()->GetTargetOpacity(), 1.0f);
- // Apps grid is animating its transform.
- auto* apps_grid_view = helper->GetRootPagedAppsGridView();
- ASSERT_TRUE(apps_grid_view->layer());
- EXPECT_TRUE(apps_grid_view->layer()->GetAnimator()->is_animating());
- EXPECT_TRUE(apps_grid_view->layer()->GetAnimator()->IsAnimatingProperty(
- ui::LayerAnimationElement::TRANSFORM));
- }
- TEST_F(AppsContainerViewTest, OpeningFolderRemovesOtherViewsFromAccessibility) {
- auto* helper = GetAppListTestHelper();
- helper->AddContinueSuggestionResults(4);
- helper->AddRecentApps(5);
- AddFolderWithApps(5);
- TabletMode::Get()->SetEnabledForTest(true);
- // Force the sorting toast to show.
- AppListController::Get()->UpdateAppListWithNewTemporarySortOrder(
- AppListSortOrder::kColor,
- /*animate=*/false, /*update_position_closure=*/base::OnceClosure());
- ASSERT_TRUE(GetToastContainerView()->GetToastButton());
- // Open the folder.
- AppListItemView* folder_item =
- helper->GetRootPagedAppsGridView()->GetItemViewAt(0);
- LeftClickOn(folder_item);
- // Note: For fullscreen app list, the search box is part of the focus cycle
- // when a folder is open.
- auto* continue_section = helper->GetFullscreenContinueSectionView();
- EXPECT_TRUE(continue_section->GetViewAccessibility().IsIgnored());
- EXPECT_TRUE(continue_section->GetViewAccessibility().IsLeaf());
- auto* recent_apps = helper->GetFullscreenRecentAppsView();
- EXPECT_TRUE(recent_apps->GetViewAccessibility().IsIgnored());
- EXPECT_TRUE(recent_apps->GetViewAccessibility().IsLeaf());
- auto* toast_container = GetToastContainerView();
- EXPECT_TRUE(toast_container->GetViewAccessibility().IsIgnored());
- EXPECT_TRUE(toast_container->GetViewAccessibility().IsLeaf());
- auto* apps_grid_view = helper->GetRootPagedAppsGridView();
- EXPECT_TRUE(apps_grid_view->GetViewAccessibility().IsIgnored());
- EXPECT_TRUE(apps_grid_view->GetViewAccessibility().IsLeaf());
- // Close the folder.
- PressAndReleaseKey(ui::VKEY_ESCAPE);
- EXPECT_FALSE(continue_section->GetViewAccessibility().IsIgnored());
- EXPECT_FALSE(continue_section->GetViewAccessibility().IsLeaf());
- EXPECT_FALSE(recent_apps->GetViewAccessibility().IsIgnored());
- EXPECT_FALSE(recent_apps->GetViewAccessibility().IsLeaf());
- EXPECT_FALSE(toast_container->GetViewAccessibility().IsIgnored());
- EXPECT_FALSE(toast_container->GetViewAccessibility().IsLeaf());
- EXPECT_FALSE(apps_grid_view->GetViewAccessibility().IsIgnored());
- EXPECT_FALSE(apps_grid_view->GetViewAccessibility().IsLeaf());
- }
- TEST_F(AppsContainerViewTest, UpdatesSelectedPageAfterFocusTraversal) {
- auto* helper = GetAppListTestHelper();
- helper->AddRecentApps(5);
- helper->AddAppItems(16);
- TabletMode::Get()->SetEnabledForTest(true);
- auto* apps_grid_view = helper->GetRootPagedAppsGridView();
- auto* recent_apps_view = helper->GetFullscreenRecentAppsView();
- auto* search_box = helper->GetSearchBoxView()->search_box();
- // Focus moves to the search box.
- PressDown();
- EXPECT_TRUE(search_box->HasFocus());
- EXPECT_EQ(GetSelectedPage(), 0);
- // Focus moves to the first item inside `RecentAppsView`.
- PressDown();
- EXPECT_TRUE(recent_apps_view->GetItemViewAt(0)->HasFocus());
- EXPECT_EQ(GetSelectedPage(), 0);
- // Focus moves to the first item / first row inside `PagedAppsGridView`.
- PressDown();
- EXPECT_TRUE(apps_grid_view->GetItemViewAt(0)->HasFocus());
- EXPECT_EQ(GetSelectedPage(), 0);
- // Focus moves to the first item / second row inside `PagedAppsGridView`.
- PressDown();
- EXPECT_TRUE(apps_grid_view->GetItemViewAt(5)->HasFocus());
- EXPECT_EQ(GetSelectedPage(), 0);
- // Focus moves to the first item / third row inside `PagedAppsGridView`.
- PressDown();
- EXPECT_TRUE(apps_grid_view->GetItemViewAt(10)->HasFocus());
- EXPECT_EQ(GetSelectedPage(), 0);
- // Focus moves to the first item / first row on the second page of
- // `PagedAppsGridView`.
- PressDown();
- EXPECT_TRUE(apps_grid_view->GetItemViewAt(15)->HasFocus());
- EXPECT_EQ(GetSelectedPage(), 1);
- // Focus moves to the search box, but second page stays active.
- PressDown();
- EXPECT_TRUE(search_box->HasFocus());
- EXPECT_EQ(GetSelectedPage(), 1);
- // Focus moves to the first item inside `RecentAppsView` and activates first
- // page.
- PressDown();
- EXPECT_TRUE(recent_apps_view->GetItemViewAt(0)->HasFocus());
- EXPECT_EQ(GetSelectedPage(), 0);
- }
- // Test that the gradient mask is created when the page drag begins, and
- // destroyed once the page drag has been released and completes.
- TEST_F(AppsContainerViewTest, StartPageDragThenRelease) {
- GetAppListTestHelper()->AddAppItems(23);
- TabletMode::Get()->SetEnabledForTest(true);
- auto* apps_grid_view = GetAppListTestHelper()->GetRootPagedAppsGridView();
- test::AppsGridViewTestApi test_api(apps_grid_view);
- EXPECT_FALSE(HasGradientMask());
- EXPECT_EQ(0, GetSelectedPage());
- EXPECT_EQ(2, GetTotalPages());
- TransitionWaiter transition_waiter(apps_grid_view->pagination_model());
- gfx::Point start_page_drag = test_api.GetViewAtIndex(GridIndex(0, 0))
- ->GetIconBoundsInScreen()
- .bottom_right();
- start_page_drag.Offset(10, 0);
- // Begin a touch and drag the page upward.
- auto* generator = GetEventGenerator();
- generator->set_current_screen_location(start_page_drag);
- generator->PressTouch();
- generator->MoveTouchBy(0, -20);
- // Move the touch down a bit so it does not register as a fling to the next
- // page.
- generator->MoveTouchBy(0, 1);
- // Gradient mask should exist during the page drag.
- EXPECT_TRUE(HasGradientMask());
- // End the page drag and wait for the page to animate back to the correct
- // position.
- generator->ReleaseTouch();
- transition_waiter.Wait();
- // The gradient mask should be removed after the end of the page animation.
- EXPECT_FALSE(HasGradientMask());
- EXPECT_EQ(0, GetSelectedPage());
- }
- } // namespace ash
|