Add taskbar TAPL tests
- Added TAPL objects to support TAPL tests for taskbar features
- Added tests for major taskbar features: hiding/showing the taskbar, opening all apps, lauching apps and app deep shortcuts from the taskbar/all apps page, launching apps and app deep shortcuts in splitscreen
- These tests should only run for large devices
Design: go/taskbar-tapl-tests
Test: ran tests locally with invalid configurations and remotely
Bug: 215218813
Change-Id: I3c28c339707467414aa47fa633f87fa0c1fdee57
diff --git a/Android.bp b/Android.bp
index c980a2e..78db2f1 100644
--- a/Android.bp
+++ b/Android.bp
@@ -210,6 +210,8 @@
srcs: [
"ext_tests/src/**/*.java",
"ext_tests/src/**/*.kt",
+ "quickstep/ext_tests/src/**/*.java",
+ "quickstep/ext_tests/src/**/*.kt",
],
}
diff --git a/ext_tests/src/com/android/launcher3/testing/DebugTestInformationHandler.java b/ext_tests/src/com/android/launcher3/testing/DebugTestInformationHandler.java
index 02206c0..d16e12c 100644
--- a/ext_tests/src/com/android/launcher3/testing/DebugTestInformationHandler.java
+++ b/ext_tests/src/com/android/launcher3/testing/DebugTestInformationHandler.java
@@ -29,8 +29,10 @@
import androidx.annotation.Keep;
import androidx.annotation.Nullable;
+import com.android.launcher3.BubbleTextView;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherSettings;
+import com.android.launcher3.ShortcutAndWidgetContainer;
import java.util.ArrayList;
import java.util.Collection;
@@ -205,6 +207,32 @@
}
}
+ case TestProtocol.REQUEST_USE_TEST_WORKSPACE_LAYOUT: {
+ useTestWorkspaceLayout(true);
+ return response;
+ }
+
+ case TestProtocol.REQUEST_USE_DEFAULT_WORKSPACE_LAYOUT: {
+ useTestWorkspaceLayout(false);
+ return response;
+ }
+
+ case TestProtocol.REQUEST_HOTSEAT_ICON_NAMES: {
+ return getLauncherUIProperty(Bundle::putStringArrayList, l -> {
+ ShortcutAndWidgetContainer hotseatIconsContainer =
+ l.getHotseat().getShortcutsAndWidgets();
+ ArrayList<String> hotseatIconNames = new ArrayList<>();
+
+ for (int i = 0; i < hotseatIconsContainer.getChildCount(); i++) {
+ // Use unchecked cast to catch changes in hotseat layout
+ BubbleTextView icon = (BubbleTextView) hotseatIconsContainer.getChildAt(i);
+ hotseatIconNames.add((String) icon.getText());
+ }
+
+ return hotseatIconNames;
+ });
+ }
+
case TestProtocol.REQUEST_GET_ACTIVITIES_CREATED_COUNT: {
response.putInt(TestProtocol.TEST_INFO_RESPONSE_FIELD, sActivitiesCreatedCount);
return response;
@@ -223,4 +251,15 @@
return super.call(method, arg, extras);
}
}
+
+ private void useTestWorkspaceLayout(boolean useTestWorkspaceLayout) {
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ LauncherSettings.Settings.call(mContext.getContentResolver(), useTestWorkspaceLayout
+ ? LauncherSettings.Settings.METHOD_SET_USE_TEST_WORKSPACE_LAYOUT_FLAG
+ : LauncherSettings.Settings.METHOD_CLEAR_USE_TEST_WORKSPACE_LAYOUT_FLAG);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
}
diff --git a/quickstep/ext_tests/src/com/android/quickstep/DebugQuickstepTestInformationHandler.java b/quickstep/ext_tests/src/com/android/quickstep/DebugQuickstepTestInformationHandler.java
new file mode 100644
index 0000000..e5f0295
--- /dev/null
+++ b/quickstep/ext_tests/src/com/android/quickstep/DebugQuickstepTestInformationHandler.java
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.quickstep;
+
+import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.os.Bundle;
+
+import androidx.annotation.Nullable;
+
+import com.android.launcher3.BaseQuickstepLauncher;
+import com.android.launcher3.Launcher;
+import com.android.launcher3.R;
+import com.android.launcher3.taskbar.LauncherTaskbarUIController;
+import com.android.launcher3.testing.DebugTestInformationHandler;
+import com.android.launcher3.testing.TestProtocol;
+
+import java.util.concurrent.ExecutionException;
+
+/**
+ * Class to handle requests from tests, including debug ones, to Quickstep Launcher builds.
+ */
+public abstract class DebugQuickstepTestInformationHandler extends QuickstepTestInformationHandler {
+
+ private final DebugTestInformationHandler mDebugTestInformationHandler;
+
+ public DebugQuickstepTestInformationHandler(Context context) {
+ super(context);
+ mDebugTestInformationHandler = new DebugTestInformationHandler(context);
+ }
+
+ @Override
+ public Bundle call(String method, String arg, @Nullable Bundle extras) {
+ Bundle response = new Bundle();
+ switch (method) {
+ case TestProtocol.REQUEST_ENABLE_MANUAL_TASKBAR_STASHING:
+ runOnUIThread(l -> {
+ enableManualTaskbarStashing(l, true);
+ });
+ return response;
+
+ case TestProtocol.REQUEST_DISABLE_MANUAL_TASKBAR_STASHING:
+ runOnUIThread(l -> {
+ enableManualTaskbarStashing(l, false);
+ });
+ return response;
+
+ case TestProtocol.REQUEST_UNSTASH_TASKBAR_IF_STASHED:
+ runOnUIThread(l -> {
+ enableManualTaskbarStashing(l, true);
+
+ BaseQuickstepLauncher quickstepLauncher = (BaseQuickstepLauncher) l;
+ LauncherTaskbarUIController taskbarUIController =
+ quickstepLauncher.getTaskbarUIController();
+
+ // Allow null-pointer to catch illegal states.
+ taskbarUIController.unstashTaskbarIfStashed();
+
+ enableManualTaskbarStashing(l, false);
+ });
+ return response;
+
+ case TestProtocol.REQUEST_STASHED_TASKBAR_HEIGHT: {
+ final Resources resources = mContext.getResources();
+ response.putInt(TestProtocol.TEST_INFO_RESPONSE_FIELD,
+ resources.getDimensionPixelSize(R.dimen.taskbar_stashed_size));
+ return response;
+ }
+
+ default:
+ response = super.call(method, arg, extras);
+ if (response != null) return response;
+ return mDebugTestInformationHandler.call(method, arg, extras);
+ }
+ }
+
+ private void enableManualTaskbarStashing(Launcher launcher, boolean enable) {
+ BaseQuickstepLauncher quickstepLauncher = (BaseQuickstepLauncher) launcher;
+ LauncherTaskbarUIController taskbarUIController =
+ quickstepLauncher.getTaskbarUIController();
+
+ // Allow null-pointer to catch illegal states.
+ taskbarUIController.enableManualStashingForTests(enable);
+ }
+
+ /**
+ * Runs the given command on the UI thread.
+ */
+ private static void runOnUIThread(UIThreadCommand command) {
+ try {
+ MAIN_EXECUTOR.submit(() -> {
+ command.execute(Launcher.ACTIVITY_TRACKER.getCreatedActivity());
+ return null;
+ }).get();
+ } catch (ExecutionException | InterruptedException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ private interface UIThreadCommand {
+
+ void execute(Launcher launcher);
+ }
+}
+
diff --git a/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java b/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
index 768a348..793d987 100644
--- a/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
@@ -27,6 +27,7 @@
import android.view.WindowManagerGlobal;
import androidx.annotation.NonNull;
+import androidx.annotation.VisibleForTesting;
import com.android.launcher3.BaseQuickstepLauncher;
import com.android.launcher3.DeviceProfile;
@@ -118,6 +119,24 @@
}
/**
+ * Enables manual taskbar stashing. This method should only be used for tests that need to
+ * stash/unstash the taskbar.
+ */
+ @VisibleForTesting
+ public void enableManualStashingForTests(boolean enableManualStashing) {
+ mControllers.taskbarStashController.enableManualStashingForTests(enableManualStashing);
+ }
+
+ /**
+ * Unstashes the Taskbar if it is stashed. This method should only be used to unstash the
+ * taskbar at the end of a test.
+ */
+ @VisibleForTesting
+ public void unstashTaskbarIfStashed() {
+ mControllers.taskbarStashController.onLongPressToUnstashTaskbar();
+ }
+
+ /**
* Should be called from onResume() and onPause(), and animates the Taskbar accordingly.
*/
public void onLauncherResumedOrPaused(boolean isResumed) {
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
index 5387b1a..6c74ae3 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
@@ -71,6 +71,8 @@
import com.android.launcher3.model.data.WorkspaceItemInfo;
import com.android.launcher3.popup.PopupDataProvider;
import com.android.launcher3.taskbar.allapps.TaskbarAllAppsController;
+import com.android.launcher3.testing.TestLogging;
+import com.android.launcher3.testing.TestProtocol;
import com.android.launcher3.touch.ItemClickHandler;
import com.android.launcher3.util.DisplayController;
import com.android.launcher3.util.DisplayController.NavigationMode;
@@ -646,12 +648,16 @@
Toast.makeText(this, R.string.safemode_shortcut_error,
Toast.LENGTH_SHORT).show();
} else if (info.isPromise()) {
+ TestLogging.recordEvent(
+ TestProtocol.SEQUENCE_MAIN, "start: taskbarPromiseIcon");
intent = new PackageManagerHelper(this)
.getMarketIntent(info.getTargetPackage())
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
} else if (info.itemType == Favorites.ITEM_TYPE_DEEP_SHORTCUT) {
+ TestLogging.recordEvent(
+ TestProtocol.SEQUENCE_MAIN, "start: taskbarDeepShortcut");
String id = info.getDeepShortcutId();
String packageName = intent.getPackage();
getSystemService(LauncherApps.class)
@@ -680,6 +686,7 @@
Intent intent = new Intent(info.getIntent())
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
try {
+ TestLogging.recordEvent(TestProtocol.SEQUENCE_MAIN, "start: taskbarAppIcon");
if (info.user.equals(Process.myUserHandle())) {
// TODO(b/216683257): Use startActivityForResult for search results that require it.
startActivity(intent);
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragController.java
index af98b7f..e2ab443 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragController.java
@@ -63,6 +63,8 @@
import com.android.launcher3.popup.PopupContainerWithArrow;
import com.android.launcher3.shortcuts.DeepShortcutView;
import com.android.launcher3.shortcuts.ShortcutDragPreviewProvider;
+import com.android.launcher3.testing.TestLogging;
+import com.android.launcher3.testing.TestProtocol;
import com.android.launcher3.util.IntSet;
import com.android.launcher3.util.ItemInfoMatcher;
import com.android.systemui.shared.recents.model.Task;
@@ -128,7 +130,7 @@
if (!(view instanceof BubbleTextView)) {
return false;
}
-
+ TestLogging.recordEvent(TestProtocol.SEQUENCE_MAIN, "onTaskbarItemLongClick");
BubbleTextView btv = (BubbleTextView) view;
mActivity.onDragStart();
btv.post(() -> {
@@ -164,24 +166,7 @@
dragLayerY += dragRect.top;
DragOptions dragOptions = new DragOptions();
- dragOptions.preDragCondition = new DragOptions.PreDragCondition() {
- private DragView mDragView;
-
- @Override
- public boolean shouldStartDrag(double distanceDragged) {
- return mDragView != null && mDragView.isAnimationFinished();
- }
-
- @Override
- public void onPreDragStart(DropTarget.DragObject dragObject) {
- mDragView = dragObject.dragView;
- }
-
- @Override
- public void onPreDragEnd(DropTarget.DragObject dragObject, boolean dragStarted) {
- mDragView = null;
- }
- };
+ dragOptions.preDragCondition = null;
if (FeatureFlags.ENABLE_TASKBAR_POPUP_MENU.get()) {
PopupContainerWithArrow<BaseTaskbarContext> popupContainer =
mControllers.taskbarPopupController.showForIcon(btv);
@@ -189,6 +174,32 @@
dragOptions.preDragCondition = popupContainer.createPreDragCondition(false);
}
}
+ if (dragOptions.preDragCondition == null) {
+ dragOptions.preDragCondition = new DragOptions.PreDragCondition() {
+ private DragView mDragView;
+
+ @Override
+ public boolean shouldStartDrag(double distanceDragged) {
+ return mDragView != null && mDragView.isAnimationFinished();
+ }
+
+ @Override
+ public void onPreDragStart(DropTarget.DragObject dragObject) {
+ mDragView = dragObject.dragView;
+
+ if (FeatureFlags.ENABLE_TASKBAR_POPUP_MENU.get()
+ && !shouldStartDrag(0)) {
+ // Immediately close the popup menu.
+ mDragView.setOnAnimationEndCallback(() -> callOnDragStart());
+ }
+ }
+
+ @Override
+ public void onPreDragEnd(DropTarget.DragObject dragObject, boolean dragStarted) {
+ mDragView = null;
+ }
+ };
+ }
return startDrag(
drawable,
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarPopupController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarPopupController.java
index ea4fe34..f9c8062 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarPopupController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarPopupController.java
@@ -145,6 +145,7 @@
});
// TODO (b/198438631): configure for taskbar/context
container.setPopupItemDragHandler(new TaskbarPopupItemDragHandler());
+ mControllers.taskbarDragController.addDragListener(container);
container.populateAndShow(icon,
mPopupDataProvider.getShortcutCountForItem(item),
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
index 473be9e..f9a282b 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
@@ -136,6 +136,8 @@
private boolean mIsSystemGestureInProgress;
private boolean mIsImeShowing;
+ private boolean mEnableManualStashingForTests = false;
+
// Evaluate whether the handle should be stashed
private final StatePropertyHolder mStatePropertyHolder = new StatePropertyHolder(
flags -> {
@@ -199,12 +201,15 @@
*/
protected boolean supportsManualStashing() {
return supportsVisualStashing()
- && (!Utilities.IS_RUNNING_IN_TEST_HARNESS || supportsStashingForTests());
+ && (!Utilities.IS_RUNNING_IN_TEST_HARNESS || mEnableManualStashingForTests);
}
- private boolean supportsStashingForTests() {
- // TODO: enable this for tests that specifically check stash/unstash behavior.
- return false;
+ /**
+ * Enables support for manual stashing. This should only be used to add this functionality
+ * to Launcher specific tests.
+ */
+ public void enableManualStashingForTests(boolean enableManualStashing) {
+ mEnableManualStashingForTests = enableManualStashing;
}
/**
diff --git a/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsContext.java b/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsContext.java
index a67ca70..579b5d7 100644
--- a/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsContext.java
+++ b/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsContext.java
@@ -24,6 +24,7 @@
import android.content.Context;
import android.graphics.Insets;
import android.view.KeyEvent;
+import android.view.MotionEvent;
import android.view.View;
import android.view.WindowInsets;
@@ -38,6 +39,8 @@
import com.android.launcher3.taskbar.TaskbarActivityContext;
import com.android.launcher3.taskbar.TaskbarDragController;
import com.android.launcher3.taskbar.TaskbarStashController;
+import com.android.launcher3.testing.TestLogging;
+import com.android.launcher3.testing.TestProtocol;
import com.android.launcher3.util.OnboardingPrefs;
import com.android.launcher3.util.TouchController;
import com.android.launcher3.views.BaseDragLayer;
@@ -181,6 +184,12 @@
}
@Override
+ public boolean dispatchTouchEvent(MotionEvent ev) {
+ TestLogging.recordMotionEvent(TestProtocol.SEQUENCE_MAIN, "Touch event", ev);
+ return super.dispatchTouchEvent(ev);
+ }
+
+ @Override
public boolean dispatchKeyEvent(KeyEvent event) {
if (event.getAction() == ACTION_UP && event.getKeyCode() == KEYCODE_BACK) {
AbstractFloatingView topView = AbstractFloatingView.getTopOpenView(mActivity);
diff --git a/quickstep/tests/src/com/android/quickstep/TaplTestsTaskbar.java b/quickstep/tests/src/com/android/quickstep/TaplTestsTaskbar.java
new file mode 100644
index 0000000..ba93975
--- /dev/null
+++ b/quickstep/tests/src/com/android/quickstep/TaplTestsTaskbar.java
@@ -0,0 +1,155 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.quickstep;
+
+import static androidx.test.InstrumentationRegistry.getInstrumentation;
+
+import static junit.framework.TestCase.assertEquals;
+
+import android.content.Intent;
+
+import androidx.test.filters.LargeTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.launcher3.tapl.Taskbar;
+import com.android.launcher3.ui.TaplTestsLauncher3;
+
+import org.junit.After;
+import org.junit.Assume;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.List;
+
+@LargeTest
+@RunWith(AndroidJUnit4.class)
+public class TaplTestsTaskbar extends AbstractQuickStepTest {
+
+ private static final String TEST_APP_NAME = "LauncherTestApp";
+ private static final String TEST_APP_PACKAGE =
+ getInstrumentation().getContext().getPackageName();
+ private static final String CALCULATOR_APP_PACKAGE =
+ resolveSystemApp(Intent.CATEGORY_APP_CALCULATOR);
+
+ @Override
+ public void setUp() throws Exception {
+ Assume.assumeTrue(mLauncher.isTablet());
+ super.setUp();
+ mLauncher.useTestWorkspaceLayoutOnReload();
+ TaplTestsLauncher3.initialize(this);
+
+ startAppFast(CALCULATOR_APP_PACKAGE);
+ mLauncher.showTaskbarIfHidden();
+ }
+
+ @After
+ public void tearDown() {
+ mLauncher.useDefaultWorkspaceLayoutOnReload();
+ }
+
+ @Test
+ public void testHideShowTaskbar() {
+ getTaskbar().hide();
+ mLauncher.getLaunchedAppState().showTaskbar();
+ }
+
+ @Test
+ public void testLaunchApp() throws Exception {
+ getTaskbar().getAppIcon(TEST_APP_NAME).launch(TEST_APP_PACKAGE);
+ }
+
+ @Test
+ public void testOpenMenu() throws Exception {
+ getTaskbar().getAppIcon(TEST_APP_NAME).openMenu();
+ }
+
+ @Test
+ public void testLaunchShortcut() throws Exception {
+ getTaskbar().getAppIcon(TEST_APP_NAME)
+ .openDeepShortcutMenu()
+ .getMenuItem("Shortcut 1")
+ .launch(TEST_APP_PACKAGE);
+ }
+
+ @Test
+ @PortraitLandscape
+ public void testLaunchAppInSplitscreen() throws Exception {
+ getTaskbar().getAppIcon(TEST_APP_NAME).dragToSplitscreen(
+ TEST_APP_PACKAGE, CALCULATOR_APP_PACKAGE);
+ }
+
+ @Test
+ @PortraitLandscape
+ public void testLaunchShortcutInSplitscreen() throws Exception {
+ getTaskbar().getAppIcon(TEST_APP_NAME)
+ .openDeepShortcutMenu()
+ .getMenuItem("Shortcut 1")
+ .dragToSplitscreen(TEST_APP_PACKAGE, CALCULATOR_APP_PACKAGE);
+ }
+
+ @Test
+ public void testLaunchApp_FromTaskbarAllApps() throws Exception {
+ getTaskbar().openAllApps().getAppIcon(TEST_APP_NAME).launch(TEST_APP_PACKAGE);
+ }
+
+ @Test
+ public void testOpenMenu_FromTaskbarAllApps() throws Exception {
+ getTaskbar().openAllApps().getAppIcon(TEST_APP_NAME).openMenu();
+ }
+
+ @Test
+ public void testLaunchShortcut_FromTaskbarAllApps() throws Exception {
+ getTaskbar().openAllApps()
+ .getAppIcon(TEST_APP_NAME)
+ .openDeepShortcutMenu()
+ .getMenuItem("Shortcut 1")
+ .launch(TEST_APP_PACKAGE);
+ }
+
+ @Test
+ @PortraitLandscape
+ public void testLaunchAppInSplitscreen_FromTaskbarAllApps() throws Exception {
+ getTaskbar().openAllApps()
+ .getAppIcon(TEST_APP_NAME)
+ .dragToSplitscreen(TEST_APP_PACKAGE, CALCULATOR_APP_PACKAGE);
+ }
+
+ @Test
+ @PortraitLandscape
+ public void testLaunchShortcutInSplitscreen_FromTaskbarAllApps() throws Exception {
+ getTaskbar().openAllApps()
+ .getAppIcon(TEST_APP_NAME)
+ .openDeepShortcutMenu()
+ .getMenuItem("Shortcut 1")
+ .dragToSplitscreen(TEST_APP_PACKAGE, CALCULATOR_APP_PACKAGE);
+ }
+
+ private Taskbar getTaskbar() {
+ Taskbar taskbar = mLauncher.getLaunchedAppState().getTaskbar();
+ List<String> taskbarIconNames = taskbar.getIconNames();
+ List<String> hotseatIconNames = mLauncher.getHotseatIconNames();
+
+ assertEquals("Taskbar and hotseat icon counts do not match",
+ taskbarIconNames.size(), hotseatIconNames.size());
+
+ for (int i = 0; i < taskbarIconNames.size(); i++) {
+ assertEquals("Taskbar and Hotseat icons do not match",
+ taskbarIconNames, hotseatIconNames);
+ }
+
+ return taskbar;
+ }
+}
diff --git a/res/xml/default_test_workspace.xml b/res/xml/default_test_workspace.xml
new file mode 100644
index 0000000..bd718b3
--- /dev/null
+++ b/res/xml/default_test_workspace.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2022 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<!-- Split display specific version of Launcher3/res/xml/default_workspace_4x4.xml -->
+<favorites xmlns:launcher="http://schemas.android.com/apk/res-auto/com.android.launcher3" >
+
+ <!-- Launcher Test Activity -->
+ <resolve
+ launcher:container="-101"
+ launcher:screen="0"
+ launcher:x="0"
+ launcher:y="0" >
+ <favorite launcher:uri="#Intent;action=android.intent.action.MAIN;category=android.intent.category.CATEGORY_TEST;component=com.google.android.apps.nexuslauncher.tests/com.android.launcher3.testcomponent.BaseTestingActivity;end" />
+ </resolve>
+
+</favorites>
diff --git a/src/com/android/launcher3/LauncherProvider.java b/src/com/android/launcher3/LauncherProvider.java
index e004a4b..85ee8bc 100644
--- a/src/com/android/launcher3/LauncherProvider.java
+++ b/src/com/android/launcher3/LauncherProvider.java
@@ -102,6 +102,8 @@
public static final String AUTHORITY = BuildConfig.APPLICATION_ID + ".settings";
public static final String KEY_LAYOUT_PROVIDER_AUTHORITY = "KEY_LAYOUT_PROVIDER_AUTHORITY";
+ private static final int TEST_WORKSPACE_LAYOUT_RES_XML = R.xml.default_test_workspace;
+
static final String EMPTY_DATABASE_CREATED = "EMPTY_DATABASE_CREATED";
protected DatabaseHelper mOpenHelper;
@@ -109,6 +111,8 @@
private long mLastRestoreTimestamp = 0L;
+ private boolean mUseTestWorkspaceLayout;
+
/**
* $ adb shell dumpsys activity provider com.android.launcher3
*/
@@ -390,6 +394,14 @@
mOpenHelper.createEmptyDB(mOpenHelper.getWritableDatabase());
return null;
}
+ case LauncherSettings.Settings.METHOD_SET_USE_TEST_WORKSPACE_LAYOUT_FLAG: {
+ mUseTestWorkspaceLayout = true;
+ return null;
+ }
+ case LauncherSettings.Settings.METHOD_CLEAR_USE_TEST_WORKSPACE_LAYOUT_FLAG: {
+ mUseTestWorkspaceLayout = false;
+ return null;
+ }
case LauncherSettings.Settings.METHOD_LOAD_DEFAULT_FAVORITES: {
loadDefaultFavoritesIfNecessary();
return null;
@@ -609,7 +621,8 @@
private DefaultLayoutParser getDefaultLayoutParser(AppWidgetHost widgetHost) {
InvariantDeviceProfile idp = LauncherAppState.getIDP(getContext());
- int defaultLayout = idp.defaultLayoutId;
+ int defaultLayout = mUseTestWorkspaceLayout
+ ? TEST_WORKSPACE_LAYOUT_RES_XML : idp.defaultLayoutId;
if (getContext().getSystemService(UserManager.class).isDemoUser()
&& idp.demoModeLayoutId != 0) {
diff --git a/src/com/android/launcher3/LauncherSettings.java b/src/com/android/launcher3/LauncherSettings.java
index 048aaaa..66195f3 100644
--- a/src/com/android/launcher3/LauncherSettings.java
+++ b/src/com/android/launcher3/LauncherSettings.java
@@ -374,6 +374,12 @@
public static final String METHOD_CREATE_EMPTY_DB = "create_empty_db";
+ public static final String METHOD_SET_USE_TEST_WORKSPACE_LAYOUT_FLAG =
+ "set_use_test_workspace_layout_flag";
+
+ public static final String METHOD_CLEAR_USE_TEST_WORKSPACE_LAYOUT_FLAG =
+ "clear_use_test_workspace_layout_flag";
+
public static final String METHOD_LOAD_DEFAULT_FAVORITES = "load_default_favorites";
public static final String METHOD_REMOVE_GHOST_WIDGETS = "remove_ghost_widgets";
diff --git a/src/com/android/launcher3/dragndrop/DragView.java b/src/com/android/launcher3/dragndrop/DragView.java
index b914ae2..a3945fd 100644
--- a/src/com/android/launcher3/dragndrop/DragView.java
+++ b/src/com/android/launcher3/dragndrop/DragView.java
@@ -98,6 +98,7 @@
final ValueAnimator mAnim;
// Whether mAnim has started. Unlike mAnim.isStarted(), this is true even after mAnim ends.
private boolean mAnimStarted;
+ private Runnable mOnAnimEndCallback = null;
private int mLastTouchX;
private int mLastTouchY;
@@ -180,6 +181,14 @@
public void onAnimationStart(Animator animation) {
mAnimStarted = true;
}
+
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ super.onAnimationEnd(animation);
+ if (mOnAnimEndCallback != null) {
+ mOnAnimEndCallback.run();
+ }
+ }
});
setDragRegion(new Rect(0, 0, width, height));
@@ -199,6 +208,10 @@
setWillNotDraw(false);
}
+ public void setOnAnimationEndCallback(Runnable callback) {
+ mOnAnimEndCallback = callback;
+ }
+
/**
* Initialize {@code #mIconDrawable} if the item can be represented using
* an {@link AdaptiveIconDrawable} or {@link FolderAdaptiveIcon}.
diff --git a/src/com/android/launcher3/testing/TestProtocol.java b/src/com/android/launcher3/testing/TestProtocol.java
index 05fe182..b04110d 100644
--- a/src/com/android/launcher3/testing/TestProtocol.java
+++ b/src/com/android/launcher3/testing/TestProtocol.java
@@ -82,6 +82,10 @@
public static final String REQUEST_IS_LAUNCHER_INITIALIZED = "is-launcher-initialized";
public static final String REQUEST_FREEZE_APP_LIST = "freeze-app-list";
public static final String REQUEST_UNFREEZE_APP_LIST = "unfreeze-app-list";
+ public static final String REQUEST_ENABLE_MANUAL_TASKBAR_STASHING = "enable-taskbar-stashing";
+ public static final String REQUEST_DISABLE_MANUAL_TASKBAR_STASHING = "disable-taskbar-stashing";
+ public static final String REQUEST_UNSTASH_TASKBAR_IF_STASHED = "unstash-taskbar-if-stashed";
+ public static final String REQUEST_STASHED_TASKBAR_HEIGHT = "stashed-taskbar-height";
public static final String REQUEST_APP_LIST_FREEZE_FLAGS = "app-list-freeze-flags";
public static final String REQUEST_APPS_LIST_SCROLL_Y = "apps-list-scroll-y";
public static final String REQUEST_WIDGETS_SCROLL_Y = "widgets-scroll-y";
@@ -96,6 +100,10 @@
public static final String REQUEST_GET_HAD_NONTEST_EVENTS = "get-had-nontest-events";
public static final String REQUEST_STOP_EVENT_LOGGING = "stop-event-logging";
public static final String REQUEST_CLEAR_DATA = "clear-data";
+ public static final String REQUEST_USE_TEST_WORKSPACE_LAYOUT = "use-test-workspace-layout";
+ public static final String REQUEST_USE_DEFAULT_WORKSPACE_LAYOUT =
+ "use-default-workspace-layout";
+ public static final String REQUEST_HOTSEAT_ICON_NAMES = "get-hotseat-icon-names";
public static final String REQUEST_IS_TABLET = "is-tablet";
public static final String REQUEST_IS_TWO_PANELS = "is-two-panel";
public static final String REQUEST_START_DRAG_THRESHOLD = "start-drag-threshold";
diff --git a/tests/AndroidManifest-common.xml b/tests/AndroidManifest-common.xml
index b4cd773..9cc3aed 100644
--- a/tests/AndroidManifest-common.xml
+++ b/tests/AndroidManifest-common.xml
@@ -94,7 +94,6 @@
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
- <category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
</activity>
@@ -138,6 +137,7 @@
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
+ <category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
<intent-filter>
<action android:name="com.android.launcher3.intent.action.test_shortcut"/>
diff --git a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
index c99c4f1..136f115 100644
--- a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
+++ b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
@@ -567,6 +567,7 @@
ordinal == TestProtocol.OVERVIEW_STATE_ORDINAL);
break;
}
+ case TASKBAR_ALL_APPS:
case LAUNCHED_APP: {
assertTrue("Launcher is resumed in state: " + expectedContainerType,
!isResumed);
@@ -580,9 +581,10 @@
}
} else {
assertTrue(
- "Container type is not LAUNCHED_APP or FALLBACK_OVERVIEW: "
- + expectedContainerType,
+ "Container type is not LAUNCHED_APP, TASKBAR_ALL_APPS "
+ + "or FALLBACK_OVERVIEW: " + expectedContainerType,
expectedContainerType == ContainerType.LAUNCHED_APP
+ || expectedContainerType == ContainerType.TASKBAR_ALL_APPS
|| expectedContainerType == ContainerType.FALLBACK_OVERVIEW);
}
}
diff --git a/tests/tapl/com/android/launcher3/tapl/AllApps.java b/tests/tapl/com/android/launcher3/tapl/AllApps.java
index 3658f41..bfb115d 100644
--- a/tests/tapl/com/android/launcher3/tapl/AllApps.java
+++ b/tests/tapl/com/android/launcher3/tapl/AllApps.java
@@ -74,7 +74,7 @@
LauncherInstrumentation.log("hasClickableIcon: icon has insufficient height");
return false;
}
- if (iconCenterInSearchBox(allAppsContainer, icon)) {
+ if (hasSearchBox() && iconCenterInSearchBox(allAppsContainer, icon)) {
LauncherInstrumentation.log("hasClickableIcon: icon center is under search box");
return false;
}
@@ -107,7 +107,7 @@
final UiObject2 allAppsContainer = verifyActiveContainer();
final UiObject2 appListRecycler = mLauncher.waitForObjectInContainer(allAppsContainer,
"apps_list_view");
- final UiObject2 searchBox = getSearchBox(allAppsContainer);
+ final UiObject2 searchBox = hasSearchBox() ? getSearchBox(allAppsContainer) : null;
int deviceHeight = mLauncher.getRealDisplaySize().y;
int bottomGestureStartOnScreen = mLauncher.getBottomGestureStartOnScreen();
@@ -128,8 +128,10 @@
mLauncher.getVisibleBounds(icon).top
< bottomGestureStartOnScreen)
.collect(Collectors.toList()),
- mLauncher.getVisibleBounds(searchBox).bottom
- - mLauncher.getVisibleBounds(allAppsContainer).top);
+ hasSearchBox()
+ ? mLauncher.getVisibleBounds(searchBox).bottom
+ - mLauncher.getVisibleBounds(allAppsContainer).top
+ : 0);
verifyActiveContainer();
final int newScroll = getAllAppsScroll();
mLauncher.assertTrue(
@@ -173,18 +175,21 @@
return appIcon;
}
+ @NonNull
protected abstract AppIcon createAppIcon(UiObject2 icon);
+ protected abstract boolean hasSearchBox();
+
private void scrollBackToBeginning() {
try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
"want to scroll back in all apps")) {
LauncherInstrumentation.log("Scrolling to the beginning");
final UiObject2 allAppsContainer = verifyActiveContainer();
- final UiObject2 searchBox = getSearchBox(allAppsContainer);
+ final UiObject2 searchBox = hasSearchBox() ? getSearchBox(allAppsContainer) : null;
int attempts = 0;
- final Rect margins =
- new Rect(0, mLauncher.getVisibleBounds(searchBox).bottom + 1, 0, 5);
+ final Rect margins = new Rect(
+ 0, hasSearchBox() ? mLauncher.getVisibleBounds(searchBox).bottom + 1 : 0, 0, 5);
for (int scroll = getAllAppsScroll();
scroll != 0;
@@ -196,7 +201,11 @@
++attempts <= MAX_SCROLL_ATTEMPTS);
mLauncher.scroll(
- allAppsContainer, Direction.UP, margins, 12, false);
+ allAppsContainer,
+ Direction.UP,
+ margins,
+ /* steps= */ 12,
+ /* slowDown= */ false);
}
try (LauncherInstrumentation.Closable c1 = mLauncher.addContextLayer("scrolled up")) {
@@ -225,7 +234,11 @@
final UiObject2 allAppsContainer = verifyActiveContainer();
// Start the gesture in the center to avoid starting at elements near the top.
mLauncher.scroll(
- allAppsContainer, Direction.DOWN, new Rect(0, 0, 0, mHeight / 2), 10, false);
+ allAppsContainer,
+ Direction.DOWN,
+ new Rect(0, 0, 0, mHeight / 2),
+ /* steps= */ 10,
+ /* slowDown= */ false);
verifyActiveContainer();
}
}
@@ -240,7 +253,11 @@
final UiObject2 allAppsContainer = verifyActiveContainer();
// Start the gesture in the center, for symmetry with forward.
mLauncher.scroll(
- allAppsContainer, Direction.UP, new Rect(0, mHeight / 2, 0, 0), 10, false);
+ allAppsContainer,
+ Direction.UP,
+ new Rect(0, mHeight / 2, 0, 0),
+ /* steps= */ 10,
+ /*slowDown= */ false);
verifyActiveContainer();
}
}
diff --git a/tests/tapl/com/android/launcher3/tapl/AllAppsFromTaskbar.java b/tests/tapl/com/android/launcher3/tapl/AllAppsFromTaskbar.java
new file mode 100644
index 0000000..5164025
--- /dev/null
+++ b/tests/tapl/com/android/launcher3/tapl/AllAppsFromTaskbar.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.tapl;
+
+import androidx.annotation.NonNull;
+import androidx.test.uiautomator.UiObject2;
+
+/**
+ * Operations on AllApps opened from the Taskbar.
+ */
+public class AllAppsFromTaskbar extends AllApps {
+
+ AllAppsFromTaskbar(LauncherInstrumentation launcher) {
+ super(launcher);
+ }
+
+ @Override
+ protected LauncherInstrumentation.ContainerType getContainerType() {
+ return LauncherInstrumentation.ContainerType.TASKBAR_ALL_APPS;
+ }
+
+ @NonNull
+ @Override
+ public TaskbarAppIcon getAppIcon(String appName) {
+ return (TaskbarAppIcon) super.getAppIcon(appName);
+ }
+
+ @NonNull
+ @Override
+ protected TaskbarAppIcon createAppIcon(UiObject2 icon) {
+ return new TaskbarAppIcon(mLauncher, icon);
+ }
+
+ @Override
+ protected boolean hasSearchBox() {
+ return false;
+ }
+}
diff --git a/tests/tapl/com/android/launcher3/tapl/AppIcon.java b/tests/tapl/com/android/launcher3/tapl/AppIcon.java
index bef242c..e28f0af 100644
--- a/tests/tapl/com/android/launcher3/tapl/AppIcon.java
+++ b/tests/tapl/com/android/launcher3/tapl/AppIcon.java
@@ -51,7 +51,7 @@
public AppIconMenu openMenu() {
try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck()) {
return createMenu(mLauncher.clickAndGet(
- mObject, "popup_container", getLongClickEvent()));
+ mObject, /* resName= */ "popup_container", getLongClickEvent()));
}
}
@@ -61,7 +61,7 @@
public AppIconMenu openDeepShortcutMenu() {
try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck()) {
return createMenu(mLauncher.clickAndGet(
- mObject, "deep_shortcuts_container", getLongClickEvent()));
+ mObject, /* resName= */ "deep_shortcuts_container", getLongClickEvent()));
}
}
@@ -73,8 +73,8 @@
}
@Override
- protected String getLongPressIndicator() {
- return "popup_container";
+ protected void waitForLongPressConfirmation() {
+ mLauncher.waitForLauncherObject("popup_container");
}
@Override
diff --git a/tests/tapl/com/android/launcher3/tapl/AppIconMenuItem.java b/tests/tapl/com/android/launcher3/tapl/AppIconMenuItem.java
index a6a1531..5cf5aba 100644
--- a/tests/tapl/com/android/launcher3/tapl/AppIconMenuItem.java
+++ b/tests/tapl/com/android/launcher3/tapl/AppIconMenuItem.java
@@ -41,8 +41,8 @@
}
@Override
- protected String getLongPressIndicator() {
- return "drop_target_bar";
+ protected void waitForLongPressConfirmation() {
+ mLauncher.waitForLauncherObject("drop_target_bar");
}
@Override
diff --git a/tests/tapl/com/android/launcher3/tapl/HomeAllApps.java b/tests/tapl/com/android/launcher3/tapl/HomeAllApps.java
index 2e00d59..c275f3b 100644
--- a/tests/tapl/com/android/launcher3/tapl/HomeAllApps.java
+++ b/tests/tapl/com/android/launcher3/tapl/HomeAllApps.java
@@ -35,8 +35,14 @@
return (AllAppsAppIcon) super.getAppIcon(appName);
}
+ @NonNull
@Override
protected HomeAppIcon createAppIcon(UiObject2 icon) {
return new AllAppsAppIcon(mLauncher, icon);
}
+
+ @Override
+ protected boolean hasSearchBox() {
+ return true;
+ }
}
diff --git a/tests/tapl/com/android/launcher3/tapl/HomeAppIcon.java b/tests/tapl/com/android/launcher3/tapl/HomeAppIcon.java
index baabe12..71d8ba9 100644
--- a/tests/tapl/com/android/launcher3/tapl/HomeAppIcon.java
+++ b/tests/tapl/com/android/launcher3/tapl/HomeAppIcon.java
@@ -51,8 +51,7 @@
() -> {
final Rect bounds = target.getDropLocationBounds();
return new Point(bounds.centerX(), bounds.centerY());
- },
- getLongPressIndicator());
+ });
FolderIcon result = target.getTargetFolder(dropBounds);
mLauncher.assertTrue("Can't find the target folder.", result != null);
return result;
@@ -115,8 +114,12 @@
String.format("want to drag the icon to cell(%d, %d)", cellX, cellY))
) {
final Supplier<Point> dest = () -> Workspace.getCellCenter(mLauncher, cellX, cellY);
- Workspace.dragIconToWorkspace(mLauncher, this, dest, getLongPressIndicator(),
- () -> addExpectedEventsForLongClick(), null);
+ Workspace.dragIconToWorkspace(
+ mLauncher,
+ /* launchable= */ this,
+ dest,
+ () -> addExpectedEventsForLongClick(),
+ /*expectDropEvents= */ null);
try (LauncherInstrumentation.Closable ignore = mLauncher.addContextLayer("dragged")) {
WorkspaceAppIcon appIcon =
(WorkspaceAppIcon) mLauncher.getWorkspace().getWorkspaceAppIcon(mAppName);
diff --git a/tests/tapl/com/android/launcher3/tapl/Launchable.java b/tests/tapl/com/android/launcher3/tapl/Launchable.java
index a28eac6..45a0196 100644
--- a/tests/tapl/com/android/launcher3/tapl/Launchable.java
+++ b/tests/tapl/com/android/launcher3/tapl/Launchable.java
@@ -97,8 +97,7 @@
return new LaunchedAppState(mLauncher);
}
- Point startDrag(long downTime, String longPressIndicator,
- Runnable expectLongClickEvents, boolean runToSpringLoadedState) {
+ Point startDrag(long downTime, Runnable expectLongClickEvents, boolean runToSpringLoadedState) {
final Point iconCenter = getObject().getVisibleCenter();
final Point dragStartCenter = new Point(iconCenter.x,
iconCenter.y - getStartDragThreshold());
@@ -108,7 +107,6 @@
downTime,
iconCenter,
dragStartCenter,
- longPressIndicator,
expectLongClickEvents),
SPRING_LOADED_STATE_ORDINAL, "long-pressing and triggering drag start");
} else {
@@ -116,7 +114,6 @@
downTime,
iconCenter,
dragStartCenter,
- longPressIndicator,
expectLongClickEvents);
}
@@ -125,21 +122,43 @@
}
/**
+ * Waits for a confirmation that a long press has successfully been triggered.
+ *
+ * This method waits for a view to either appear or disappear to confirm that the long press
+ * has been triggered and fails if no confirmation is received before the default timeout.
+ */
+ protected abstract void waitForLongPressConfirmation();
+
+ /**
* Drags this Launchable a short distance before starting a full drag.
*
* This is necessary for shortcuts, which require being dragged beyond a threshold to close
* their container and start drag callbacks.
*/
- private void movePointerForStartDrag(long downTime, Point iconCenter, Point dragStartCenter,
- String longPressIndicator, Runnable expectLongClickEvents) {
- mLauncher.sendPointer(downTime, downTime, MotionEvent.ACTION_DOWN,
- iconCenter, LauncherInstrumentation.GestureScope.INSIDE);
+ private void movePointerForStartDrag(
+ long downTime,
+ Point iconCenter,
+ Point dragStartCenter,
+ Runnable expectLongClickEvents) {
+ mLauncher.sendPointer(
+ downTime,
+ downTime,
+ MotionEvent.ACTION_DOWN,
+ iconCenter,
+ LauncherInstrumentation.GestureScope.INSIDE);
LauncherInstrumentation.log("movePointerForStartDrag: sent down");
expectLongClickEvents.run();
- mLauncher.waitForLauncherObject(longPressIndicator);
+ waitForLongPressConfirmation();
LauncherInstrumentation.log("movePointerForStartDrag: indicator");
- mLauncher.movePointer(iconCenter, dragStartCenter, DEFAULT_DRAG_STEPS, false,
- downTime, downTime, true, LauncherInstrumentation.GestureScope.INSIDE);
+ mLauncher.movePointer(
+ iconCenter,
+ dragStartCenter,
+ DEFAULT_DRAG_STEPS,
+ /* isDecelerating= */ false,
+ downTime,
+ downTime,
+ /* slowDown= */ true,
+ LauncherInstrumentation.GestureScope.INSIDE);
}
private int getStartDragThreshold() {
@@ -148,6 +167,4 @@
}
protected abstract void addExpectedEventsForLongClick();
-
- protected abstract String getLongPressIndicator();
}
diff --git a/tests/tapl/com/android/launcher3/tapl/LaunchedAppState.java b/tests/tapl/com/android/launcher3/tapl/LaunchedAppState.java
index 3f1be80..2033a42 100644
--- a/tests/tapl/com/android/launcher3/tapl/LaunchedAppState.java
+++ b/tests/tapl/com/android/launcher3/tapl/LaunchedAppState.java
@@ -16,11 +16,27 @@
package com.android.launcher3.tapl;
+import static com.android.launcher3.testing.TestProtocol.REQUEST_DISABLE_MANUAL_TASKBAR_STASHING;
+import static com.android.launcher3.testing.TestProtocol.REQUEST_ENABLE_MANUAL_TASKBAR_STASHING;
+import static com.android.launcher3.testing.TestProtocol.REQUEST_STASHED_TASKBAR_HEIGHT;
+
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.os.SystemClock;
+import android.view.MotionEvent;
+
+import androidx.test.uiautomator.By;
+
+import com.android.launcher3.testing.TestProtocol;
+
/**
* Background state operations specific to when an app has been launched.
*/
public final class LaunchedAppState extends Background {
+ // More drag steps than Launchables to give the window manager time to register the drag.
+ private static final int DEFAULT_DRAG_STEPS = 35;
+
LaunchedAppState(LauncherInstrumentation launcher) {
super(launcher);
}
@@ -29,4 +45,126 @@
protected LauncherInstrumentation.ContainerType getContainerType() {
return LauncherInstrumentation.ContainerType.LAUNCHED_APP;
}
+
+ /**
+ * Returns the taskbar.
+ *
+ * The taskbar must already be visible when calling this method.
+ */
+ public Taskbar getTaskbar() {
+ try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
+ "want to get the taskbar")) {
+ mLauncher.waitForLauncherObject("taskbar_view");
+
+ return new Taskbar(mLauncher);
+ }
+ }
+
+ /**
+ * Returns the Taskbar in a visible state.
+ *
+ * The taskbar must already be hidden when calling this method.
+ */
+ public Taskbar showTaskbar() {
+ mLauncher.getTestInfo(REQUEST_ENABLE_MANUAL_TASKBAR_STASHING);
+
+ try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck();
+ LauncherInstrumentation.Closable c1 = mLauncher.addContextLayer(
+ "want to show the taskbar")) {
+ mLauncher.waitUntilLauncherObjectGone("taskbar_view");
+
+ final long downTime = SystemClock.uptimeMillis();
+ final int unstashTargetY = mLauncher.getRealDisplaySize().y
+ - (mLauncher.getTestInfo(REQUEST_STASHED_TASKBAR_HEIGHT)
+ .getInt(TestProtocol.TEST_INFO_RESPONSE_FIELD) / 2);
+ final Point unstashTarget = new Point(
+ mLauncher.getRealDisplaySize().x / 2, unstashTargetY);
+
+ mLauncher.sendPointer(downTime, downTime, MotionEvent.ACTION_DOWN, unstashTarget,
+ LauncherInstrumentation.GestureScope.OUTSIDE_WITH_PILFER);
+ LauncherInstrumentation.log("showTaskbar: sent down");
+
+ try (LauncherInstrumentation.Closable c2 = mLauncher.addContextLayer("pressed down")) {
+ mLauncher.waitForLauncherObject("taskbar_view");
+ mLauncher.sendPointer(downTime, downTime, MotionEvent.ACTION_UP, unstashTarget,
+ LauncherInstrumentation.GestureScope.OUTSIDE_WITH_PILFER);
+
+ return new Taskbar(mLauncher);
+ }
+ } finally {
+ mLauncher.getTestInfo(REQUEST_DISABLE_MANUAL_TASKBAR_STASHING);
+ }
+ }
+
+ static void dragToSplitscreen(
+ LauncherInstrumentation launcher, Launchable launchable, String expectedNewPackageName,
+ String expectedExistingPackageName) {
+ try (LauncherInstrumentation.Closable c1 = launcher.addContextLayer(
+ "want to drag taskbar item to splitscreen")) {
+ final Point displaySize = launcher.getRealDisplaySize();
+ final Point endPoint = new Point(displaySize.x / 4, 3 * displaySize.y / 4);
+ final long downTime = SystemClock.uptimeMillis();
+ // Use mObject before starting drag since the system drag and drop moves the original
+ // view.
+ Point itemVisibleCenter = launchable.mObject.getVisibleCenter();
+ Rect itemVisibleBounds = launcher.getVisibleBounds(launchable.mObject);
+ String itemLabel = launchable.mObject.getText();
+
+ Point dragStart = launchable.startDrag(
+ downTime,
+ launchable::addExpectedEventsForLongClick,
+ /* runToSpringLoadedState= */ false);
+
+ try (LauncherInstrumentation.Closable c2 = launcher.addContextLayer(
+ "started item drag")) {
+ launcher.movePointer(
+ dragStart,
+ endPoint,
+ DEFAULT_DRAG_STEPS,
+ /* isDecelerating= */ true,
+ downTime,
+ SystemClock.uptimeMillis(),
+ /* slowDown= */ false,
+ LauncherInstrumentation.GestureScope.INSIDE);
+
+ try (LauncherInstrumentation.Closable c3 = launcher.addContextLayer(
+ "moved pointer to drop point")) {
+ dropDraggedItem(
+ launcher,
+ launchable,
+ expectedNewPackageName,
+ endPoint, downTime,
+ itemVisibleCenter,
+ itemVisibleBounds,
+ itemLabel,
+ expectedExistingPackageName);
+ }
+ }
+ }
+ }
+
+ private static void dropDraggedItem(
+ LauncherInstrumentation launcher, Launchable launchable, String expectedNewPackageName,
+ Point endPoint, long downTime, Point itemVisibleCenter, Rect itemVisibleBounds,
+ String itemLabel, String expectedExistingPackageName) {
+ LauncherInstrumentation.log("SplitscreenDragSource.dragToSplitscreen before drop "
+ + itemVisibleCenter + " in " + itemVisibleBounds);
+
+ launchable.executeAndWaitForWindowChange(() -> {
+ launcher.sendPointer(
+ downTime,
+ SystemClock.uptimeMillis(),
+ MotionEvent.ACTION_UP,
+ endPoint,
+ LauncherInstrumentation.GestureScope.INSIDE_TO_OUTSIDE_WITHOUT_PILFER);
+ LauncherInstrumentation.log("SplitscreenDragSource.dragToSplitscreen: after "
+ + "drop");
+ }, itemLabel, "dropping taskbar item");
+
+ try (LauncherInstrumentation.Closable c = launcher.addContextLayer("dropped item")) {
+ launchable.assertAppLaunched(itemLabel, By.pkg(expectedNewPackageName));
+ launcher.checkPackagesVisible(
+ new String[] {expectedNewPackageName, expectedExistingPackageName});
+ }
+ }
}
diff --git a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
index bbfbc55..1e3b078 100644
--- a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
+++ b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
@@ -84,6 +84,7 @@
import java.util.LinkedList;
import java.util.List;
import java.util.Optional;
+import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.function.Consumer;
@@ -123,7 +124,8 @@
// Types for launcher containers that the user is interacting with. "Background" is a
// pseudo-container corresponding to inactive launcher covered by another app.
public enum ContainerType {
- WORKSPACE, HOME_ALL_APPS, OVERVIEW, WIDGETS, FALLBACK_OVERVIEW, LAUNCHED_APP
+ WORKSPACE, HOME_ALL_APPS, OVERVIEW, WIDGETS, FALLBACK_OVERVIEW, LAUNCHED_APP,
+ TASKBAR_ALL_APPS
}
public enum NavigationModel {ZERO_BUTTON, THREE_BUTTON}
@@ -167,6 +169,7 @@
private static final String OVERVIEW_RES_ID = "overview_panel";
private static final String WIDGETS_RES_ID = "primary_widgets_list_view";
private static final String CONTEXT_MENU_RES_ID = "popup_container";
+ private static final String TASKBAR_RES_ID = "taskbar_view";
public static final int WAIT_TIME_MS = 60000;
private static final String SYSTEMUI_PACKAGE = "com.android.systemui";
private static final String ANDROID_PACKAGE = "android";
@@ -500,6 +503,16 @@
}
}
+ void checkPackagesVisible(String[] expectedVisiblePackages) {
+ Set<String> actualVisiblePackages =
+ getVisiblePackagesStream().collect(Collectors.toSet());
+
+ for (String expectedVisible : expectedVisiblePackages) {
+ assertTrue("Expected package not visible: " + expectedVisible,
+ actualVisiblePackages.contains(expectedVisible));
+ }
+ }
+
private String getVisiblePackages() {
final String apps = getVisiblePackagesStream().collect(Collectors.joining(", "));
return !apps.isEmpty()
@@ -722,27 +735,41 @@
waitUntilLauncherObjectGone(APPS_RES_ID);
waitUntilLauncherObjectGone(OVERVIEW_RES_ID);
waitUntilLauncherObjectGone(WIDGETS_RES_ID);
+ waitUntilLauncherObjectGone(TASKBAR_RES_ID);
+
return waitForLauncherObject(WORKSPACE_RES_ID);
}
case WIDGETS: {
waitUntilLauncherObjectGone(WORKSPACE_RES_ID);
waitUntilLauncherObjectGone(APPS_RES_ID);
waitUntilLauncherObjectGone(OVERVIEW_RES_ID);
+ waitUntilLauncherObjectGone(TASKBAR_RES_ID);
+
return waitForLauncherObject(WIDGETS_RES_ID);
}
+ case TASKBAR_ALL_APPS:
case HOME_ALL_APPS: {
waitUntilLauncherObjectGone(WORKSPACE_RES_ID);
waitUntilLauncherObjectGone(OVERVIEW_RES_ID);
waitUntilLauncherObjectGone(WIDGETS_RES_ID);
+ waitUntilLauncherObjectGone(TASKBAR_RES_ID);
+
return waitForLauncherObject(APPS_RES_ID);
}
case OVERVIEW: {
waitUntilLauncherObjectGone(APPS_RES_ID);
waitUntilLauncherObjectGone(WORKSPACE_RES_ID);
waitUntilLauncherObjectGone(WIDGETS_RES_ID);
+ waitUntilLauncherObjectGone(TASKBAR_RES_ID);
+
return waitForLauncherObject(OVERVIEW_RES_ID);
}
case FALLBACK_OVERVIEW: {
+ waitUntilLauncherObjectGone(APPS_RES_ID);
+ waitUntilLauncherObjectGone(WORKSPACE_RES_ID);
+ waitUntilLauncherObjectGone(WIDGETS_RES_ID);
+ waitUntilLauncherObjectGone(TASKBAR_RES_ID);
+
return waitForFallbackLauncherObject(OVERVIEW_RES_ID);
}
case LAUNCHED_APP: {
@@ -750,6 +777,12 @@
waitUntilLauncherObjectGone(APPS_RES_ID);
waitUntilLauncherObjectGone(OVERVIEW_RES_ID);
waitUntilLauncherObjectGone(WIDGETS_RES_ID);
+
+ if (isTablet() && !isFallbackOverview()) {
+ waitForLauncherObject(TASKBAR_RES_ID);
+ } else {
+ waitUntilLauncherObjectGone(TASKBAR_RES_ID);
+ }
return null;
}
default:
@@ -863,8 +896,13 @@
setForcePauseTimeout(FORCE_PAUSE_TIMEOUT_MS);
final Point displaySize = getRealDisplaySize();
+ // The swipe up to home gesture starts from inside the launcher when the user is
+ // already home. Otherwise, the gesture can start inside the launcher process if the
+ // taskbar is visible.
boolean gestureStartFromLauncher = isTablet()
- ? !isLauncher3() || hasLauncherObject(WORKSPACE_RES_ID)
+ ? !isLauncher3()
+ || hasLauncherObject(WORKSPACE_RES_ID)
+ || hasLauncherObject(TASKBAR_RES_ID)
: isLauncherVisible();
// CLose floating views before going back to home.
@@ -1301,9 +1339,7 @@
}
void scrollToLastVisibleRow(
- UiObject2 container,
- Collection<UiObject2> items,
- int topPaddingInContainer) {
+ UiObject2 container, Collection<UiObject2> items, int topPaddingInContainer) {
final UiObject2 lowestItem = Collections.max(items, (i1, i2) ->
Integer.compare(getVisibleBounds(i1).top, getVisibleBounds(i2).top));
@@ -1326,8 +1362,8 @@
containerRect.height() - distance - bottomGestureMarginInContainer,
0,
bottomGestureMarginInContainer),
- 10,
- true);
+ /* steps= */ 10,
+ /* slowDown= */ true);
}
void scrollLeftByDistance(UiObject2 container, int distance) {
@@ -1650,6 +1686,29 @@
getTestInfo(TestProtocol.REQUEST_CLEAR_DATA);
}
+ /**
+ * Reloads the workspace with a test layout that includes the Test Activity app icon on the
+ * hotseat.
+ */
+ public void useTestWorkspaceLayoutOnReload() {
+ getTestInfo(TestProtocol.REQUEST_USE_TEST_WORKSPACE_LAYOUT);
+ }
+
+ /** Reloads the workspace with the default layout defined by the user's grid size selection. */
+ public void useDefaultWorkspaceLayoutOnReload() {
+ getTestInfo(TestProtocol.REQUEST_USE_DEFAULT_WORKSPACE_LAYOUT);
+ }
+
+ /** Shows the taskbar if it is hidden, otherwise does nothing. */
+ public void showTaskbarIfHidden() {
+ getTestInfo(TestProtocol.REQUEST_UNSTASH_TASKBAR_IF_STASHED);
+ }
+
+ public List<String> getHotseatIconNames() {
+ return getTestInfo(TestProtocol.REQUEST_HOTSEAT_ICON_NAMES)
+ .getStringArrayList(TestProtocol.TEST_INFO_RESPONSE_FIELD);
+ }
+
private String[] getActivities() {
return getTestInfo(TestProtocol.REQUEST_GET_ACTIVITIES)
.getStringArray(TestProtocol.TEST_INFO_RESPONSE_FIELD);
diff --git a/tests/tapl/com/android/launcher3/tapl/SplitscreenDragSource.java b/tests/tapl/com/android/launcher3/tapl/SplitscreenDragSource.java
new file mode 100644
index 0000000..ce1c3c0
--- /dev/null
+++ b/tests/tapl/com/android/launcher3/tapl/SplitscreenDragSource.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.tapl;
+
+/** Launchable that can serve as a source for dragging and dropping to splitscreen. */
+interface SplitscreenDragSource {
+
+ /**
+ * Drags this app icon to the left (landscape) or bottom (portrait) of the screen, launching it
+ * in splitscreen.
+ *
+ * @param expectedNewPackageName package name of the app being dragged
+ * @param expectedExistingPackageName package name of the already-launched app
+ */
+ default void dragToSplitscreen(
+ String expectedNewPackageName, String expectedExistingPackageName) {
+ Launchable launchable = getLaunchable();
+ LauncherInstrumentation launcher = launchable.mLauncher;
+ try (LauncherInstrumentation.Closable e = launcher.eventsCheck()) {
+ LaunchedAppState.dragToSplitscreen(
+ launcher, launchable, expectedNewPackageName, expectedExistingPackageName);
+ }
+ }
+
+ Launchable getLaunchable();
+}
diff --git a/tests/tapl/com/android/launcher3/tapl/Taskbar.java b/tests/tapl/com/android/launcher3/tapl/Taskbar.java
new file mode 100644
index 0000000..b5a08c3
--- /dev/null
+++ b/tests/tapl/com/android/launcher3/tapl/Taskbar.java
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.tapl;
+
+import static com.android.launcher3.testing.TestProtocol.REQUEST_DISABLE_MANUAL_TASKBAR_STASHING;
+import static com.android.launcher3.testing.TestProtocol.REQUEST_ENABLE_MANUAL_TASKBAR_STASHING;
+
+import android.graphics.Point;
+import android.os.SystemClock;
+import android.text.TextUtils;
+import android.view.MotionEvent;
+import android.widget.TextView;
+
+import androidx.annotation.NonNull;
+import androidx.test.uiautomator.By;
+import androidx.test.uiautomator.BySelector;
+import androidx.test.uiautomator.UiObject2;
+
+import java.util.List;
+import java.util.stream.Collectors;
+
+/**
+ * Operations on the Taskbar from LaunchedApp.
+ */
+public final class Taskbar {
+
+ private final LauncherInstrumentation mLauncher;
+
+ Taskbar(LauncherInstrumentation launcher) {
+ mLauncher = launcher;
+ }
+
+ /**
+ * Returns an app icon with the given name. This fails if the icon is not found.
+ */
+ @NonNull
+ public TaskbarAppIcon getAppIcon(String appName) {
+ try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
+ "want to get a taskbar icon")) {
+ return new TaskbarAppIcon(mLauncher, mLauncher.waitForObjectInContainer(
+ mLauncher.waitForLauncherObject("taskbar_view"),
+ AppIcon.getAppIconSelector(appName, mLauncher)));
+ }
+ }
+
+ /**
+ * Hides this taskbar.
+ *
+ * The taskbar must already be visible when calling this method.
+ */
+ public void hide() {
+ mLauncher.getTestInfo(REQUEST_ENABLE_MANUAL_TASKBAR_STASHING);
+
+ try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
+ "want to hide the taskbar");
+ LauncherInstrumentation.Closable e = mLauncher.eventsCheck()) {
+ mLauncher.waitForLauncherObject("taskbar_view");
+
+ final long downTime = SystemClock.uptimeMillis();
+ Point stashTarget = new Point(
+ mLauncher.getRealDisplaySize().x - 1, mLauncher.getRealDisplaySize().y - 1);
+
+ mLauncher.sendPointer(downTime, downTime, MotionEvent.ACTION_DOWN, stashTarget,
+ LauncherInstrumentation.GestureScope.INSIDE);
+ LauncherInstrumentation.log("hideTaskbar: sent down");
+
+ try (LauncherInstrumentation.Closable c2 = mLauncher.addContextLayer("pressed down")) {
+ mLauncher.waitUntilLauncherObjectGone("taskbar_view");
+ mLauncher.sendPointer(downTime, downTime, MotionEvent.ACTION_UP, stashTarget,
+ LauncherInstrumentation.GestureScope.INSIDE);
+ }
+ } finally {
+ mLauncher.getTestInfo(REQUEST_DISABLE_MANUAL_TASKBAR_STASHING);
+ }
+ }
+
+ /**
+ * Opens the Taskbar all apps page.
+ */
+ public AllAppsFromTaskbar openAllApps() {
+ try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
+ "want to open taskbar all apps");
+ LauncherInstrumentation.Closable e = mLauncher.eventsCheck()) {
+
+ mLauncher.clickLauncherObject(mLauncher.waitForObjectInContainer(
+ mLauncher.waitForLauncherObject("taskbar_view"), getAllAppsButtonSelector()));
+
+ return new AllAppsFromTaskbar(mLauncher);
+ }
+ }
+
+ /** Returns a list of app icon names on the Taskbar */
+ public List<String> getIconNames() {
+ try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
+ "want to get all taskbar icons")) {
+ return mLauncher.waitForObjectsInContainer(
+ mLauncher.waitForLauncherObject("taskbar_view"),
+ AppIcon.getAnyAppIconSelector())
+ .stream()
+ .map(UiObject2::getText)
+ .filter(text -> !TextUtils.isEmpty(text)) // Filter out the all apps button
+ .collect(Collectors.toList());
+ }
+ }
+
+ private static BySelector getAllAppsButtonSelector() {
+ // Look for an icon with no text
+ return By.clazz(TextView.class).text("");
+ }
+}
diff --git a/tests/tapl/com/android/launcher3/tapl/TaskbarAppIcon.java b/tests/tapl/com/android/launcher3/tapl/TaskbarAppIcon.java
new file mode 100644
index 0000000..099acd4
--- /dev/null
+++ b/tests/tapl/com/android/launcher3/tapl/TaskbarAppIcon.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.tapl;
+
+import androidx.test.uiautomator.UiObject2;
+
+import java.util.regex.Pattern;
+
+/**
+ * App icon specifically on the Taskbar.
+ */
+public final class TaskbarAppIcon extends AppIcon implements SplitscreenDragSource {
+
+ private static final Pattern LONG_CLICK_EVENT = Pattern.compile("onTaskbarItemLongClick");
+
+ TaskbarAppIcon(LauncherInstrumentation launcher, UiObject2 icon) {
+ super(launcher, icon);
+ }
+
+ @Override
+ protected Pattern getLongClickEvent() {
+ return LONG_CLICK_EVENT;
+ }
+
+ @Override
+ public TaskbarAppIconMenu openDeepShortcutMenu() {
+ return (TaskbarAppIconMenu) super.openDeepShortcutMenu();
+ }
+
+ @Override
+ protected TaskbarAppIconMenu createMenu(UiObject2 menu) {
+ return new TaskbarAppIconMenu(mLauncher, menu);
+ }
+
+ @Override
+ public Launchable getLaunchable() {
+ return this;
+ }
+}
diff --git a/tests/tapl/com/android/launcher3/tapl/TaskbarAppIconMenu.java b/tests/tapl/com/android/launcher3/tapl/TaskbarAppIconMenu.java
new file mode 100644
index 0000000..1f137c5
--- /dev/null
+++ b/tests/tapl/com/android/launcher3/tapl/TaskbarAppIconMenu.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.tapl;
+
+import androidx.test.uiautomator.UiObject2;
+
+/**
+ * Context menu of a Taskbar app icon.
+ */
+public final class TaskbarAppIconMenu extends AppIconMenu {
+
+ TaskbarAppIconMenu(LauncherInstrumentation launcher, UiObject2 deepShortcutsContainer) {
+ super(launcher, deepShortcutsContainer);
+ }
+
+ @Override
+ public TaskbarAppIconMenuItem getMenuItem(String shortcutText) {
+ return (TaskbarAppIconMenuItem) super.getMenuItem(shortcutText);
+ }
+
+ @Override
+ protected TaskbarAppIconMenuItem createMenuItem(UiObject2 menuItem) {
+ return new TaskbarAppIconMenuItem(mLauncher, menuItem);
+ }
+}
diff --git a/tests/tapl/com/android/launcher3/tapl/TaskbarAppIconMenuItem.java b/tests/tapl/com/android/launcher3/tapl/TaskbarAppIconMenuItem.java
new file mode 100644
index 0000000..69a8a08
--- /dev/null
+++ b/tests/tapl/com/android/launcher3/tapl/TaskbarAppIconMenuItem.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.tapl;
+
+import androidx.test.uiautomator.UiObject2;
+
+import com.android.launcher3.testing.TestProtocol;
+
+import java.util.regex.Pattern;
+
+/**
+ * Menu item in a Taskbar app icon menu.
+ */
+public final class TaskbarAppIconMenuItem extends AppIconMenuItem implements SplitscreenDragSource {
+
+ private static final Pattern LONG_CLICK_EVENT = Pattern.compile("onTaskbarItemLongClick");
+
+ TaskbarAppIconMenuItem(
+ LauncherInstrumentation launcher, UiObject2 shortcut) {
+ super(launcher, shortcut);
+ }
+
+ @Override
+ protected void addExpectedEventsForLongClick() {
+ mLauncher.expectEvent(TestProtocol.SEQUENCE_MAIN, LONG_CLICK_EVENT);
+ }
+
+ @Override
+ protected void waitForLongPressConfirmation() {
+ // On long-press, the popup container closes and the system drag-and-drop begins. This
+ // only leaves launcher views that were previously visible.
+ mLauncher.waitUntilLauncherObjectGone("popup_container");
+ }
+
+ @Override
+ protected String launchableType() {
+ return "taskbar app icon menu item";
+ }
+
+ @Override
+ public Launchable getLaunchable() {
+ return this;
+ }
+}
diff --git a/tests/tapl/com/android/launcher3/tapl/Widget.java b/tests/tapl/com/android/launcher3/tapl/Widget.java
index 73e9830..2346249 100644
--- a/tests/tapl/com/android/launcher3/tapl/Widget.java
+++ b/tests/tapl/com/android/launcher3/tapl/Widget.java
@@ -39,8 +39,8 @@
}
@Override
- protected String getLongPressIndicator() {
- return "drop_target_bar";
+ protected void waitForLongPressConfirmation() {
+ mLauncher.waitForLauncherObject("drop_target_bar");
}
@Override
diff --git a/tests/tapl/com/android/launcher3/tapl/Workspace.java b/tests/tapl/com/android/launcher3/tapl/Workspace.java
index 3bc5389..fee4490 100644
--- a/tests/tapl/com/android/launcher3/tapl/Workspace.java
+++ b/tests/tapl/com/android/launcher3/tapl/Workspace.java
@@ -205,7 +205,6 @@
mLauncher,
homeAppIcon,
new Point(targetX, mLauncher.getVisibleBounds(workspace).centerY()),
- "popup_container",
false,
false,
() -> mLauncher.expectEvent(
@@ -244,11 +243,11 @@
LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
"removing app icon from workspace")) {
dragIconToWorkspace(
- mLauncher, homeAppIcon,
+ mLauncher,
+ homeAppIcon,
() -> getDropPointFromDropTargetBar(mLauncher, DELETE_TARGET_TEXT_ID),
- homeAppIcon.getLongPressIndicator(),
() -> mLauncher.expectEvent(TestProtocol.SEQUENCE_MAIN, LONG_CLICK_EVENT),
- null);
+ /* expectDropEvents= */ null);
try (LauncherInstrumentation.Closable c1 = mLauncher.addContextLayer(
"dragged the app to the drop bar")) {
@@ -274,11 +273,11 @@
try (LauncherInstrumentation.Closable c = launcher.addContextLayer(
"uninstalling app icon")) {
dragIconToWorkspace(
- launcher, homeAppIcon,
+ launcher,
+ homeAppIcon,
() -> getDropPointFromDropTargetBar(launcher, UNINSTALL_TARGET_TEXT_ID),
- homeAppIcon.getLongPressIndicator(),
expectLongClickEvents,
- null);
+ /* expectDropEvents= */null);
launcher.waitUntilLauncherObjectGone(DROP_BAR_RES_ID);
@@ -345,15 +344,15 @@
}
static void dragIconToWorkspace(LauncherInstrumentation launcher, Launchable launchable,
- Point dest, String longPressIndicator, boolean startsActivity, boolean isWidgetShortcut,
+ Point dest, boolean startsActivity, boolean isWidgetShortcut,
Runnable expectLongClickEvents) {
Runnable expectDropEvents = null;
if (startsActivity || isWidgetShortcut) {
expectDropEvents = () -> launcher.expectEvent(TestProtocol.SEQUENCE_MAIN,
LauncherInstrumentation.EVENT_START);
}
- dragIconToWorkspace(launcher, launchable, () -> dest, longPressIndicator,
- expectLongClickEvents, expectDropEvents);
+ dragIconToWorkspace(
+ launcher, launchable, () -> dest, expectLongClickEvents, expectDropEvents);
}
/**
@@ -361,22 +360,27 @@
* (There is no slow down time before drop event)
* This function expects the launchable is inside the workspace and there is no drop event.
*/
- static void dragIconToWorkspace(LauncherInstrumentation launcher, Launchable launchable,
- Supplier<Point> destSupplier, String longPressIndicator) {
- dragIconToWorkspace(launcher, launchable, destSupplier, longPressIndicator,
- () -> launcher.expectEvent(TestProtocol.SEQUENCE_MAIN, LONG_CLICK_EVENT), null);
+ static void dragIconToWorkspace(
+ LauncherInstrumentation launcher, Launchable launchable, Supplier<Point> destSupplier) {
+ dragIconToWorkspace(
+ launcher,
+ launchable,
+ destSupplier,
+ () -> launcher.expectEvent(TestProtocol.SEQUENCE_MAIN, LONG_CLICK_EVENT),
+ /* expectDropEvents= */ null);
}
static void dragIconToWorkspace(
- LauncherInstrumentation launcher, Launchable launchable, Supplier<Point> dest,
- String longPressIndicator, Runnable expectLongClickEvents,
+ LauncherInstrumentation launcher,
+ Launchable launchable,
+ Supplier<Point> dest,
+ Runnable expectLongClickEvents,
@Nullable Runnable expectDropEvents) {
try (LauncherInstrumentation.Closable ignored = launcher.addContextLayer(
"want to drag icon to workspace")) {
final long downTime = SystemClock.uptimeMillis();
Point dragStart = launchable.startDrag(
downTime,
- longPressIndicator,
expectLongClickEvents,
/* runToSpringLoadedState= */ true);
Point targetDest = dest.get();
diff --git a/tests/tapl/com/android/launcher3/tapl/WorkspaceDragSource.java b/tests/tapl/com/android/launcher3/tapl/WorkspaceDragSource.java
index 93c2213..d8d4420 100644
--- a/tests/tapl/com/android/launcher3/tapl/WorkspaceDragSource.java
+++ b/tests/tapl/com/android/launcher3/tapl/WorkspaceDragSource.java
@@ -41,7 +41,6 @@
? launchableCenter.x - width / 2
: launchableCenter.x + width / 2,
displaySize.y / 2),
- launchable.getLongPressIndicator(),
startsActivity,
isWidgetShortcut,
launchable::addExpectedEventsForLongClick);