Merge "Add taskbar TAPL tests" into tm-dev

This commit is contained in:
TreeHugger Robot
2022-03-11 00:00:36 +00:00
committed by Android (Google) Code Review
34 changed files with 1129 additions and 87 deletions
+2
View File
@@ -208,6 +208,8 @@ filegroup {
srcs: [
"ext_tests/src/**/*.java",
"ext_tests/src/**/*.kt",
"quickstep/ext_tests/src/**/*.java",
"quickstep/ext_tests/src/**/*.kt",
],
}
@@ -29,8 +29,10 @@ import android.view.View;
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 @@ public class DebugTestInformationHandler extends TestInformationHandler {
}
}
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 @@ public class DebugTestInformationHandler extends TestInformationHandler {
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);
}
}
}
@@ -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);
}
}
@@ -27,6 +27,7 @@ import android.view.TaskTransitionSpec;
import android.view.WindowManagerGlobal;
import androidx.annotation.NonNull;
import androidx.annotation.VisibleForTesting;
import com.android.launcher3.BaseQuickstepLauncher;
import com.android.launcher3.DeviceProfile;
@@ -117,6 +118,24 @@ public class LauncherTaskbarUIController extends TaskbarUIController {
shouldDelayLauncherStateAnim);
}
/**
* 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.
*/
@@ -71,6 +71,8 @@ import com.android.launcher3.model.data.ItemInfo;
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 @@ public class TaskbarActivityContext extends BaseTaskbarContext {
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 @@ public class TaskbarActivityContext extends BaseTaskbarContext {
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);
@@ -63,6 +63,8 @@ import com.android.launcher3.model.data.WorkspaceItemInfo;
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 @@ public class TaskbarDragController extends DragController<BaseTaskbarContext> im
if (!(view instanceof BubbleTextView)) {
return false;
}
TestLogging.recordEvent(TestProtocol.SEQUENCE_MAIN, "onTaskbarItemLongClick");
BubbleTextView btv = (BubbleTextView) view;
mActivity.onDragStart();
btv.post(() -> {
@@ -164,24 +166,7 @@ public class TaskbarDragController extends DragController<BaseTaskbarContext> im
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 @@ public class TaskbarDragController extends DragController<BaseTaskbarContext> im
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,
@@ -145,6 +145,7 @@ public class TaskbarPopupController implements TaskbarControllers.LoggableTaskba
});
// TODO (b/198438631): configure for taskbar/context
container.setPopupItemDragHandler(new TaskbarPopupItemDragHandler());
mControllers.taskbarDragController.addDragListener(container);
container.populateAndShow(icon,
mPopupDataProvider.getShortcutCountForItem(item),
@@ -136,6 +136,8 @@ public class TaskbarStashController implements TaskbarControllers.LoggableTaskba
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 @@ public class TaskbarStashController implements TaskbarControllers.LoggableTaskba
*/
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;
}
/**
@@ -24,6 +24,7 @@ import static com.android.systemui.shared.system.ViewTreeObserverWrapper.InsetsI
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.BaseTaskbarContext;
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;
@@ -179,6 +182,12 @@ class TaskbarAllAppsContext extends BaseTaskbarContext {
mControllers = new TouchController[]{mActivity.mDragController};
}
@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) {
@@ -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;
}
}
+29
View File
@@ -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>
@@ -102,6 +102,8 @@ public class LauncherProvider extends ContentProvider {
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 @@ public class LauncherProvider extends ContentProvider {
private long mLastRestoreTimestamp = 0L;
private boolean mUseTestWorkspaceLayout;
/**
* $ adb shell dumpsys activity provider com.android.launcher3
*/
@@ -390,6 +394,14 @@ public class LauncherProvider extends ContentProvider {
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 @@ public class LauncherProvider extends ContentProvider {
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) {
@@ -374,6 +374,12 @@ public class LauncherSettings {
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";
@@ -98,6 +98,7 @@ public abstract class DragView<T extends Context & ActivityContext> extends Fram
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 abstract class DragView<T extends Context & ActivityContext> extends Fram
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 @@ public abstract class DragView<T extends Context & ActivityContext> extends Fram
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}.
@@ -82,6 +82,10 @@ public final class TestProtocol {
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 final class TestProtocol {
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";
+1 -1
View File
@@ -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"/>
@@ -567,6 +567,7 @@ public abstract class AbstractLauncherUiTest {
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 @@ public abstract class AbstractLauncherUiTest {
}
} 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);
}
}
@@ -74,7 +74,7 @@ public abstract class AllApps extends LauncherInstrumentation.VisibleContainer {
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 @@ public abstract class AllApps extends LauncherInstrumentation.VisibleContainer {
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 @@ public abstract class AllApps extends LauncherInstrumentation.VisibleContainer {
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 @@ public abstract class AllApps extends LauncherInstrumentation.VisibleContainer {
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 @@ public abstract class AllApps extends LauncherInstrumentation.VisibleContainer {
++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 @@ public abstract class AllApps extends LauncherInstrumentation.VisibleContainer {
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 @@ public abstract class AllApps extends LauncherInstrumentation.VisibleContainer {
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();
}
}
@@ -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;
}
}
@@ -51,7 +51,7 @@ public abstract class AppIcon extends Launchable {
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 abstract class AppIcon extends Launchable {
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 @@ public abstract class AppIcon extends Launchable {
}
@Override
protected String getLongPressIndicator() {
return "popup_container";
protected void waitForLongPressConfirmation() {
mLauncher.waitForLauncherObject("popup_container");
}
@Override
@@ -41,8 +41,8 @@ public abstract class AppIconMenuItem extends Launchable {
}
@Override
protected String getLongPressIndicator() {
return "drop_target_bar";
protected void waitForLongPressConfirmation() {
mLauncher.waitForLauncherObject("drop_target_bar");
}
@Override
@@ -35,8 +35,14 @@ public class HomeAllApps extends AllApps {
return (AllAppsAppIcon) super.getAppIcon(appName);
}
@NonNull
@Override
protected HomeAppIcon createAppIcon(UiObject2 icon) {
return new AllAppsAppIcon(mLauncher, icon);
}
@Override
protected boolean hasSearchBox() {
return true;
}
}
@@ -51,8 +51,7 @@ public abstract class HomeAppIcon extends AppIcon implements FolderDragTarget, W
() -> {
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 @@ public abstract class HomeAppIcon extends AppIcon implements FolderDragTarget, W
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);
@@ -97,8 +97,7 @@ abstract class Launchable {
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 @@ abstract class Launchable {
downTime,
iconCenter,
dragStartCenter,
longPressIndicator,
expectLongClickEvents),
SPRING_LOADED_STATE_ORDINAL, "long-pressing and triggering drag start");
} else {
@@ -116,7 +114,6 @@ abstract class Launchable {
downTime,
iconCenter,
dragStartCenter,
longPressIndicator,
expectLongClickEvents);
}
@@ -124,22 +121,44 @@ abstract class Launchable {
return dragStartCenter;
}
/**
* 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 @@ abstract class Launchable {
}
protected abstract void addExpectedEventsForLongClick();
protected abstract String getLongPressIndicator();
}
@@ -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 @@ public final class LaunchedAppState extends Background {
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});
}
}
}
@@ -84,6 +84,7 @@ import java.util.Deque;
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 @@ public final class LauncherInstrumentation {
// 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 @@ public final class LauncherInstrumentation {
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 @@ public final class LauncherInstrumentation {
}
}
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 @@ public final class LauncherInstrumentation {
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 @@ public final class LauncherInstrumentation {
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 @@ public final class LauncherInstrumentation {
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 @@ public final class LauncherInstrumentation {
}
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 @@ public final class LauncherInstrumentation {
containerRect.height() - distance - bottomGestureMarginInContainer,
0,
bottomGestureMarginInContainer),
10,
true);
/* steps= */ 10,
/* slowDown= */ true);
}
void scrollLeftByDistance(UiObject2 container, int distance) {
@@ -1650,6 +1686,29 @@ public final class LauncherInstrumentation {
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);
@@ -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();
}
@@ -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("");
}
}
@@ -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;
}
}
@@ -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);
}
}
@@ -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;
}
}
@@ -39,8 +39,8 @@ public final class Widget extends Launchable implements WorkspaceDragSource {
}
@Override
protected String getLongPressIndicator() {
return "drop_target_bar";
protected void waitForLongPressConfirmation() {
mLauncher.waitForLauncherObject("drop_target_bar");
}
@Override
@@ -205,7 +205,6 @@ public final class Workspace extends Home {
mLauncher,
homeAppIcon,
new Point(targetX, mLauncher.getVisibleBounds(workspace).centerY()),
"popup_container",
false,
false,
() -> mLauncher.expectEvent(
@@ -244,11 +243,11 @@ public final class Workspace extends Home {
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 @@ public final class Workspace extends Home {
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 @@ public final class Workspace extends Home {
}
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 @@ public final class Workspace extends Home {
* (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();
@@ -41,7 +41,6 @@ interface WorkspaceDragSource {
? launchableCenter.x - width / 2
: launchableCenter.x + width / 2,
displaySize.y / 2),
launchable.getLongPressIndicator(),
startsActivity,
isWidgetShortcut,
launchable::addExpectedEventsForLongClick);