Merge "Implement e2e test for desktop windowing" into main
This commit is contained in:
committed by
Android (Google) Code Review
commit
3307f87b82
@@ -19,7 +19,7 @@
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
|
||||
xmlns:launcher="http://schemas.android.com/apk/res-auto"
|
||||
android:id="@+id/task"
|
||||
android:id="@+id/task_view_single"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:clipChildren="false"
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
|
||||
xmlns:launcher="http://schemas.android.com/apk/res-auto"
|
||||
android:id="@+id/task"
|
||||
android:id="@+id/task_view_desktop"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:clipChildren="true"
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
|
||||
xmlns:launcher="http://schemas.android.com/apk/res-auto"
|
||||
android:id="@+id/task"
|
||||
android:id="@+id/task_view_grouped"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:clipChildren="false"
|
||||
|
||||
@@ -30,6 +30,8 @@ import android.view.ViewGroup
|
||||
import androidx.core.view.updateLayoutParams
|
||||
import com.android.launcher3.Flags.enableRefactorTaskThumbnail
|
||||
import com.android.launcher3.R
|
||||
import com.android.launcher3.testing.TestLogging
|
||||
import com.android.launcher3.testing.shared.TestProtocol
|
||||
import com.android.launcher3.util.RunnableList
|
||||
import com.android.launcher3.util.SplitConfigurationOptions
|
||||
import com.android.launcher3.util.TransformingTouchDelegate
|
||||
@@ -213,7 +215,15 @@ class DesktopTaskView @JvmOverloads constructor(context: Context, attrs: Attribu
|
||||
}
|
||||
|
||||
override fun needsUpdate(dataChange: Int, flag: Int) =
|
||||
if (flag == FLAG_UPDATE_THUMBNAIL) super.needsUpdate(dataChange, flag) else false
|
||||
if (flag == FLAG_UPDATE_CORNER_RADIUS) false else super.needsUpdate(dataChange, flag)
|
||||
|
||||
override fun onIconLoaded(taskContainer: TaskContainer) {
|
||||
// Update contentDescription of snapshotView only, individual task icon is unused.
|
||||
taskContainer.snapshotView.contentDescription = taskContainer.task.titleDescription
|
||||
}
|
||||
|
||||
// Ignoring [onIconUnloaded] as all tasks shares the same Desktop icon
|
||||
override fun onIconUnloaded(taskContainer: TaskContainer) {}
|
||||
|
||||
// thumbnailView is laid out differently and is handled in onMeasure
|
||||
override fun updateThumbnailSize() {}
|
||||
@@ -228,6 +238,11 @@ class DesktopTaskView @JvmOverloads constructor(context: Context, attrs: Attribu
|
||||
|
||||
override fun launchTaskAnimated(): RunnableList? {
|
||||
val recentsView = recentsView ?: return null
|
||||
TestLogging.recordEvent(
|
||||
TestProtocol.SEQUENCE_MAIN,
|
||||
"launchDesktopFromRecents",
|
||||
taskIds.contentToString()
|
||||
)
|
||||
val endCallback = RunnableList()
|
||||
val desktopController = recentsView.desktopRecentsController
|
||||
checkNotNull(desktopController) { "recentsController is null" }
|
||||
|
||||
@@ -901,18 +901,11 @@ constructor(
|
||||
it.task.icon = icon
|
||||
it.task.titleDescription = contentDescription
|
||||
it.task.title = title
|
||||
setIcon(it.iconView, icon)
|
||||
if (enableOverviewIconMenu()) {
|
||||
setText(it.iconView, title)
|
||||
}
|
||||
it.digitalWellBeingToast?.initialize(it.task)
|
||||
onIconLoaded(it)
|
||||
}
|
||||
?.also { request -> pendingIconLoadRequests.add(request) }
|
||||
} else {
|
||||
setIcon(it.iconView, null)
|
||||
if (enableOverviewIconMenu()) {
|
||||
setText(it.iconView, null)
|
||||
}
|
||||
onIconUnloaded(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -931,6 +924,21 @@ constructor(
|
||||
pendingIconLoadRequests.clear()
|
||||
}
|
||||
|
||||
protected open fun onIconLoaded(taskContainer: TaskContainer) {
|
||||
setIcon(taskContainer.iconView, taskContainer.task.icon)
|
||||
if (enableOverviewIconMenu()) {
|
||||
setText(taskContainer.iconView, taskContainer.task.title)
|
||||
}
|
||||
taskContainer.digitalWellBeingToast?.initialize(taskContainer.task)
|
||||
}
|
||||
|
||||
protected open fun onIconUnloaded(taskContainer: TaskContainer) {
|
||||
setIcon(taskContainer.iconView, null)
|
||||
if (enableOverviewIconMenu()) {
|
||||
setText(taskContainer.iconView, null)
|
||||
}
|
||||
}
|
||||
|
||||
protected fun setIcon(iconView: TaskViewIcon, icon: Drawable?) {
|
||||
with(iconView) {
|
||||
if (icon != null) {
|
||||
@@ -1152,6 +1160,11 @@ constructor(
|
||||
isClickableAsLiveTile = true
|
||||
return runnableList
|
||||
}
|
||||
TestLogging.recordEvent(
|
||||
TestProtocol.SEQUENCE_MAIN,
|
||||
"composeRecentsLaunchAnimator",
|
||||
taskIds.contentToString()
|
||||
)
|
||||
val runnableList = RunnableList()
|
||||
with(AnimatorSet()) {
|
||||
TaskViewUtils.composeRecentsLaunchAnimator(
|
||||
|
||||
@@ -0,0 +1,105 @@
|
||||
/*
|
||||
* Copyright (C) 2024 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 android.platform.test.rule.AllowedDevices
|
||||
import android.platform.test.rule.DeviceProduct
|
||||
import android.platform.test.rule.IgnoreLimit
|
||||
import androidx.test.uiautomator.By
|
||||
import androidx.test.uiautomator.Until
|
||||
import com.android.launcher3.BuildConfig
|
||||
import com.android.launcher3.ui.AbstractLauncherUiTest
|
||||
import com.android.launcher3.ui.PortraitLandscapeRunner.PortraitLandscape
|
||||
import com.android.launcher3.uioverrides.QuickstepLauncher
|
||||
import com.google.common.truth.Truth.assertWithMessage
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
|
||||
/** Test Desktop windowing in Overview. */
|
||||
@AllowedDevices(allowed = [DeviceProduct.CF_TABLET, DeviceProduct.TANGORPRO])
|
||||
@IgnoreLimit(ignoreLimit = BuildConfig.IS_STUDIO_BUILD)
|
||||
class TaplTestsOverviewDesktop : AbstractLauncherUiTest<QuickstepLauncher?>() {
|
||||
@Before
|
||||
fun setup() {
|
||||
val overview = mLauncher.goHome().switchToOverview()
|
||||
if (overview.hasTasks()) {
|
||||
overview.dismissAllTasks()
|
||||
}
|
||||
startTestAppsWithCheck()
|
||||
mLauncher.goHome()
|
||||
}
|
||||
|
||||
@Test
|
||||
@PortraitLandscape
|
||||
fun enterDesktopViaOverviewMenu() {
|
||||
// Move last launched TEST_ACTIVITY_2 into Desktop
|
||||
mLauncher.workspace
|
||||
.switchToOverview()
|
||||
.getTestActivityTask(TEST_ACTIVITY_2)
|
||||
.tapMenu()
|
||||
.tapDesktopMenuItem()
|
||||
assertTestAppLaunched(TEST_ACTIVITY_2)
|
||||
|
||||
// Scroll back to TEST_ACTIVITY_1, then move it into Desktop
|
||||
mLauncher
|
||||
.goHome()
|
||||
.switchToOverview()
|
||||
.apply { flingForward() }
|
||||
.getTestActivityTask(TEST_ACTIVITY_1)
|
||||
.tapMenu()
|
||||
.tapDesktopMenuItem()
|
||||
TEST_ACTIVITIES.forEach { assertTestAppLaunched(it) }
|
||||
|
||||
// Launch static DesktopTaskView
|
||||
val desktop =
|
||||
mLauncher.goHome().switchToOverview().getTestActivityTask(TEST_ACTIVITIES).open()
|
||||
TEST_ACTIVITIES.forEach { assertTestAppLaunched(it) }
|
||||
|
||||
// Launch live-tile DesktopTaskView
|
||||
desktop.switchToOverview().getTestActivityTask(TEST_ACTIVITIES).open()
|
||||
TEST_ACTIVITIES.forEach { assertTestAppLaunched(it) }
|
||||
}
|
||||
|
||||
private fun startTestAppsWithCheck() {
|
||||
TEST_ACTIVITIES.forEach {
|
||||
startTestActivity(it)
|
||||
executeOnLauncher { launcher ->
|
||||
assertWithMessage(
|
||||
"Launcher activity is the top activity; expecting TestActivity$it"
|
||||
)
|
||||
.that(isInLaunchedApp(launcher))
|
||||
.isTrue()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun assertTestAppLaunched(index: Int) {
|
||||
assertWithMessage("TestActivity$index not opened in Desktop")
|
||||
.that(
|
||||
mDevice.wait(
|
||||
Until.hasObject(By.pkg(getAppPackageName()).text("TestActivity$index")),
|
||||
DEFAULT_UI_TIMEOUT
|
||||
)
|
||||
)
|
||||
.isTrue()
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val TEST_ACTIVITY_1 = 2
|
||||
const val TEST_ACTIVITY_2 = 3
|
||||
val TEST_ACTIVITIES = listOf(TEST_ACTIVITY_1, TEST_ACTIVITY_2)
|
||||
}
|
||||
}
|
||||
@@ -43,6 +43,7 @@ import android.os.RemoteException;
|
||||
import android.os.UserHandle;
|
||||
import android.os.UserManager;
|
||||
import android.platform.test.flag.junit.SetFlagsRule;
|
||||
import android.platform.test.rule.LimitDevicesRule;
|
||||
import android.system.OsConstants;
|
||||
import android.util.Log;
|
||||
|
||||
@@ -222,6 +223,9 @@ public abstract class AbstractLauncherUiTest<LAUNCHER_TYPE extends Launcher> {
|
||||
@Rule
|
||||
public ExtendedLongPressTimeoutRule mLongPressTimeoutRule = new ExtendedLongPressTimeoutRule();
|
||||
|
||||
@Rule
|
||||
public LimitDevicesRule mlimitDevicesRule = new LimitDevicesRule();
|
||||
|
||||
public static void initialize(AbstractLauncherUiTest test) throws Exception {
|
||||
test.reinitializeLauncherData();
|
||||
test.mDevice.pressHome();
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
package com.android.launcher3.tapl;
|
||||
|
||||
import static com.android.launcher3.tapl.BaseOverview.TASK_RES_ID;
|
||||
import static com.android.launcher3.tapl.BaseOverview.TASK_SELECTOR;
|
||||
import static com.android.launcher3.tapl.OverviewTask.TASK_START_EVENT;
|
||||
import static com.android.launcher3.testing.shared.TestProtocol.OVERVIEW_STATE_ORDINAL;
|
||||
|
||||
@@ -117,10 +117,10 @@ public abstract class Background extends LauncherInstrumentation.VisibleContaine
|
||||
// non-tablet overview, snapshots can be on either side of the swiped
|
||||
// task, but we still check that they become visible after swiping and
|
||||
// pausing.
|
||||
mLauncher.waitForOverviewObject(TASK_RES_ID);
|
||||
mLauncher.waitForObjectBySelector(TASK_SELECTOR);
|
||||
if (mLauncher.isTablet()) {
|
||||
List<UiObject2> tasks = mLauncher.getDevice().findObjects(
|
||||
mLauncher.getOverviewObjectSelector(TASK_RES_ID));
|
||||
TASK_SELECTOR);
|
||||
final int centerX = mLauncher.getDevice().getDisplayWidth() / 2;
|
||||
mLauncher.assertTrue(
|
||||
"All tasks not to the left of the swiped task",
|
||||
|
||||
@@ -19,7 +19,9 @@ package com.android.launcher3.tapl;
|
||||
import static android.view.KeyEvent.KEYCODE_ESCAPE;
|
||||
|
||||
import static com.android.launcher3.tapl.LauncherInstrumentation.TASKBAR_RES_ID;
|
||||
import static com.android.launcher3.tapl.LauncherInstrumentation.log;
|
||||
import static com.android.launcher3.tapl.OverviewTask.TASK_START_EVENT;
|
||||
import static com.android.launcher3.tapl.TestHelpers.getOverviewPackageName;
|
||||
import static com.android.launcher3.testing.shared.TestProtocol.NORMAL_STATE_ORDINAL;
|
||||
|
||||
import android.graphics.Rect;
|
||||
@@ -35,9 +37,11 @@ import androidx.test.uiautomator.UiObject2;
|
||||
|
||||
import com.android.launcher3.testing.shared.TestProtocol;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@@ -46,7 +50,9 @@ import java.util.stream.Collectors;
|
||||
*/
|
||||
public class BaseOverview extends LauncherInstrumentation.VisibleContainer {
|
||||
private static final String TAG = "BaseOverview";
|
||||
protected static final String TASK_RES_ID = "task";
|
||||
protected static final BySelector TASK_SELECTOR = By.res(Pattern.compile(
|
||||
getOverviewPackageName()
|
||||
+ ":id/(task_view_single|task_view_grouped|task_view_desktop)"));
|
||||
private static final Pattern EVENT_ALT_ESC_UP = Pattern.compile(
|
||||
"Key event: KeyEvent.*?action=ACTION_UP.*?keyCode=KEYCODE_ESCAPE.*?metaState=0");
|
||||
private static final Pattern EVENT_ENTER_DOWN = Pattern.compile(
|
||||
@@ -56,10 +62,22 @@ public class BaseOverview extends LauncherInstrumentation.VisibleContainer {
|
||||
|
||||
private static final int FLINGS_FOR_DISMISS_LIMIT = 40;
|
||||
|
||||
private final @Nullable UiObject2 mLiveTileTask;
|
||||
|
||||
|
||||
BaseOverview(LauncherInstrumentation launcher) {
|
||||
this(launcher, /*launchedFromApp=*/false);
|
||||
}
|
||||
|
||||
BaseOverview(LauncherInstrumentation launcher, boolean launchedFromApp) {
|
||||
super(launcher);
|
||||
verifyActiveContainer();
|
||||
verifyActionsViewVisibility();
|
||||
if (launchedFromApp) {
|
||||
mLiveTileTask = getCurrentTaskUnchecked();
|
||||
} else {
|
||||
mLiveTileTask = null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -79,7 +97,7 @@ public class BaseOverview extends LauncherInstrumentation.VisibleContainer {
|
||||
private void flingForwardImpl() {
|
||||
try (LauncherInstrumentation.Closable c =
|
||||
mLauncher.addContextLayer("want to fling forward in overview")) {
|
||||
LauncherInstrumentation.log("Overview.flingForward before fling");
|
||||
log("Overview.flingForward before fling");
|
||||
final UiObject2 overview = verifyActiveContainer();
|
||||
final int leftMargin =
|
||||
mLauncher.getTargetInsets().left + mLauncher.getEdgeSensitivityWidth();
|
||||
@@ -105,7 +123,7 @@ public class BaseOverview extends LauncherInstrumentation.VisibleContainer {
|
||||
private void flingBackwardImpl() {
|
||||
try (LauncherInstrumentation.Closable c =
|
||||
mLauncher.addContextLayer("want to fling backward in overview")) {
|
||||
LauncherInstrumentation.log("Overview.flingBackward before fling");
|
||||
log("Overview.flingBackward before fling");
|
||||
final UiObject2 overview = verifyActiveContainer();
|
||||
final int rightMargin =
|
||||
mLauncher.getTargetInsets().right + mLauncher.getEdgeSensitivityWidth();
|
||||
@@ -276,37 +294,56 @@ public class BaseOverview extends LauncherInstrumentation.VisibleContainer {
|
||||
*/
|
||||
@NonNull
|
||||
public OverviewTask getCurrentTask() {
|
||||
UiObject2 currentTask = getCurrentTaskUnchecked();
|
||||
mLauncher.assertNotNull("Unable to find a task", currentTask);
|
||||
return new OverviewTask(mLauncher, currentTask, this);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private UiObject2 getCurrentTaskUnchecked() {
|
||||
final List<UiObject2> taskViews = getTasks();
|
||||
mLauncher.assertNotEquals("Unable to find a task", 0, taskViews.size());
|
||||
if (taskViews.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// The widest, and most top-right task should be the current task
|
||||
UiObject2 currentTask = Collections.max(taskViews,
|
||||
return Collections.max(taskViews,
|
||||
Comparator.comparingInt((UiObject2 t) -> t.getVisibleBounds().width())
|
||||
.thenComparingInt((UiObject2 t) -> t.getVisibleCenter().x)
|
||||
.thenComparing(Comparator.comparing(
|
||||
(UiObject2 t) -> t.getVisibleCenter().y).reversed()));
|
||||
return new OverviewTask(mLauncher, currentTask, this);
|
||||
}
|
||||
|
||||
/** Returns an overview task matching TestActivity {@param activityNumber}. */
|
||||
/**
|
||||
* Returns an overview task that contains the specified test activity in its thumbnails.
|
||||
*
|
||||
* @param activityIndex index of TestActivity to match against
|
||||
*/
|
||||
@NonNull
|
||||
public OverviewTask getTestActivityTask(int activityNumber) {
|
||||
public OverviewTask getTestActivityTask(int activityIndex) {
|
||||
return getTestActivityTask(Collections.singleton(activityIndex));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an overview task that contains all the specified test activities in its thumbnails.
|
||||
*
|
||||
* @param activityNumbers collection of indices of TestActivity to match against
|
||||
*/
|
||||
@NonNull
|
||||
public OverviewTask getTestActivityTask(Collection<Integer> activityNumbers) {
|
||||
final List<UiObject2> taskViews = getTasks();
|
||||
mLauncher.assertNotEquals("Unable to find a task", 0, taskViews.size());
|
||||
|
||||
final String activityName = "TestActivity" + activityNumber;
|
||||
UiObject2 task = null;
|
||||
for (UiObject2 taskView : taskViews) {
|
||||
// TODO(b/239452415): Use equals instead of descEndsWith
|
||||
if (taskView.getParent().hasObject(By.descEndsWith(activityName))) {
|
||||
task = taskView;
|
||||
break;
|
||||
}
|
||||
}
|
||||
mLauncher.assertNotNull(
|
||||
"Unable to find a task with " + activityName + " from the task list", task);
|
||||
Optional<UiObject2> task = taskViews.stream().filter(
|
||||
taskView -> activityNumbers.stream().allMatch(activityNumber ->
|
||||
// TODO(b/239452415): Use equals instead of descEndsWith
|
||||
taskView.hasObject(By.descEndsWith("TestActivity" + activityNumber))
|
||||
)).findFirst();
|
||||
|
||||
return new OverviewTask(mLauncher, task, this);
|
||||
mLauncher.assertTrue("Unable to find a task with test activities " + activityNumbers
|
||||
+ " from the task list", task.isPresent());
|
||||
|
||||
return new OverviewTask(mLauncher, task.get(), this);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -328,8 +365,7 @@ public class BaseOverview extends LauncherInstrumentation.VisibleContainer {
|
||||
try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
|
||||
"want to get overview tasks")) {
|
||||
verifyActiveContainer();
|
||||
return mLauncher.getDevice().findObjects(
|
||||
mLauncher.getOverviewObjectSelector(TASK_RES_ID));
|
||||
return mLauncher.getDevice().findObjects(TASK_SELECTOR);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -506,4 +542,10 @@ public class BaseOverview extends LauncherInstrumentation.VisibleContainer {
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
protected boolean isLiveTile(UiObject2 task) {
|
||||
// UiObject2.equals returns false even when mLiveTileTask and task have the same node, hence
|
||||
// compare only hashCode as a workaround.
|
||||
return mLiveTileTask != null && mLiveTileTask.hashCode() == task.hashCode();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,6 +33,7 @@ import android.view.InputDevice;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.ViewConfiguration;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.test.uiautomator.Condition;
|
||||
import androidx.test.uiautomator.UiDevice;
|
||||
|
||||
@@ -75,6 +76,20 @@ public final class LaunchedAppState extends Background {
|
||||
return false;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public BaseOverview switchToOverview() {
|
||||
try (LauncherInstrumentation.Closable ignored = mLauncher.eventsCheck();
|
||||
LauncherInstrumentation.Closable ignored1 = mLauncher.addContextLayer(
|
||||
"want to switch from background to overview")) {
|
||||
verifyActiveContainer();
|
||||
goToOverviewUnchecked();
|
||||
return mLauncher.is3PLauncher()
|
||||
? new BaseOverview(mLauncher, /*launchedFromApp=*/true)
|
||||
: new Overview(mLauncher, /*launchedFromApp=*/true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the taskbar.
|
||||
*
|
||||
|
||||
@@ -1585,7 +1585,7 @@ public final class LauncherInstrumentation {
|
||||
return objects;
|
||||
}
|
||||
|
||||
private UiObject2 waitForObjectBySelector(BySelector selector) {
|
||||
UiObject2 waitForObjectBySelector(BySelector selector) {
|
||||
Log.d(TEST_DRAG_APP_ICON_TO_MULTIPLE_WORKSPACES_FAILURE,
|
||||
"LauncherInstrumentation.waitForObjectBySelector");
|
||||
final UiObject2 object = mDevice.wait(Until.findObject(selector), WAIT_TIME_MS);
|
||||
|
||||
@@ -22,9 +22,12 @@ import com.android.launcher3.tapl.LauncherInstrumentation.ContainerType;
|
||||
* Overview pane.
|
||||
*/
|
||||
public class Overview extends BaseOverview {
|
||||
|
||||
Overview(LauncherInstrumentation launcher) {
|
||||
super(launcher);
|
||||
this(launcher, /*launchedFromApp=*/false);
|
||||
}
|
||||
|
||||
Overview(LauncherInstrumentation launcher, boolean launchedFromApp) {
|
||||
super(launcher, launchedFromApp);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -40,16 +40,23 @@ import java.util.stream.Collectors;
|
||||
public final class OverviewTask {
|
||||
private static final String SYSTEMUI_PACKAGE = "com.android.systemui";
|
||||
static final Pattern TASK_START_EVENT = Pattern.compile("startActivityFromRecentsAsync");
|
||||
static final Pattern TASK_START_EVENT_DESKTOP = Pattern.compile("launchDesktopFromRecents");
|
||||
static final Pattern TASK_START_EVENT_LIVE_TILE = Pattern.compile(
|
||||
"composeRecentsLaunchAnimator");
|
||||
static final Pattern SPLIT_SELECT_EVENT = Pattern.compile("enterSplitSelect");
|
||||
static final Pattern SPLIT_START_EVENT = Pattern.compile("launchSplitTasks");
|
||||
private final LauncherInstrumentation mLauncher;
|
||||
@NonNull
|
||||
private final UiObject2 mTask;
|
||||
private final TaskViewType mType;
|
||||
private final BaseOverview mOverview;
|
||||
|
||||
OverviewTask(LauncherInstrumentation launcher, UiObject2 task, BaseOverview overview) {
|
||||
OverviewTask(LauncherInstrumentation launcher, @NonNull UiObject2 task, BaseOverview overview) {
|
||||
mLauncher = launcher;
|
||||
mLauncher.assertNotNull("task must not be null", task);
|
||||
mTask = task;
|
||||
mOverview = overview;
|
||||
mType = getType();
|
||||
verifyActiveContainer();
|
||||
}
|
||||
|
||||
@@ -220,7 +227,22 @@ public final class OverviewTask {
|
||||
return new LaunchedAppState(mLauncher);
|
||||
}
|
||||
} else {
|
||||
mLauncher.expectEvent(TestProtocol.SEQUENCE_MAIN, TASK_START_EVENT);
|
||||
final Pattern event;
|
||||
if (mOverview.isLiveTile(mTask)) {
|
||||
event = TASK_START_EVENT_LIVE_TILE;
|
||||
} else if (mType == TaskViewType.DESKTOP) {
|
||||
event = TASK_START_EVENT_DESKTOP;
|
||||
} else {
|
||||
event = TASK_START_EVENT;
|
||||
}
|
||||
mLauncher.expectEvent(TestProtocol.SEQUENCE_MAIN, event);
|
||||
|
||||
if (mType == TaskViewType.DESKTOP) {
|
||||
try (LauncherInstrumentation.Closable ignored = mLauncher.addContextLayer(
|
||||
"launched desktop")) {
|
||||
mLauncher.waitForSystemUiObject("desktop_mode_caption");
|
||||
}
|
||||
}
|
||||
return new LaunchedAppState(mLauncher);
|
||||
}
|
||||
}
|
||||
@@ -273,6 +295,17 @@ public final class OverviewTask {
|
||||
return actual.contains(expected);
|
||||
}
|
||||
|
||||
private TaskViewType getType() {
|
||||
String resourceName = mTask.getResourceName();
|
||||
if (resourceName.endsWith("task_view_grouped")) {
|
||||
return TaskViewType.GROUPED;
|
||||
} else if (resourceName.endsWith("task_view_desktop")) {
|
||||
return TaskViewType.DESKTOP;
|
||||
} else {
|
||||
return TaskViewType.SINGLE;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Enum used to specify which task is retrieved when it is a split task.
|
||||
*/
|
||||
@@ -292,4 +325,10 @@ public final class OverviewTask {
|
||||
this.iconAppRes = iconAppRes;
|
||||
}
|
||||
}
|
||||
|
||||
private enum TaskViewType {
|
||||
SINGLE,
|
||||
GROUPED,
|
||||
DESKTOP
|
||||
}
|
||||
}
|
||||
|
||||
@@ -97,20 +97,35 @@ public class OverviewTaskMenu {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Taps the Desktop item from the overview task menu and returns the LaunchedAppState
|
||||
* representing the Desktop.
|
||||
*/
|
||||
@NonNull
|
||||
public LaunchedAppState tapDesktopMenuItem() {
|
||||
try (LauncherInstrumentation.Closable ignored = mLauncher.eventsCheck();
|
||||
LauncherInstrumentation.Closable ignored1 = mLauncher.addContextLayer(
|
||||
"before tapping the desktop menu item")) {
|
||||
mLauncher.executeAndWaitForLauncherStop(
|
||||
() -> mLauncher.clickLauncherObject(
|
||||
mLauncher.findObjectInContainer(mMenu, By.text("Desktop"))),
|
||||
"tapped desktop menu item");
|
||||
|
||||
try (LauncherInstrumentation.Closable ignored2 = mLauncher.addContextLayer(
|
||||
"tapped desktop menu item")) {
|
||||
mLauncher.waitUntilSystemLauncherObjectGone("overview_panel");
|
||||
mLauncher.waitForSystemUiObject("desktop_mode_caption");
|
||||
return new LaunchedAppState(mLauncher);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Returns true if an item matching the given string is present in the menu. */
|
||||
public boolean hasMenuItem(String expectedMenuItemText) {
|
||||
UiObject2 menuItem = mLauncher.findObjectInContainer(mMenu, By.text(expectedMenuItemText));
|
||||
return menuItem != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the menu item specified by name if present.
|
||||
*/
|
||||
public OverviewTaskMenuItem getMenuItemByName(String menuItemName) {
|
||||
return new OverviewTaskMenuItem(mLauncher,
|
||||
mLauncher.waitForObjectInContainer(mMenu, By.text(menuItemName)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Taps outside task menu to dismiss it.
|
||||
*/
|
||||
|
||||
@@ -1,39 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2023 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 android.graphics.Rect;
|
||||
|
||||
import androidx.test.uiautomator.UiObject2;
|
||||
|
||||
/** Represents an item in the overview task menu. */
|
||||
public class OverviewTaskMenuItem {
|
||||
|
||||
private final LauncherInstrumentation mLauncher;
|
||||
private final UiObject2 mMenuItem;
|
||||
|
||||
OverviewTaskMenuItem(LauncherInstrumentation launcher, UiObject2 menuItem) {
|
||||
mLauncher = launcher;
|
||||
mMenuItem = menuItem;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns this menu item's visible bounds.
|
||||
*/
|
||||
public Rect getVisibleBounds() {
|
||||
return mMenuItem.getVisibleBounds();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user