blob: 2a75f060a13ff7591b3c2a34822b3a74bc5714b3 [file] [log] [blame]
// Copyright 2019 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "ash/app_list/app_list_controller_impl.h"
#include <string>
#include "ash/app_list/app_list_badge_controller.h"
#include "ash/app_list/app_list_bubble_presenter.h"
#include "ash/app_list/app_list_metrics.h"
#include "ash/app_list/app_list_presenter_impl.h"
#include "ash/app_list/test/app_list_test_helper.h"
#include "ash/app_list/views/app_list_bubble_view.h"
#include "ash/app_list/views/app_list_item_view.h"
#include "ash/app_list/views/app_list_main_view.h"
#include "ash/app_list/views/app_list_view.h"
#include "ash/app_list/views/apps_container_view.h"
#include "ash/app_list/views/apps_grid_view.h"
#include "ash/app_list/views/apps_grid_view_test_api.h"
#include "ash/app_list/views/contents_view.h"
#include "ash/app_list/views/paged_apps_grid_view.h"
#include "ash/app_list/views/search_box_view.h"
#include "ash/assistant/model/assistant_ui_model.h"
#include "ash/constants/ash_features.h"
#include "ash/constants/ash_pref_names.h"
#include "ash/keyboard/keyboard_controller_impl.h"
#include "ash/keyboard/ui/test/keyboard_test_util.h"
#include "ash/public/cpp/app_list/app_list_features.h"
#include "ash/public/cpp/assistant/controller/assistant_ui_controller.h"
#include "ash/public/cpp/session/session_types.h"
#include "ash/public/cpp/shelf_config.h"
#include "ash/public/cpp/shelf_item_delegate.h"
#include "ash/public/cpp/shelf_model.h"
#include "ash/public/cpp/shelf_types.h"
#include "ash/public/cpp/system_tray_test_api.h"
#include "ash/public/cpp/test/assistant_test_api.h"
#include "ash/public/cpp/test/shell_test_api.h"
#include "ash/public/cpp/test/test_shelf_item_delegate.h"
#include "ash/session/session_controller_impl.h"
#include "ash/shelf/shelf.h"
#include "ash/shelf/shelf_view.h"
#include "ash/shelf/shelf_view_test_api.h"
#include "ash/shelf/shelf_widget.h"
#include "ash/shell.h"
#include "ash/test/ash_test_base.h"
#include "ash/wm/overview/overview_controller.h"
#include "ash/wm/tablet_mode/tablet_mode_controller.h"
#include "ash/wm/window_state.h"
#include "ash/wm/window_util.h"
#include "base/i18n/number_formatting.h"
#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
#include "base/test/scoped_feature_list.h"
#include "components/session_manager/session_manager_types.h"
#include "ui/compositor/layer.h"
#include "ui/compositor/scoped_animation_duration_scale_mode.h"
#include "ui/compositor/test/layer_animation_stopped_waiter.h"
#include "ui/events/test/event_generator.h"
#include "ui/message_center/message_center.h"
#include "ui/message_center/views/message_popup_view.h"
#include "ui/views/controls/textfield/textfield_test_api.h"
#include "ui/views/test/views_test_utils.h"
namespace ash {
namespace {
void PressHomeButton() {
Shell::Get()->app_list_controller()->ToggleAppList(
display::Screen::GetScreen()->GetPrimaryDisplay().id(),
AppListShowSource::kShelfButton, base::TimeTicks());
}
bool IsTabletMode() {
return Shell::Get()->tablet_mode_controller()->InTabletMode();
}
AppListModel* GetAppListModel() {
return AppListModelProvider::Get()->model();
}
AppListView* GetAppListView() {
return Shell::Get()->app_list_controller()->fullscreen_presenter()->GetView();
}
ContentsView* GetContentsView() {
return GetAppListView()->app_list_main_view()->contents_view();
}
SearchBoxView* GetSearchBoxView() {
return GetContentsView()->GetSearchBoxView();
}
aura::Window* GetVirtualKeyboardWindow() {
return Shell::Get()
->keyboard_controller()
->keyboard_ui_controller()
->GetKeyboardWindow();
}
AppsContainerView* GetAppsContainerView() {
return GetContentsView()->apps_container_view();
}
PagedAppsGridView* GetAppsGridView() {
return GetAppsContainerView()->apps_grid_view();
}
void ShowAppListNow(AppListViewState state) {
Shell::Get()->app_list_controller()->fullscreen_presenter()->Show(
state, display::Screen::GetScreen()->GetPrimaryDisplay().id(),
base::TimeTicks::Now(), /*show_source*/ absl::nullopt);
}
void DismissAppListNow() {
Shell::Get()->app_list_controller()->fullscreen_presenter()->Dismiss(
base::TimeTicks::Now());
}
void EnableTabletMode() {
Shell::Get()->tablet_mode_controller()->SetEnabledForTest(true);
}
class ShelfItemFactoryFake : public ShelfModel::ShelfItemFactory {
public:
virtual ~ShelfItemFactoryFake() = default;
bool CreateShelfItemForAppId(
const std::string& app_id,
ShelfItem* item,
std::unique_ptr<ShelfItemDelegate>* delegate) override {
*item = ShelfItem();
item->id = ShelfID(app_id);
*delegate = std::make_unique<TestShelfItemDelegate>(item->id);
return true;
}
};
} // namespace
class AppListControllerImplTest : public AshTestBase {
public:
AppListControllerImplTest() = default;
AppListControllerImplTest(const AppListControllerImplTest&) = delete;
AppListControllerImplTest& operator=(const AppListControllerImplTest&) =
delete;
~AppListControllerImplTest() override = default;
void SetUp() override {
AshTestBase::SetUp();
shelf_item_factory_ = std::make_unique<ShelfItemFactoryFake>();
ShelfModel::Get()->SetShelfItemFactory(shelf_item_factory_.get());
}
void TearDown() override {
ShelfModel::Get()->SetShelfItemFactory(nullptr);
AshTestBase::TearDown();
}
void PopulateItem(int num) {
AppListModel* const model = GetAppListModel();
for (int i = 0; i < num; i++) {
AppListItem* item = model->AddItem(std::make_unique<AppListItem>(
"app_id" +
base::UTF16ToUTF8(base::FormatNumber(populated_item_count_))));
// Give each item a name so that the accessibility paint checks pass.
// (Focusable items should have accessible names.)
model->SetItemName(item, item->id());
++populated_item_count_;
}
}
bool IsAppListBoundsAnimationRunning() {
AppListView* app_list_view = GetAppListTestHelper()->GetAppListView();
ui::Layer* widget_layer =
app_list_view ? app_list_view->GetWidget()->GetLayer() : nullptr;
return widget_layer && widget_layer->GetAnimator()->is_animating();
}
private:
// The count of the items created by `PopulateItem()`.
int populated_item_count_ = 0;
std::unique_ptr<ShelfItemFactoryFake> shelf_item_factory_;
};
// Tests that the AppList hides when shelf alignment changes. This necessary
// because the AppList is shown with certain assumptions based on shelf
// orientation.
TEST_F(AppListControllerImplTest, AppListHiddenWhenShelfAlignmentChanges) {
Shelf* const shelf = AshTestBase::GetPrimaryShelf();
shelf->SetAlignment(ShelfAlignment::kBottom);
const std::vector<ShelfAlignment> alignments(
{ShelfAlignment::kLeft, ShelfAlignment::kRight, ShelfAlignment::kBottom});
for (ShelfAlignment alignment : alignments) {
ShowAppListNow(AppListViewState::kFullscreenAllApps);
EXPECT_TRUE(Shell::Get()
->app_list_controller()
->fullscreen_presenter()
->IsVisibleDeprecated());
shelf->SetAlignment(alignment);
EXPECT_EQ(AppListViewState::kClosed, GetAppListView()->app_list_state());
}
}
// Verifies that the dragged item has the correct focusable siblings after drag
// (https://crbug.com/990071).
TEST_F(AppListControllerImplTest, CheckTabOrderAfterDragIconToShelf) {
// Adds three items to AppsGridView.
PopulateItem(3);
// Shows the app list in fullscreen.
ShowAppListNow(AppListViewState::kFullscreenAllApps);
ASSERT_EQ(AppListViewState::kFullscreenAllApps,
GetAppListView()->app_list_state());
test::AppsGridViewTestApi apps_grid_view_test_api(GetAppsGridView());
const AppListItemView* item1 =
apps_grid_view_test_api.GetViewAtIndex(GridIndex(0, 0));
AppListItemView* item2 =
apps_grid_view_test_api.GetViewAtIndex(GridIndex(0, 1));
const AppListItemView* item3 =
apps_grid_view_test_api.GetViewAtIndex(GridIndex(0, 2));
// Verifies that AppListItemView has the correct focusable siblings before
// drag.
ASSERT_EQ(item1, item2->GetPreviousFocusableView());
ASSERT_EQ(item3, item2->GetNextFocusableView());
// Pins |item2| by dragging it to ShelfView.
ShelfView* shelf_view = GetPrimaryShelf()->GetShelfViewForTesting();
ASSERT_EQ(0u, shelf_view->view_model()->view_size());
GetEventGenerator()->MoveMouseTo(item2->GetBoundsInScreen().CenterPoint());
GetEventGenerator()->PressLeftButton();
item2->FireMouseDragTimerForTest();
GetEventGenerator()->MoveMouseTo(
shelf_view->GetBoundsInScreen().CenterPoint());
ASSERT_TRUE(GetAppsGridView()->FireDragToShelfTimerForTest());
GetEventGenerator()->ReleaseLeftButton();
ASSERT_EQ(1u, shelf_view->view_model()->view_size());
// Verifies that the dragged item has the correct previous/next focusable
// view after drag.
EXPECT_EQ(item1, item2->GetPreviousFocusableView());
EXPECT_EQ(item3, item2->GetNextFocusableView());
}
TEST_F(AppListControllerImplTest, PageResetByTimerInTabletMode) {
Shell::Get()->tablet_mode_controller()->SetEnabledForTest(true);
PopulateItem(30);
PagedAppsGridView* apps_grid_view = GetAppsGridView();
apps_grid_view->pagination_model()->SelectPage(1, false /* animate */);
// Create a test window to hide the app list.
std::unique_ptr<views::Widget> dummy = CreateTestWidget();
EXPECT_FALSE(Shell::Get()->app_list_controller()->IsVisible());
// When timer is not skipped the selected page should not change when app list
// is closed.
EXPECT_EQ(1, apps_grid_view->pagination_model()->selected_page());
// Skip the page reset timer to simulate timer exipration.
GetAppListView()->SetSkipPageResetTimerForTesting(true);
dummy->Minimize();
EXPECT_TRUE(Shell::Get()->app_list_controller()->IsVisible());
EXPECT_EQ(1, apps_grid_view->pagination_model()->selected_page());
dummy->Show();
EXPECT_FALSE(Shell::Get()->app_list_controller()->IsVisible());
// Once the app list is closed, the page should be reset when the timer is
// skipped.
EXPECT_EQ(0, apps_grid_view->pagination_model()->selected_page());
}
TEST_F(AppListControllerImplTest, PagePersistanceTabletModeTest) {
PopulateItem(30);
Shell::Get()->tablet_mode_controller()->SetEnabledForTest(true);
EXPECT_TRUE(Shell::Get()->app_list_controller()->IsVisible());
PagedAppsGridView* const apps_grid_view = GetAppsGridView();
apps_grid_view->pagination_model()->SelectPage(1, false /* animate */);
// Close and re-open the app list to ensure the current page persists.
std::unique_ptr<views::Widget> dummy = CreateTestWidget();
EXPECT_FALSE(Shell::Get()->app_list_controller()->IsVisible());
dummy->Minimize();
EXPECT_TRUE(Shell::Get()->app_list_controller()->IsVisible());
// The current page should not be reset for the tablet mode app list.
EXPECT_EQ(1, apps_grid_view->pagination_model()->selected_page());
}
// Verifies that the the virtual keyboard does not get shown if the search box
// is activated by user typing when the app list in the fullscreen state in
// tablet mode.
TEST_F(AppListControllerImplTest, VirtualKeyboardNotShownWhenUserStartsTyping) {
Shell::Get()->keyboard_controller()->SetEnableFlag(
keyboard::KeyboardEnableFlag::kShelfEnabled);
Shell::Get()->tablet_mode_controller()->SetEnabledForTest(true);
// Show the AppListView, then simulate a key press - verify that the virtual
// keyboard is not shown.
ShowAppListNow(AppListViewState::kFullscreenAllApps);
EXPECT_EQ(AppListViewState::kFullscreenAllApps,
GetAppListView()->app_list_state());
PressAndReleaseKey(ui::KeyboardCode::VKEY_0);
EXPECT_EQ(AppListViewState::kFullscreenSearch,
GetAppListView()->app_list_state());
base::RunLoop().RunUntilIdle();
EXPECT_FALSE(GetVirtualKeyboardWindow()->IsVisible());
// The keyboard should get shown if the user taps on the search box.
GestureTapOn(GetAppListView()->search_box_view());
ASSERT_TRUE(keyboard::WaitUntilShown());
DismissAppListNow();
base::RunLoop().RunUntilIdle();
EXPECT_EQ(nullptr, GetVirtualKeyboardWindow());
}
#if defined(ADDRESS_SANITIZER)
#define MAYBE_CloseNotificationWithAppListShown \
DISABLED_CloseNotificationWithAppListShown
#else
#define MAYBE_CloseNotificationWithAppListShown \
CloseNotificationWithAppListShown
#endif
// Verifies that closing notification by gesture should not dismiss the AppList.
// (see https://crbug.com/948344)
// TODO(crbug.com/1120501): Test is flaky on ASAN builds.
TEST_F(AppListControllerImplTest, MAYBE_CloseNotificationWithAppListShown) {
ShowAppListNow(AppListViewState::kFullscreenAllApps);
// Add one notification.
ASSERT_EQ(
0u, message_center::MessageCenter::Get()->GetPopupNotifications().size());
const std::string notification_id("id");
const std::string notification_title("title");
message_center::MessageCenter::Get()->AddNotification(
std::make_unique<message_center::Notification>(
message_center::NOTIFICATION_TYPE_SIMPLE, notification_id,
base::UTF8ToUTF16(notification_title), u"test message",
ui::ImageModel(), std::u16string() /* display_source */, GURL(),
message_center::NotifierId(), message_center::RichNotificationData(),
new message_center::NotificationDelegate()));
base::RunLoop().RunUntilIdle();
ASSERT_EQ(
1u, message_center::MessageCenter::Get()->GetPopupNotifications().size());
// Calculate the drag start point and end point.
SystemTrayTestApi test_api;
message_center::MessagePopupView* popup_view =
test_api.GetPopupViewForNotificationID(notification_id);
ASSERT_TRUE(popup_view);
gfx::Rect bounds_in_screen = popup_view->GetBoundsInScreen();
const gfx::Point drag_start = bounds_in_screen.left_center();
const gfx::Point drag_end = bounds_in_screen.right_center();
// Swipe away notification by gesture. Verifies that AppListView still shows.
ui::test::EventGenerator* event_generator = GetEventGenerator();
event_generator->GestureScrollSequence(drag_start, drag_end,
base::Microseconds(500), 10);
base::RunLoop().RunUntilIdle();
EXPECT_TRUE(GetAppListView());
EXPECT_EQ(
0u, message_center::MessageCenter::Get()->GetPopupNotifications().size());
}
// Verifiy that when showing the launcher, the virtual keyboard dismissed before
// will not show automatically due to the feature called "transient blur" (see
// https://crbug.com/1057320).
TEST_F(AppListControllerImplTest,
TransientBlurIsNotTriggeredWhenShowingLauncher) {
// Enable animation.
ui::ScopedAnimationDurationScaleMode non_zero_duration(
ui::ScopedAnimationDurationScaleMode::NORMAL_DURATION);
// Enable virtual keyboard.
KeyboardController* const keyboard_controller =
Shell::Get()->keyboard_controller();
keyboard_controller->SetEnableFlag(
keyboard::KeyboardEnableFlag::kCommandLineEnabled);
// Create |window1| which contains a textfield as child view.
std::unique_ptr<aura::Window> window1 =
AshTestBase::CreateTestWindow(gfx::Rect(0, 0, 200, 200));
auto* widget = views::Widget::GetWidgetForNativeView(window1.get());
std::unique_ptr<views::Textfield> text_field =
std::make_unique<views::Textfield>();
// Focusable views need an accessible name to pass the accessibility paint
// checks.
text_field->SetAccessibleName(u"Name");
// Note that the bounds of |text_field| cannot be too small. Otherwise, it
// may not receive the gesture event.
text_field->SetBoundsRect(gfx::Rect(0, 0, 100, 100));
const auto* text_field_p = text_field.get();
widget->GetRootView()->AddChildView(std::move(text_field));
wm::ActivateWindow(window1.get());
widget->Show();
// Create |window2|.
std::unique_ptr<aura::Window> window2 =
AshTestBase::CreateTestWindow(gfx::Rect(200, 0, 200, 200));
window2->Show();
// Tap at the textfield in |window1|. The virtual keyboard should be visible.
GestureTapOn(text_field_p);
ASSERT_TRUE(keyboard::WaitUntilShown());
// Tap at the center of |window2| to hide the virtual keyboard.
GetEventGenerator()->GestureTapAt(window2->GetBoundsInScreen().CenterPoint());
ASSERT_TRUE(keyboard::WaitUntilHidden());
// Press the home button to show the launcher. Wait for the animation of
// launcher to finish. Note that the launcher does not exist before toggling
// the home button.
PressHomeButton();
const base::TimeDelta delta = base::Milliseconds(200);
do {
base::RunLoop run_loop;
base::SingleThreadTaskRunner::GetCurrentDefault()->PostDelayedTask(
FROM_HERE, run_loop.QuitClosure(), delta);
run_loop.Run();
} while (IsAppListBoundsAnimationRunning());
// Expect that the virtual keyboard is invisible when the launcher shows.
EXPECT_FALSE(keyboard_controller->IsKeyboardVisible());
}
// Regression test for https://crbug.com/1073548
// Verifies that app list shown from overview after toggling tablet mode can be
// closed.
TEST_F(AppListControllerImplTest,
CloseAppListShownFromOverviewAfterTabletExit) {
auto* shell = Shell::Get();
auto* tablet_mode_controller = shell->tablet_mode_controller();
auto* controller = Shell::Get()->app_list_controller();
// Move to tablet mode and back.
tablet_mode_controller->SetEnabledForTest(true);
tablet_mode_controller->SetEnabledForTest(false);
std::unique_ptr<aura::Window> w(
AshTestBase::CreateTestWindow(gfx::Rect(0, 0, 400, 400)));
EnterOverview();
// Press home button - verify overview exits and the app list is shown.
PressHomeButton();
EXPECT_FALSE(shell->overview_controller()->InOverviewSession());
EXPECT_TRUE(controller->bubble_presenter_for_test()->IsShowing());
EXPECT_TRUE(controller->IsVisible());
// Pressing home button again should close the app list.
PressHomeButton();
EXPECT_FALSE(controller->bubble_presenter_for_test()->IsShowing());
EXPECT_FALSE(controller->IsVisible());
}
// Tests that swapping out an AppListModel (simulating a profile swap with
// multiprofile enabled) drops all references to previous folders (see
// https://crbug.com/1130901).
TEST_F(AppListControllerImplTest, SimulateProfileSwapNoCrashOnDestruct) {
// Add a folder, whose AppListItemList the AppListModel will observe.
AppListModel* model = GetAppListModel();
const std::string folder_id("folder_1");
model->CreateFolderItem(folder_id);
for (int i = 0; i < 2; ++i) {
auto item = std::make_unique<AppListItem>(base::StringPrintf("app_%d", i));
model->AddItemToFolder(std::move(item), folder_id);
}
// Set a new model, simulating profile switching in multi-profile mode. This
// should cleanly drop the reference to the folder added earlier.
auto updated_model = std::make_unique<test::AppListTestModel>();
auto update_search_model = std::make_unique<SearchModel>();
Shell::Get()->app_list_controller()->SetActiveModel(
/*profile_id=*/1, updated_model.get(), update_search_model.get());
Shell::Get()->app_list_controller()->ClearActiveModel();
updated_model.reset();
// Test that there is no crash on ~AppListModel() when the test finishes.
}
class AppListControllerImplTestWithNotificationBadging
: public AppListControllerImplTest {
public:
AppListControllerImplTestWithNotificationBadging() = default;
AppListControllerImplTestWithNotificationBadging(
const AppListControllerImplTestWithNotificationBadging& other) = delete;
AppListControllerImplTestWithNotificationBadging& operator=(
const AppListControllerImplTestWithNotificationBadging& other) = delete;
~AppListControllerImplTestWithNotificationBadging() override = default;
void UpdateAppHasBadge(const std::string& app_id, bool app_has_badge) {
AppListControllerImpl* controller = Shell::Get()->app_list_controller();
AccountId account_id = AccountId::FromUserEmail("test@gmail.com");
apps::App test_app(apps::AppType::kArc, app_id);
test_app.has_badge = app_has_badge;
apps::AppUpdate test_update(nullptr, /*delta=*/&test_app, account_id);
controller->badge_controller_for_test()->OnAppUpdate(test_update);
}
};
// Tests that when an app has an update to its notification badge, the change
// gets propagated to the corresponding AppListItemView.
TEST_F(AppListControllerImplTestWithNotificationBadging,
NotificationBadgeUpdateTest) {
PopulateItem(1);
ShowAppListNow(AppListViewState::kFullscreenAllApps);
test::AppsGridViewTestApi apps_grid_view_test_api(GetAppsGridView());
const AppListItemView* item_view =
apps_grid_view_test_api.GetViewAtIndex(GridIndex(0, 0));
ASSERT_TRUE(item_view);
const std::string app_id = item_view->item()->id();
EXPECT_FALSE(item_view->IsNotificationIndicatorShownForTest());
UpdateAppHasBadge(app_id, /*app_has_badge=*/true);
EXPECT_TRUE(item_view->IsNotificationIndicatorShownForTest());
UpdateAppHasBadge(app_id, /*app_has_badge=*/false);
EXPECT_FALSE(item_view->IsNotificationIndicatorShownForTest());
}
TEST_F(AppListControllerImplTestWithNotificationBadging,
NotificationBadgeUpdateForFolderTest) {
std::string folder_id = "folder_1";
AppListModel* model = GetAppListModel();
model->CreateFolderItem(folder_id);
model->AddItemToFolder(std::make_unique<AppListItem>("app_1"), folder_id);
model->AddItemToFolder(std::make_unique<AppListItem>("app_2"), folder_id);
ShowAppListNow(AppListViewState::kFullscreenAllApps);
test::AppsGridViewTestApi apps_grid_view_test_api(GetAppsGridView());
const AppListItemView* folder_view =
apps_grid_view_test_api.GetViewAtIndex(GridIndex(0, 0));
ASSERT_TRUE(folder_view);
EXPECT_FALSE(folder_view->IsNotificationIndicatorShownForTest());
UpdateAppHasBadge("app_1", /*app_has_badge=*/true);
EXPECT_TRUE(folder_view->IsNotificationIndicatorShownForTest());
UpdateAppHasBadge("app_2", /*app_has_badge=*/true);
EXPECT_TRUE(folder_view->IsNotificationIndicatorShownForTest());
UpdateAppHasBadge("app_1", /*app_has_badge=*/false);
EXPECT_TRUE(folder_view->IsNotificationIndicatorShownForTest());
UpdateAppHasBadge("app_2", /*app_has_badge=*/false);
EXPECT_FALSE(folder_view->IsNotificationIndicatorShownForTest());
}
TEST_F(AppListControllerImplTestWithNotificationBadging,
NotificationBadgeUpdateAfterAddingRemovingAppTest) {
std::string folder_id = "folder_1";
AppListModel* model = GetAppListModel();
model->CreateFolderItem(folder_id);
AppListItem* app = model->AddItem(std::make_unique<AppListItem>("app_1"));
model->AddItemToFolder(std::make_unique<AppListItem>("app_2"), folder_id);
// Give this item a name so that the accessibility paint checks pass.
// (Focusable items should have accessible names.)
GetAppListModel()->SetItemName(app, app->id());
ShowAppListNow(AppListViewState::kFullscreenAllApps);
test::AppsGridViewTestApi apps_grid_view_test_api(GetAppsGridView());
const AppListItemView* folder_view =
apps_grid_view_test_api.GetViewAtIndex(GridIndex(0, 0));
ASSERT_TRUE(folder_view);
EXPECT_FALSE(folder_view->IsNotificationIndicatorShownForTest());
UpdateAppHasBadge("app_1", /*app_has_badge=*/true);
EXPECT_FALSE(folder_view->IsNotificationIndicatorShownForTest());
model->MoveItemToFolder(app, folder_id);
EXPECT_TRUE(folder_view->IsNotificationIndicatorShownForTest());
model->MoveItemToRootAt(app, model->FindFolderItem(folder_id)->position());
EXPECT_FALSE(folder_view->IsNotificationIndicatorShownForTest());
}
// Verifies that the pinned app should still show after canceling the drag from
// AppsGridView to Shelf (https://crbug.com/1021768).
TEST_F(AppListControllerImplTest, DragItemFromAppsGridView) {
// Turn on the tablet mode.
Shell::Get()->tablet_mode_controller()->SetEnabledForTest(true);
EXPECT_TRUE(IsTabletMode());
Shelf* const shelf = GetPrimaryShelf();
// Add icons with the same app id to Shelf and AppsGridView respectively.
ShelfViewTestAPI shelf_view_test_api(shelf->GetShelfViewForTesting());
std::string app_id = shelf_view_test_api.AddItem(TYPE_PINNED_APP).app_id;
AppListItem* item =
GetAppListModel()->AddItem(std::make_unique<AppListItem>(app_id));
// Give each item a name so that the accessibility paint checks pass.
// (Focusable items should have accessible names.)
GetAppListModel()->SetItemName(item, item->id());
AppsGridView* apps_grid_view = GetAppsGridView();
views::test::RunScheduledLayout(apps_grid_view);
AppListItemView* app_list_item_view =
test::AppsGridViewTestApi(apps_grid_view).GetViewAtIndex(GridIndex(0, 0));
views::View* shelf_icon_view =
shelf->GetShelfViewForTesting()->view_model()->view_at(0);
// Drag the app icon from AppsGridView to Shelf. Then move the icon back to
// AppsGridView before drag ends.
GetEventGenerator()->MoveMouseTo(
app_list_item_view->GetBoundsInScreen().CenterPoint());
GetEventGenerator()->PressLeftButton();
app_list_item_view->FireMouseDragTimerForTest();
GetEventGenerator()->MoveMouseTo(
shelf_icon_view->GetBoundsInScreen().CenterPoint());
GetEventGenerator()->MoveMouseTo(
apps_grid_view->GetBoundsInScreen().CenterPoint());
GetEventGenerator()->ReleaseLeftButton();
// The icon's opacity updates at the end of animation.
shelf_view_test_api.RunMessageLoopUntilAnimationsDone();
// The icon is pinned before drag starts. So the shelf icon should show in
// spite that drag is canceled.
EXPECT_TRUE(shelf_icon_view->GetVisible());
EXPECT_EQ(1.0f, shelf_icon_view->layer()->opacity());
}
TEST_F(AppListControllerImplTest, OnlyMinimizeCycleListWindows) {
std::unique_ptr<aura::Window> w1(CreateTestWindow(gfx::Rect(0, 0, 400, 400)));
std::unique_ptr<aura::Window> w2(CreateTestWindow(
gfx::Rect(0, 0, 400, 400), aura::client::WINDOW_TYPE_POPUP));
Shell::Get()->tablet_mode_controller()->SetEnabledForTest(true);
std::unique_ptr<ui::Event> test_event = std::make_unique<ui::KeyEvent>(
ui::EventType::ET_MOUSE_PRESSED, ui::VKEY_UNKNOWN, ui::EF_NONE);
Shell::Get()->app_list_controller()->GoHome(GetPrimaryDisplay().id());
EXPECT_TRUE(WindowState::Get(w1.get())->IsMinimized());
EXPECT_FALSE(WindowState::Get(w2.get())->IsMinimized());
}
// Tests that the home screen is visible after rotating the screen in overview
// mode.
TEST_F(AppListControllerImplTest,
HomeScreenVisibleAfterDisplayUpdateInOverview) {
Shell::Get()->tablet_mode_controller()->SetEnabledForTest(true);
EnterOverview();
// Trigger a display configuration change, this simulates screen rotation.
Shell::Get()->app_list_controller()->OnDisplayConfigurationChanged();
// End overview mode, the home launcher should be visible.
ExitOverview();
ShellTestApi().WaitForOverviewAnimationState(
OverviewAnimationState::kExitAnimationComplete);
EXPECT_TRUE(
Shell::Get()->app_list_controller()->GetHomeScreenWindow()->IsVisible());
}
TEST_F(AppListControllerImplTest, CreatePage) {
ShowAppListNow(AppListViewState::kFullscreenAllApps);
PagedAppsGridView* apps_grid_view = GetAppsGridView();
test::AppsGridViewTestApi test_api(apps_grid_view);
PopulateItem(test_api.TilesPerPageInPagedGrid(0));
EXPECT_EQ(1, apps_grid_view->pagination_model()->total_pages());
// Add an extra item and verify that the page count is 2 now.
PopulateItem(1);
EXPECT_EQ(2, apps_grid_view->pagination_model()->total_pages());
}
// The test parameter indicates whether the shelf should auto-hide. In either
// case the animation behaviors should be the same.
// TODO(crbug.com/1344199): Remove after flipping
// `kAnimateScaleOnTabletModeTransition`.
class AppListAnimationTest : public AshTestBase,
public testing::WithParamInterface<bool> {
public:
AppListAnimationTest() {
scoped_feature_list_.InitAndDisableFeature(
app_list_features::kAnimateScaleOnTabletModeTransition);
}
AppListAnimationTest(const AppListAnimationTest&) = delete;
AppListAnimationTest& operator=(const AppListAnimationTest&) = delete;
~AppListAnimationTest() override = default;
void SetUp() override {
AshTestBase::SetUp();
Shelf* const shelf = AshTestBase::GetPrimaryShelf();
shelf->SetAlignment(ShelfAlignment::kBottom);
if (GetParam()) {
shelf->SetAutoHideBehavior(ShelfAutoHideBehavior::kAlways);
}
// The shelf should be shown at this point despite auto hide behavior, given
// that no windows are shown.
shown_shelf_bounds_ = shelf->shelf_widget()->GetWindowBoundsInScreen();
}
int GetAppListCurrentTop() {
return GetAppListView()
->GetWidget()
->GetLayer()
->transform()
.MapPoint(GetAppListView()->GetBoundsInScreen().top_center())
.y();
}
int GetAppListTargetTop() {
return GetAppListView()
->GetWidget()
->GetLayer()
->GetTargetTransform()
.MapPoint(GetAppListView()->GetBoundsInScreen().top_center())
.y();
}
int shown_shelf_top() const { return shown_shelf_bounds_.y(); }
// The offset that should be animated between kFullscreenAllApps and kClosed
// app list view states - the vertical distance between shelf top (in shown
// state) and the app list top in fullscreen state.
int FullscreenHeightOffset() const {
return shown_shelf_bounds_.y() - FullscreenHeightTop();
}
// The app list view y coordinate in peeking state.
int FullscreenHeightTop() const {
return shown_shelf_bounds_.bottom() -
GetAppListView()->GetHeightForState(
AppListViewState::kFullscreenAllApps);
}
private:
// Set during setup.
gfx::Rect shown_shelf_bounds_;
base::test::ScopedFeatureList scoped_feature_list_;
};
INSTANTIATE_TEST_SUITE_P(AutoHideShelf, AppListAnimationTest, testing::Bool());
// Tests app list animation to fullscreen state.
TEST_P(AppListAnimationTest, AppListShowFullscreenAnimation) {
// Set the normal transition duration so tests can easily determine intended
// animation length, and calculate expected app list position at different
// animation step points. Also, prevents the app list view to snapping to the
// final position.
ui::ScopedAnimationDurationScaleMode non_zero_duration_mode(
ui::ScopedAnimationDurationScaleMode::NORMAL_DURATION);
ShowAppListNow(AppListViewState::kFullscreenAllApps);
// Verify that the app list view's top matches the shown shelf top as the show
// animation starts.
EXPECT_EQ(shown_shelf_top(), GetAppListCurrentTop());
EXPECT_EQ(FullscreenHeightTop(), GetAppListTargetTop());
}
// Tests app list animation from fullscreen to closed state.
TEST_P(AppListAnimationTest, AppListCloseFromFullscreenAnimation) {
ShowAppListNow(AppListViewState::kFullscreenAllApps);
// Set the normal transition duration so tests can easily determine intended
// animation length, and calculate expected app list position at different
// animation step points. Also, prevents the app list view to snapping to the
// final position.
ui::ScopedAnimationDurationScaleMode non_zero_duration_mode(
ui::ScopedAnimationDurationScaleMode::NORMAL_DURATION);
// Dismiss app list, initial app list position should be at fullscreen height.
const int offset_to_animate = FullscreenHeightOffset();
DismissAppListNow();
EXPECT_EQ(shown_shelf_top() - offset_to_animate, GetAppListCurrentTop());
EXPECT_EQ(shown_shelf_top(), GetAppListTargetTop());
}
// Tests app list close animation when app list gets dismissed while animating
// to fullscreen state.
TEST_P(AppListAnimationTest, AppListDismissWhileShowingFullscreen) {
// Set the normal transition duration so tests can easily determine intended
// animation length, and calculate expected app list position at different
// animation step points. Also, prevents the app list view to snapping to the
// final position.
ui::ScopedAnimationDurationScaleMode non_zero_duration_mode(
ui::ScopedAnimationDurationScaleMode::NORMAL_DURATION);
ShowAppListNow(AppListViewState::kFullscreenAllApps);
// Verify that the app list view's top matches the shown shelf top as the show
// animation starts.
EXPECT_EQ(shown_shelf_top(), GetAppListCurrentTop());
EXPECT_EQ(FullscreenHeightTop(), GetAppListTargetTop());
// Start dismissing app list. Verify the new animation starts at the same
// point the show animation ended.
DismissAppListNow();
EXPECT_EQ(shown_shelf_top(), GetAppListTargetTop());
}
// Tests app list animation when show is requested while app list close
// animation is in progress.
TEST_P(AppListAnimationTest, AppListShowFullscreenWhileClosing) {
// Show app list while animations are still instantanious.
ShowAppListNow(AppListViewState::kFullscreenAllApps);
// Set the normal transition duration so tests can easily determine intended
// animation length, and calculate expected app list position at different
// animation step points. Also, prevents the app list view to snapping to the
// final position.
ui::ScopedAnimationDurationScaleMode non_zero_duration_mode(
ui::ScopedAnimationDurationScaleMode::NORMAL_DURATION);
int offset_to_animate = FullscreenHeightOffset();
DismissAppListNow();
// Verify that the app list view's top initially matches the fullscreen
// height.
EXPECT_EQ(shown_shelf_top() - offset_to_animate, GetAppListCurrentTop());
EXPECT_EQ(shown_shelf_top(), GetAppListTargetTop());
// Start showing the app list. Verify the new animation starts at the same
// point the show animation ended.
ShowAppListNow(AppListViewState::kFullscreenAllApps);
EXPECT_EQ(FullscreenHeightTop(), GetAppListTargetTop());
}
// Tests that how search box opacity is animated when the app list is shown and
// closed.
TEST_P(AppListAnimationTest, SearchBoxOpacityDuringShowAndClose) {
// Set a transition duration that prevents the app list view from snapping to
// the final position.
ui::ScopedAnimationDurationScaleMode non_zero_duration_mode(
ui::ScopedAnimationDurationScaleMode::NON_ZERO_DURATION);
Shell::Get()->tablet_mode_controller()->SetEnabledForTest(true);
SearchBoxView* const search_box = GetSearchBoxView();
// The search box opacity should start at 0, and animate to 1.
EXPECT_EQ(0.0f, search_box->layer()->opacity());
EXPECT_EQ(1.0f, search_box->layer()->GetTargetOpacity());
// If the app list is closed while the animation is still in progress, the
// search box opacity should animate from the current opacity.
Shell::Get()->tablet_mode_controller()->SetEnabledForTest(false);
EXPECT_EQ(0.0f, search_box->layer()->opacity());
EXPECT_EQ(0.0f, search_box->layer()->GetTargetOpacity());
search_box->layer()->GetAnimator()->StopAnimating();
// When show again, verify the app list animates from 0 opacity again.
Shell::Get()->tablet_mode_controller()->SetEnabledForTest(true);
EXPECT_EQ(0.0f, search_box->layer()->opacity());
EXPECT_EQ(1.0f, search_box->layer()->GetTargetOpacity());
search_box->layer()->GetAnimator()->StopAnimating();
EXPECT_EQ(1.0f, search_box->layer()->opacity());
// Search box opacity animates from the current (full opacity) when closed
// from shown state.
Shell::Get()->tablet_mode_controller()->SetEnabledForTest(false);
EXPECT_EQ(1.0f, search_box->layer()->opacity());
EXPECT_EQ(0.0f, search_box->layer()->GetTargetOpacity());
// If the app list is show again during close animation, the search box
// opacity should animate from the current value.
Shell::Get()->tablet_mode_controller()->SetEnabledForTest(true);
EXPECT_EQ(1.0f, search_box->layer()->opacity());
EXPECT_EQ(1.0f, search_box->layer()->GetTargetOpacity());
}
TEST_F(AppListControllerImplTest, ShowAppListOpensBubble) {
auto* controller = Shell::Get()->app_list_controller();
controller->ShowAppList(AppListShowSource::kSearchKey);
EXPECT_TRUE(controller->bubble_presenter_for_test()->IsShowing());
EXPECT_TRUE(controller->IsVisible());
}
TEST_F(AppListControllerImplTest, ToggleAppListOpensBubble) {
auto* controller = Shell::Get()->app_list_controller();
controller->ToggleAppList(GetPrimaryDisplay().id(),
AppListShowSource::kShelfButton,
/*event_time_stamp=*/{});
EXPECT_TRUE(controller->bubble_presenter_for_test()->IsShowing());
EXPECT_TRUE(controller->IsVisible());
}
TEST_F(AppListControllerImplTest, DismissAppListClosesBubble) {
auto* controller = Shell::Get()->app_list_controller();
controller->ShowAppList(AppListShowSource::kSearchKey);
controller->DismissAppList();
EXPECT_FALSE(controller->bubble_presenter_for_test()->IsShowing());
EXPECT_FALSE(controller->IsVisible());
}
TEST_F(AppListControllerImplTest, ShowAppListDoesNotOpenBubbleInTabletMode) {
EnableTabletMode();
auto* controller = Shell::Get()->app_list_controller();
controller->ShowAppList(AppListShowSource::kSearchKey);
EXPECT_FALSE(controller->bubble_presenter_for_test()->IsShowing());
EXPECT_TRUE(controller->IsVisible());
}
TEST_F(AppListControllerImplTest, ToggleAppListDoesNotOpenBubbleInTabletMode) {
EnableTabletMode();
auto* controller = Shell::Get()->app_list_controller();
controller->ToggleAppList(GetPrimaryDisplay().id(),
AppListShowSource::kShelfButton,
/*event_time_stamp=*/{});
EXPECT_FALSE(controller->bubble_presenter_for_test()->IsShowing());
EXPECT_TRUE(controller->IsVisible());
}
TEST_F(AppListControllerImplTest, EnteringTabletModeClosesBubble) {
auto* controller = Shell::Get()->app_list_controller();
controller->ShowAppList(AppListShowSource::kSearchKey);
EnableTabletMode();
EXPECT_FALSE(controller->bubble_presenter_for_test()->IsShowing());
}
TEST_F(AppListControllerImplTest, WallpaperColorChangeDoesNotCrash) {
auto* controller = Shell::Get()->app_list_controller();
controller->ShowAppList(AppListShowSource::kSearchKey);
// Simulate synced wallpaper update while bubble is open.
controller->OnWallpaperColorsChanged();
// No crash.
}
TEST_F(AppListControllerImplTest, HideContinueSectionUpdatesPref) {
auto* controller = Shell::Get()->app_list_controller();
PrefService* prefs =
Shell::Get()->session_controller()->GetLastActiveUserPrefService();
// Continue section defaults to not hidden.
EXPECT_FALSE(prefs->GetBoolean(prefs::kLauncherContinueSectionHidden));
EXPECT_FALSE(controller->ShouldHideContinueSection());
// Hiding continue section is reflected in prefs.
controller->SetHideContinueSection(true);
EXPECT_TRUE(controller->ShouldHideContinueSection());
EXPECT_TRUE(prefs->GetBoolean(prefs::kLauncherContinueSectionHidden));
// Showing continue section is reflected in prefs.
controller->SetHideContinueSection(false);
EXPECT_FALSE(controller->ShouldHideContinueSection());
EXPECT_FALSE(prefs->GetBoolean(prefs::kLauncherContinueSectionHidden));
}
// AppListControllerImpl test that start in inactive session.
class AppListControllerImplNotLoggedInTest : public AppListControllerImplTest {
public:
AppListControllerImplNotLoggedInTest() = default;
~AppListControllerImplNotLoggedInTest() override = default;
void SetUp() override {
AppListControllerImplTest::SetUp();
SetSessionState(session_manager::SessionState::LOGIN_PRIMARY);
}
void SetSessionState(session_manager::SessionState state) {
SessionInfo info;
info.state = state;
Shell::Get()->session_controller()->SetSessionInfo(info);
}
};
TEST_F(AppListControllerImplNotLoggedInTest, ToggleAppListOnLoginScreen) {
auto* controller = Shell::Get()->app_list_controller();
controller->ToggleAppList(GetPrimaryDisplay().id(),
AppListShowSource::kSearchKey,
/*event_time_stamp=*/{});
EXPECT_FALSE(controller->bubble_presenter_for_test()->IsShowing());
EXPECT_FALSE(controller->fullscreen_presenter()->GetTargetVisibility());
EXPECT_FALSE(controller->IsVisible());
// Verify app list cannot be toggled in logged in but inactive state.
SetSessionState(session_manager::SessionState::LOGGED_IN_NOT_ACTIVE);
controller->ToggleAppList(GetPrimaryDisplay().id(),
AppListShowSource::kSearchKey,
/*event_time_stamp=*/{});
EXPECT_FALSE(controller->bubble_presenter_for_test()->IsShowing());
EXPECT_FALSE(controller->fullscreen_presenter()->GetTargetVisibility());
EXPECT_FALSE(controller->IsVisible());
// Toggle app list works when session is active.
SetSessionState(session_manager::SessionState::ACTIVE);
controller->ToggleAppList(GetPrimaryDisplay().id(),
AppListShowSource::kSearchKey,
/*event_time_stamp=*/{});
EXPECT_TRUE(controller->bubble_presenter_for_test()->IsShowing());
EXPECT_FALSE(controller->fullscreen_presenter()->GetTargetVisibility());
EXPECT_TRUE(controller->IsVisible());
}
TEST_F(AppListControllerImplNotLoggedInTest, ShowAppListOnLoginScreen) {
auto* controller = Shell::Get()->app_list_controller();
controller->ShowAppList(AppListShowSource::kSearchKey);
EXPECT_FALSE(controller->bubble_presenter_for_test()->IsShowing());
EXPECT_FALSE(controller->fullscreen_presenter()->GetTargetVisibility());
EXPECT_FALSE(controller->IsVisible());
// Verify app list cannot be toggled in logged in but inactive state.
SetSessionState(session_manager::SessionState::LOGGED_IN_NOT_ACTIVE);
controller->ShowAppList(AppListShowSource::kSearchKey);
EXPECT_FALSE(controller->bubble_presenter_for_test()->IsShowing());
EXPECT_FALSE(controller->fullscreen_presenter()->GetTargetVisibility());
EXPECT_FALSE(controller->IsVisible());
// Toggle app list works when session is active.
SetSessionState(session_manager::SessionState::ACTIVE);
controller->ShowAppList(AppListShowSource::kSearchKey);
EXPECT_TRUE(controller->bubble_presenter_for_test()->IsShowing());
EXPECT_FALSE(controller->fullscreen_presenter()->GetTargetVisibility());
EXPECT_TRUE(controller->IsVisible());
}
TEST_F(AppListControllerImplNotLoggedInTest, ToggleAppListInOobe) {
SetSessionState(session_manager::SessionState::OOBE);
auto* controller = Shell::Get()->app_list_controller();
controller->ToggleAppList(GetPrimaryDisplay().id(),
AppListShowSource::kSearchKey,
/*event_time_stamp=*/{});
EXPECT_FALSE(controller->bubble_presenter_for_test()->IsShowing());
EXPECT_FALSE(controller->fullscreen_presenter()->GetTargetVisibility());
EXPECT_FALSE(controller->IsVisible());
SetSessionState(session_manager::SessionState::LOGGED_IN_NOT_ACTIVE);
controller->ToggleAppList(GetPrimaryDisplay().id(),
AppListShowSource::kSearchKey,
/*event_time_stamp=*/{});
EXPECT_FALSE(controller->bubble_presenter_for_test()->IsShowing());
EXPECT_FALSE(controller->fullscreen_presenter()->GetTargetVisibility());
EXPECT_FALSE(controller->IsVisible());
// Toggle app list works when session is active.
SetSessionState(session_manager::SessionState::ACTIVE);
controller->ToggleAppList(GetPrimaryDisplay().id(),
AppListShowSource::kSearchKey,
/*event_time_stamp=*/{});
EXPECT_TRUE(controller->bubble_presenter_for_test()->IsShowing());
EXPECT_FALSE(controller->fullscreen_presenter()->GetTargetVisibility());
EXPECT_TRUE(controller->IsVisible());
}
TEST_F(AppListControllerImplNotLoggedInTest, ShowAppListInOobe) {
SetSessionState(session_manager::SessionState::OOBE);
auto* controller = Shell::Get()->app_list_controller();
controller->ShowAppList(AppListShowSource::kSearchKey);
EXPECT_FALSE(controller->bubble_presenter_for_test()->IsShowing());
EXPECT_FALSE(controller->fullscreen_presenter()->GetTargetVisibility());
EXPECT_FALSE(controller->IsVisible());
// Verify app list cannot be toggled in logged in but inactive state.
SetSessionState(session_manager::SessionState::LOGGED_IN_NOT_ACTIVE);
controller->ShowAppList(AppListShowSource::kSearchKey);
EXPECT_FALSE(controller->bubble_presenter_for_test()->IsShowing());
EXPECT_FALSE(controller->fullscreen_presenter()->GetTargetVisibility());
EXPECT_FALSE(controller->IsVisible());
// Toggle app list works when session is active.
SetSessionState(session_manager::SessionState::ACTIVE);
controller->ShowAppList(AppListShowSource::kSearchKey);
EXPECT_TRUE(controller->bubble_presenter_for_test()->IsShowing());
EXPECT_FALSE(controller->fullscreen_presenter()->GetTargetVisibility());
EXPECT_TRUE(controller->IsVisible());
}
TEST_F(AppListControllerImplNotLoggedInTest, ToggleAppListOnLockScreen) {
SetSessionState(session_manager::SessionState::ACTIVE);
auto* controller = Shell::Get()->app_list_controller();
EXPECT_FALSE(controller->bubble_presenter_for_test()->IsShowing());
EXPECT_FALSE(controller->fullscreen_presenter()->GetTargetVisibility());
EXPECT_FALSE(controller->IsVisible());
// Lock screen - toggling app list should fail.
SetSessionState(session_manager::SessionState::LOCKED);
controller->ToggleAppList(GetPrimaryDisplay().id(),
AppListShowSource::kSearchKey,
/*event_time_stamp=*/{});
EXPECT_FALSE(controller->bubble_presenter_for_test()->IsShowing());
EXPECT_FALSE(controller->fullscreen_presenter()->GetTargetVisibility());
EXPECT_FALSE(controller->IsVisible());
// Unlock and verify toggling app list works.
SetSessionState(session_manager::SessionState::ACTIVE);
controller->ToggleAppList(GetPrimaryDisplay().id(),
AppListShowSource::kSearchKey,
/*event_time_stamp=*/{});
EXPECT_TRUE(controller->bubble_presenter_for_test()->IsShowing());
EXPECT_FALSE(controller->fullscreen_presenter()->GetTargetVisibility());
EXPECT_TRUE(controller->IsVisible());
// Locking the session hides the app list.
SetSessionState(session_manager::SessionState::LOCKED);
EXPECT_FALSE(controller->bubble_presenter_for_test()->IsShowing());
EXPECT_FALSE(controller->fullscreen_presenter()->GetTargetVisibility());
EXPECT_FALSE(controller->IsVisible());
}
TEST_F(AppListControllerImplNotLoggedInTest, ShowAppListOnLockScreen) {
SetSessionState(session_manager::SessionState::ACTIVE);
auto* controller = Shell::Get()->app_list_controller();
EXPECT_FALSE(controller->bubble_presenter_for_test()->IsShowing());
EXPECT_FALSE(controller->fullscreen_presenter()->GetTargetVisibility());
EXPECT_FALSE(controller->IsVisible());
// Lock screen - toggling app list should fail.
SetSessionState(session_manager::SessionState::LOCKED);
controller->ShowAppList(AppListShowSource::kSearchKey);
EXPECT_FALSE(controller->bubble_presenter_for_test()->IsShowing());
EXPECT_FALSE(controller->fullscreen_presenter()->GetTargetVisibility());
EXPECT_FALSE(controller->IsVisible());
// Unlock and verify toggling app list works.
SetSessionState(session_manager::SessionState::ACTIVE);
controller->ShowAppList(AppListShowSource::kSearchKey);
EXPECT_TRUE(controller->bubble_presenter_for_test()->IsShowing());
EXPECT_FALSE(controller->fullscreen_presenter()->GetTargetVisibility());
EXPECT_TRUE(controller->IsVisible());
// Locking the session hides the app list.
SetSessionState(session_manager::SessionState::LOCKED);
EXPECT_FALSE(controller->bubble_presenter_for_test()->IsShowing());
EXPECT_FALSE(controller->fullscreen_presenter()->GetTargetVisibility());
EXPECT_FALSE(controller->IsVisible());
controller->ShowAppList(AppListShowSource::kSearchKey);
EXPECT_FALSE(controller->bubble_presenter_for_test()->IsShowing());
EXPECT_FALSE(controller->fullscreen_presenter()->GetTargetVisibility());
EXPECT_FALSE(controller->IsVisible());
}
TEST_F(AppListControllerImplNotLoggedInTest, ShowAppListWhenInTabletMode) {
// Enable tablet mode while on login screen.
EnableTabletMode();
auto* controller = Shell::Get()->app_list_controller();
EXPECT_FALSE(controller->bubble_presenter_for_test()->IsShowing());
EXPECT_FALSE(controller->fullscreen_presenter()->GetTargetVisibility());
EXPECT_FALSE(controller->IsVisible());
SetSessionState(session_manager::SessionState::LOGGED_IN_NOT_ACTIVE);
EXPECT_FALSE(controller->bubble_presenter_for_test()->IsShowing());
EXPECT_FALSE(controller->fullscreen_presenter()->GetTargetVisibility());
EXPECT_FALSE(controller->IsVisible());
// Fullscreen app list should be shown upon login.
SetSessionState(session_manager::SessionState::ACTIVE);
EXPECT_FALSE(controller->bubble_presenter_for_test()->IsShowing());
EXPECT_TRUE(controller->fullscreen_presenter()->GetTargetVisibility());
EXPECT_TRUE(controller->IsVisible());
}
TEST_F(AppListControllerImplNotLoggedInTest,
FullscreenLauncherInTabletModeWhenLocked) {
auto* controller = Shell::Get()->app_list_controller();
EXPECT_FALSE(controller->bubble_presenter_for_test()->IsShowing());
EXPECT_FALSE(controller->fullscreen_presenter()->GetTargetVisibility());
EXPECT_FALSE(controller->IsVisible());
SetSessionState(session_manager::SessionState::ACTIVE);
// Enable tablet mode and lock screen - fullscreen launcher should be shown
// (behind the lock screen).
EnableTabletMode();
SetSessionState(session_manager::SessionState::LOCKED);
EXPECT_FALSE(controller->bubble_presenter_for_test()->IsShowing());
EXPECT_TRUE(controller->fullscreen_presenter()->GetTargetVisibility());
EXPECT_TRUE(controller->IsVisible());
SetSessionState(session_manager::SessionState::ACTIVE);
EXPECT_FALSE(controller->bubble_presenter_for_test()->IsShowing());
EXPECT_TRUE(controller->fullscreen_presenter()->GetTargetVisibility());
EXPECT_TRUE(controller->IsVisible());
}
TEST_F(AppListControllerImplNotLoggedInTest,
FullscreenLauncherShownWhenEnteringTabletModeOnLockScreen) {
auto* controller = Shell::Get()->app_list_controller();
EXPECT_FALSE(controller->bubble_presenter_for_test()->IsShowing());
EXPECT_FALSE(controller->fullscreen_presenter()->GetTargetVisibility());
EXPECT_FALSE(controller->IsVisible());
SetSessionState(session_manager::SessionState::ACTIVE);
SetSessionState(session_manager::SessionState::LOCKED);
// Enable tablet mode and lock screen - fullscreen launcher should be shown
// (behind the lock screen).
EnableTabletMode();
EXPECT_FALSE(controller->bubble_presenter_for_test()->IsShowing());
EXPECT_FALSE(controller->fullscreen_presenter()->GetTargetVisibility());
EXPECT_FALSE(controller->IsVisible());
SetSessionState(session_manager::SessionState::ACTIVE);
EXPECT_FALSE(controller->bubble_presenter_for_test()->IsShowing());
EXPECT_TRUE(controller->fullscreen_presenter()->GetTargetVisibility());
EXPECT_TRUE(controller->IsVisible());
}
// Kiosk tests with the bubble launcher enabled.
class AppListControllerImplKioskTest : public AppListControllerImplTest {
public:
AppListControllerImplKioskTest() = default;
~AppListControllerImplKioskTest() override = default;
void SetUp() override {
AppListControllerImplTest::SetUp();
SessionInfo info;
info.is_running_in_app_mode = true;
info.state = session_manager::SessionState::ACTIVE;
Shell::Get()->session_controller()->SetSessionInfo(info);
}
};
TEST_F(AppListControllerImplKioskTest, ShouldNotShowLauncherInTabletMode) {
EnableTabletMode();
auto* controller = Shell::Get()->app_list_controller();
EXPECT_FALSE(controller->ShouldHomeLauncherBeVisible());
}
TEST_F(AppListControllerImplKioskTest,
DoNotShowAnyAppListInClamshellModeWhenShowAppListCalled) {
auto* controller = Shell::Get()->app_list_controller();
controller->ShowAppList(AppListShowSource::kSearchKey);
EXPECT_FALSE(controller->bubble_presenter_for_test()->IsShowing());
EXPECT_FALSE(controller->IsVisible());
}
TEST_F(AppListControllerImplKioskTest,
DoNotShowAnyAppListInTabletModeWhenShowAppListCalled) {
EnableTabletMode();
auto* controller = Shell::Get()->app_list_controller();
controller->ShowAppList(AppListShowSource::kSearchKey);
EXPECT_FALSE(controller->bubble_presenter_for_test()->IsShowing());
EXPECT_FALSE(controller->IsVisible());
}
TEST_F(AppListControllerImplKioskTest,
DoNotShowHomeLauncherInTabletModeWhenOnSessionStateChangedCalled) {
EnableTabletMode();
auto* controller = Shell::Get()->app_list_controller();
controller->OnSessionStateChanged(session_manager::SessionState::ACTIVE);
EXPECT_FALSE(controller->ShouldHomeLauncherBeVisible());
}
TEST_F(AppListControllerImplKioskTest,
DoNotMinimizeAppWindowInTabletModeWhenGoHomeCalled) {
// Emulation of a Kiosk app window.
std::unique_ptr<aura::Window> w(CreateTestWindow(gfx::Rect(0, 0, 400, 400)));
EnableTabletMode();
Shell::Get()->app_list_controller()->GoHome(GetPrimaryDisplay().id());
EXPECT_FALSE(WindowState::Get(w.get())->IsMinimized());
EXPECT_TRUE(w->IsVisible());
}
TEST_F(AppListControllerImplKioskTest,
DoNotShowAppListInTabletModeWhenPressHomeButton) {
// Emulation of a Kiosk app window.
std::unique_ptr<aura::Window> w(CreateTestWindow(gfx::Rect(0, 0, 400, 400)));
EnableTabletMode();
PressHomeButton();
EXPECT_FALSE(WindowState::Get(w.get())->IsMinimized());
EXPECT_TRUE(w->IsVisible());
EXPECT_FALSE(Shell::Get()->app_list_controller()->IsVisible());
}
TEST_F(AppListControllerImplKioskTest,
DoNotOpenAnyAppListAfterSwitchingFromTabletMode) {
auto* controller = Shell::Get()->app_list_controller();
EnableTabletMode();
controller->OnTabletModeStarted();
EXPECT_FALSE(controller->IsVisible());
Shell::Get()->tablet_mode_controller()->SetEnabledForTest(false);
controller->OnTabletModeEnded();
EXPECT_FALSE(controller->bubble_presenter_for_test()->IsShowing());
EXPECT_FALSE(controller->IsVisible());
}
// App list assistant tests.
class AppListControllerWithAssistantTest : public AppListControllerImplTest {
public:
AppListControllerWithAssistantTest()
: assistant_test_api_(AssistantTestApi::Create()) {}
AppListControllerWithAssistantTest(
const AppListControllerWithAssistantTest&) = delete;
AppListControllerWithAssistantTest& operator=(
const AppListControllerWithAssistantTest&) = delete;
~AppListControllerWithAssistantTest() override = default;
// AppListControllerImplTest:
void SetUp() override {
AppListControllerImplTest::SetUp();
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();
}
protected:
void ToggleAssistantUiWithAccelerator() {
PressAndReleaseKey(ui::KeyboardCode::VKEY_A, ui::EF_COMMAND_DOWN);
EXPECT_TRUE(assistant_test_api_->IsVisible());
}
AssistantVisibility GetAssistantVisibility() const {
return AssistantUiController::Get()->GetModel()->visibility();
}
std::unique_ptr<AssistantTestApi> assistant_test_api_;
base::test::ScopedFeatureList feature_list_;
};
// Verifies the scenario that the Assistant shortcut is triggered when the app
// list close animation is running.
TEST_F(AppListControllerWithAssistantTest,
TriggerAssistantKeyWhenAppListClosing) {
// Show the Assistant and verify the app list state.
ToggleAssistantUiWithAccelerator();
auto* app_list_controller = Shell::Get()->app_list_controller();
EXPECT_TRUE(app_list_controller->IsVisible());
EXPECT_TRUE(AssistantUiController::Get()->HasShownOnboarding());
EXPECT_EQ(AssistantVisibility::kVisible, GetAssistantVisibility());
assistant_test_api_->input_text_field()->SetText(u"xyz");
EXPECT_EQ(u"xyz", assistant_test_api_->input_text_field()->GetText());
{
// Enable animation with non-zero duration.
ui::ScopedAnimationDurationScaleMode non_zero_duration(
ui::ScopedAnimationDurationScaleMode::NON_ZERO_DURATION);
// Press the search key. The launcher starts to close.
PressAndReleaseKey(ui::KeyboardCode::VKEY_COMMAND);
EXPECT_EQ(AssistantVisibility::kClosing, GetAssistantVisibility());
// Toggle the Assistant ui and wait for app list animation to finish.
AppListBubbleView* bubble_view =
app_list_controller->bubble_presenter_for_test()
->bubble_view_for_test();
ToggleAssistantUiWithAccelerator();
ui::LayerAnimationStoppedWaiter().Wait(bubble_view->layer());
}
// Verify that the Assistant ui is visible. In addition, the text in the
// textfield does not change.
EXPECT_TRUE(assistant_test_api_->IsVisible());
EXPECT_EQ(u"xyz", assistant_test_api_->input_text_field()->GetText());
EXPECT_TRUE(app_list_controller->IsVisible());
EXPECT_EQ(AssistantVisibility::kVisible, GetAssistantVisibility());
// Press the search key to close the app list.
PressAndReleaseKey(ui::KeyboardCode::VKEY_COMMAND);
EXPECT_FALSE(app_list_controller->IsVisible());
// Toggle the Assistant ui. The text is still the same in the input field.
ToggleAssistantUiWithAccelerator();
EXPECT_TRUE(app_list_controller->IsVisible());
EXPECT_TRUE(assistant_test_api_->IsVisible());
EXPECT_EQ(u"xyz", assistant_test_api_->input_text_field()->GetText());
}
// Verifies the scenario that the search key is triggered when the app list
// close animation is running.
TEST_F(AppListControllerWithAssistantTest, TriggerSearchKeyWhenAppListClosing) {
ToggleAssistantUiWithAccelerator();
auto* app_list_controller = Shell::Get()->app_list_controller();
EXPECT_TRUE(app_list_controller->IsVisible());
// Enable animation with non-zero duration.
ui::ScopedAnimationDurationScaleMode non_zero_duration(
ui::ScopedAnimationDurationScaleMode::NON_ZERO_DURATION);
// Press the search key to close the app list.
PressAndReleaseKey(ui::KeyboardCode::VKEY_COMMAND);
EXPECT_EQ(AssistantVisibility::kClosing, GetAssistantVisibility());
// Press the search key to reshow the app list.
AppListBubbleView* bubble_view =
app_list_controller->bubble_presenter_for_test()->bubble_view_for_test();
PressAndReleaseKey(ui::KeyboardCode::VKEY_COMMAND);
ui::LayerAnimationStoppedWaiter().Wait(bubble_view->layer());
// The Assistant should be closed.
EXPECT_EQ(AssistantVisibility::kClosed, GetAssistantVisibility());
}
TEST_F(AppListControllerWithAssistantTest,
AppListWindowIsNotShowingOnTopOfOtherApps) {
CreateAppWindow();
Shell::Get()->tablet_mode_controller()->SetEnabledForTest(true);
auto* home_screen_container = Shell::GetPrimaryRootWindow()->GetChildById(
kShellWindowId_HomeScreenContainer);
auto* app_list_window = Shell::Get()
->app_list_controller()
->fullscreen_presenter()
->GetView()
->GetWidget()
->GetNativeWindow();
// Default placement is in home screen container behind other app windows.
EXPECT_TRUE(home_screen_container->Contains(app_list_window));
// The app list window shows on top of other app windows when assistant UI is
// active.
ToggleAssistantUiWithAccelerator();
EXPECT_FALSE(home_screen_container->Contains(app_list_window));
// And stays there during tablet -> clamshell mode transition when assistant
// UI is active.
Shell::Get()->tablet_mode_controller()->SetEnabledForTest(false);
EXPECT_FALSE(home_screen_container->Contains(app_list_window));
// Enter tablet mode again. App list window should return to its default
// position and shouldn't move during transition to clamshell mode.
Shell::Get()->tablet_mode_controller()->SetEnabledForTest(true);
EXPECT_TRUE(home_screen_container->Contains(app_list_window));
Shell::Get()->tablet_mode_controller()->SetEnabledForTest(false);
EXPECT_TRUE(home_screen_container->Contains(app_list_window));
}
} // namespace ash