Merge "Add taskbar TAPL tests" into tm-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
6b7e469088
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+120
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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";
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user