change package name to uiuios
This commit is contained in:
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright (C) 2019 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.uiuios.tapl;
|
||||
|
||||
import androidx.test.uiautomator.By;
|
||||
import androidx.test.uiautomator.UiObject2;
|
||||
|
||||
public class AddToHomeScreenPrompt {
|
||||
private final LauncherInstrumentation mLauncher;
|
||||
private final UiObject2 mWidgetCell;
|
||||
|
||||
AddToHomeScreenPrompt(LauncherInstrumentation launcher) {
|
||||
mLauncher = launcher;
|
||||
mWidgetCell = launcher.waitForLauncherObject(By.clazz(
|
||||
"com.android.uiuios.widget.WidgetCell"));
|
||||
mLauncher.assertNotNull("Can't find widget cell object", mWidgetCell);
|
||||
}
|
||||
|
||||
public void addAutomatically() {
|
||||
mLauncher.waitForObjectInContainer(
|
||||
mWidgetCell.getParent().getParent().getParent().getParent(),
|
||||
By.text(LauncherInstrumentation.isAvd()
|
||||
? "ADD AUTOMATICALLY"
|
||||
: "Add automatically")).
|
||||
click();
|
||||
}
|
||||
}
|
||||
214
tests/tapl/com/android/uiuios/tapl/AllApps.java
Normal file
214
tests/tapl/com/android/uiuios/tapl/AllApps.java
Normal file
@@ -0,0 +1,214 @@
|
||||
/*
|
||||
* Copyright (C) 2018 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.uiuios.tapl;
|
||||
|
||||
import android.graphics.Point;
|
||||
import android.graphics.Rect;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.test.uiautomator.By;
|
||||
import androidx.test.uiautomator.BySelector;
|
||||
import androidx.test.uiautomator.Direction;
|
||||
import androidx.test.uiautomator.UiObject2;
|
||||
|
||||
import com.android.uiuios.ResourceUtils;
|
||||
import com.android.uiuios.testing.TestProtocol;
|
||||
|
||||
/**
|
||||
* Operations on AllApps opened from Home. Also a parent for All Apps opened from Overview.
|
||||
*/
|
||||
public class AllApps extends LauncherInstrumentation.VisibleContainer {
|
||||
private static final int MAX_SCROLL_ATTEMPTS = 40;
|
||||
|
||||
private final int mHeight;
|
||||
|
||||
AllApps(LauncherInstrumentation launcher) {
|
||||
super(launcher);
|
||||
final UiObject2 allAppsContainer = verifyActiveContainer();
|
||||
mHeight = allAppsContainer.getVisibleBounds().height();
|
||||
final UiObject2 appListRecycler = mLauncher.waitForObjectInContainer(allAppsContainer,
|
||||
"apps_list_view");
|
||||
// Wait for the recycler to populate.
|
||||
mLauncher.waitForObjectInContainer(appListRecycler, By.clazz(TextView.class));
|
||||
verifyNotFrozen("All apps freeze flags upon opening all apps");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected LauncherInstrumentation.ContainerType getContainerType() {
|
||||
return LauncherInstrumentation.ContainerType.ALL_APPS;
|
||||
}
|
||||
|
||||
private boolean hasClickableIcon(
|
||||
UiObject2 allAppsContainer, UiObject2 appListRecycler, BySelector appIconSelector) {
|
||||
final UiObject2 icon = appListRecycler.findObject(appIconSelector);
|
||||
if (icon == null) {
|
||||
LauncherInstrumentation.log("hasClickableIcon: icon not visible");
|
||||
return false;
|
||||
}
|
||||
final Rect iconBounds = icon.getVisibleBounds();
|
||||
LauncherInstrumentation.log("hasClickableIcon: icon bounds: " + iconBounds);
|
||||
if (iconCenterInSearchBox(allAppsContainer, icon)) {
|
||||
LauncherInstrumentation.log("hasClickableIcon: icon center is under search box");
|
||||
return false;
|
||||
}
|
||||
LauncherInstrumentation.log("hasClickableIcon: icon is clickable");
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean iconCenterInSearchBox(UiObject2 allAppsContainer, UiObject2 icon) {
|
||||
final Point iconCenter = icon.getVisibleCenter();
|
||||
return getSearchBox(allAppsContainer).getVisibleBounds().contains(
|
||||
iconCenter.x, iconCenter.y);
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds an icon. Fails if the icon doesn't exist. Scrolls the app list when needed to make
|
||||
* sure the icon is visible.
|
||||
*
|
||||
* @param appName name of the app.
|
||||
* @return The app.
|
||||
*/
|
||||
@NonNull
|
||||
public AppIcon getAppIcon(String appName) {
|
||||
try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
|
||||
"getting app icon " + appName + " on all apps")) {
|
||||
final UiObject2 allAppsContainer = verifyActiveContainer();
|
||||
final UiObject2 appListRecycler = mLauncher.waitForObjectInContainer(allAppsContainer,
|
||||
"apps_list_view");
|
||||
allAppsContainer.setGestureMargins(
|
||||
0,
|
||||
getSearchBox(allAppsContainer).getVisibleBounds().bottom + 1,
|
||||
0,
|
||||
ResourceUtils.getNavbarSize(ResourceUtils.NAVBAR_BOTTOM_GESTURE_SIZE,
|
||||
mLauncher.getResources()) + 1);
|
||||
final BySelector appIconSelector = AppIcon.getAppIconSelector(appName, mLauncher);
|
||||
if (!hasClickableIcon(allAppsContainer, appListRecycler, appIconSelector)) {
|
||||
scrollBackToBeginning();
|
||||
int attempts = 0;
|
||||
int scroll = getScroll(allAppsContainer);
|
||||
try (LauncherInstrumentation.Closable c1 = mLauncher.addContextLayer("scrolled")) {
|
||||
while (!hasClickableIcon(allAppsContainer, appListRecycler, appIconSelector)) {
|
||||
mLauncher.scroll(allAppsContainer, Direction.DOWN, 0.8f, null, 50);
|
||||
final int newScroll = getScroll(allAppsContainer);
|
||||
if (newScroll == scroll) break;
|
||||
|
||||
mLauncher.assertTrue(
|
||||
"Exceeded max scroll attempts: " + MAX_SCROLL_ATTEMPTS,
|
||||
++attempts <= MAX_SCROLL_ATTEMPTS);
|
||||
verifyActiveContainer();
|
||||
scroll = newScroll;
|
||||
}
|
||||
}
|
||||
verifyActiveContainer();
|
||||
}
|
||||
|
||||
mLauncher.assertTrue("Unable to scroll to a clickable icon: " + appName,
|
||||
hasClickableIcon(allAppsContainer, appListRecycler, appIconSelector));
|
||||
|
||||
final UiObject2 appIcon = mLauncher.getObjectInContainer(appListRecycler,
|
||||
appIconSelector);
|
||||
return new AppIcon(mLauncher, appIcon);
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
int attempts = 0;
|
||||
final Rect margins = new Rect(0, searchBox.getVisibleBounds().bottom + 1, 0, 5);
|
||||
|
||||
for (int scroll = getScroll(allAppsContainer);
|
||||
scroll != 0;
|
||||
scroll = getScroll(allAppsContainer)) {
|
||||
mLauncher.assertTrue("Negative scroll position", scroll > 0);
|
||||
|
||||
mLauncher.assertTrue(
|
||||
"Exceeded max scroll attempts: " + MAX_SCROLL_ATTEMPTS,
|
||||
++attempts <= MAX_SCROLL_ATTEMPTS);
|
||||
|
||||
mLauncher.scroll(allAppsContainer, Direction.UP, 1, margins, 50);
|
||||
}
|
||||
|
||||
try (LauncherInstrumentation.Closable c1 = mLauncher.addContextLayer("scrolled up")) {
|
||||
verifyActiveContainer();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private int getScroll(UiObject2 allAppsContainer) {
|
||||
return mLauncher.getAnswerFromLauncher(allAppsContainer, TestProtocol.GET_SCROLL_MESSAGE).
|
||||
getInt(TestProtocol.SCROLL_Y_FIELD, -1);
|
||||
}
|
||||
|
||||
private UiObject2 getSearchBox(UiObject2 allAppsContainer) {
|
||||
return mLauncher.waitForObjectInContainer(allAppsContainer, "search_container_all_apps");
|
||||
}
|
||||
|
||||
/**
|
||||
* Flings forward (down) and waits the fling's end.
|
||||
*/
|
||||
public void flingForward() {
|
||||
try (LauncherInstrumentation.Closable c =
|
||||
mLauncher.addContextLayer("want to fling forward in all apps")) {
|
||||
final UiObject2 allAppsContainer = verifyActiveContainer();
|
||||
// Start the gesture in the center to avoid starting at elements near the top.
|
||||
mLauncher.scroll(
|
||||
allAppsContainer, Direction.DOWN, 1, new Rect(0, 0, 0, mHeight / 2), 10);
|
||||
verifyActiveContainer();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Flings backward (up) and waits the fling's end.
|
||||
*/
|
||||
public void flingBackward() {
|
||||
try (LauncherInstrumentation.Closable c =
|
||||
mLauncher.addContextLayer("want to fling backward in all apps")) {
|
||||
final UiObject2 allAppsContainer = verifyActiveContainer();
|
||||
// Start the gesture in the center, for symmetry with forward.
|
||||
mLauncher.scroll(
|
||||
allAppsContainer, Direction.UP, 1, new Rect(0, mHeight / 2, 0, 0), 10);
|
||||
verifyActiveContainer();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Freezes updating app list upon app install/uninstall/update.
|
||||
*/
|
||||
public void freeze() {
|
||||
mLauncher.getTestInfo(TestProtocol.REQUEST_FREEZE_APP_LIST);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resumes updating app list upon app install/uninstall/update.
|
||||
*/
|
||||
public void unfreeze() {
|
||||
mLauncher.getTestInfo(TestProtocol.REQUEST_UNFREEZE_APP_LIST);
|
||||
verifyNotFrozen("All apps freeze flags upon unfreezing");
|
||||
}
|
||||
|
||||
private void verifyNotFrozen(String message) {
|
||||
mLauncher.assertEquals(message, 0, mLauncher.getTestInfo(
|
||||
TestProtocol.REQUEST_APP_LIST_FREEZE_FLAGS).
|
||||
getInt(TestProtocol.TEST_INFO_RESPONSE_FIELD));
|
||||
}
|
||||
}
|
||||
65
tests/tapl/com/android/uiuios/tapl/AllAppsFromOverview.java
Normal file
65
tests/tapl/com/android/uiuios/tapl/AllAppsFromOverview.java
Normal file
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* Copyright (C) 2018 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.uiuios.tapl;
|
||||
|
||||
import static com.android.uiuios.testing.TestProtocol.OVERVIEW_STATE_ORDINAL;
|
||||
|
||||
import android.graphics.Point;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.test.uiautomator.UiObject2;
|
||||
|
||||
import com.android.uiuios.testing.TestProtocol;
|
||||
|
||||
/**
|
||||
* Operations on AllApps opened from Overview.
|
||||
*/
|
||||
public final class AllAppsFromOverview extends AllApps {
|
||||
|
||||
AllAppsFromOverview(LauncherInstrumentation launcher) {
|
||||
super(launcher);
|
||||
verifyActiveContainer();
|
||||
}
|
||||
|
||||
/**
|
||||
* Swipes down to switch back to Overview whence we came from.
|
||||
*
|
||||
* @return the overview panel.
|
||||
*/
|
||||
@NonNull
|
||||
public Overview switchBackToOverview() {
|
||||
try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
|
||||
"want to switch back from all apps to overview")) {
|
||||
final UiObject2 allAppsContainer = verifyActiveContainer();
|
||||
// Swipe from the search box to the bottom.
|
||||
final UiObject2 qsb = mLauncher.waitForObjectInContainer(
|
||||
allAppsContainer, "search_container_all_apps");
|
||||
final Point start = qsb.getVisibleCenter();
|
||||
final int swipeHeight = mLauncher.getTestInfo(
|
||||
TestProtocol.REQUEST_ALL_APPS_TO_OVERVIEW_SWIPE_HEIGHT).
|
||||
getInt(TestProtocol.TEST_INFO_RESPONSE_FIELD);
|
||||
|
||||
final int endY = start.y + swipeHeight;
|
||||
LauncherInstrumentation.log("AllAppsFromOverview.switchBackToOverview before swipe");
|
||||
mLauncher.swipeToState(start.x, start.y, start.x, endY, 60, OVERVIEW_STATE_ORDINAL);
|
||||
|
||||
try (LauncherInstrumentation.Closable c1 = mLauncher.addContextLayer("swiped down")) {
|
||||
return new Overview(mLauncher);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
58
tests/tapl/com/android/uiuios/tapl/AppIcon.java
Normal file
58
tests/tapl/com/android/uiuios/tapl/AppIcon.java
Normal file
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* Copyright (C) 2018 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.uiuios.tapl;
|
||||
|
||||
import android.graphics.Point;
|
||||
import android.os.SystemClock;
|
||||
import android.view.MotionEvent;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.test.uiautomator.By;
|
||||
import androidx.test.uiautomator.BySelector;
|
||||
import androidx.test.uiautomator.UiObject2;
|
||||
|
||||
/**
|
||||
* App icon, whether in all apps or in workspace/
|
||||
*/
|
||||
public final class AppIcon extends Launchable {
|
||||
AppIcon(LauncherInstrumentation launcher, UiObject2 icon) {
|
||||
super(launcher, icon);
|
||||
}
|
||||
|
||||
static BySelector getAppIconSelector(String appName, LauncherInstrumentation launcher) {
|
||||
return By.clazz(TextView.class).text(appName).pkg(launcher.getLauncherPackageName());
|
||||
}
|
||||
|
||||
/**
|
||||
* Long-clicks the icon to open its menu.
|
||||
*/
|
||||
public AppIconMenu openMenu() {
|
||||
final Point iconCenter = mObject.getVisibleCenter();
|
||||
final long downTime = SystemClock.uptimeMillis();
|
||||
mLauncher.sendPointer(downTime, downTime, MotionEvent.ACTION_DOWN, iconCenter);
|
||||
final UiObject2 deepShortcutsContainer = mLauncher.waitForLauncherObject(
|
||||
"deep_shortcuts_container");
|
||||
mLauncher.sendPointer(
|
||||
downTime, SystemClock.uptimeMillis(), MotionEvent.ACTION_UP, iconCenter);
|
||||
return new AppIconMenu(mLauncher, deepShortcutsContainer);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getLongPressIndicator() {
|
||||
return "deep_shortcuts_container";
|
||||
}
|
||||
}
|
||||
47
tests/tapl/com/android/uiuios/tapl/AppIconMenu.java
Normal file
47
tests/tapl/com/android/uiuios/tapl/AppIconMenu.java
Normal file
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Copyright (C) 2018 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.uiuios.tapl;
|
||||
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import androidx.test.uiautomator.UiObject2;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Context menu of an app icon.
|
||||
*/
|
||||
public class AppIconMenu {
|
||||
private final LauncherInstrumentation mLauncher;
|
||||
private final UiObject2 mDeepShortcutsContainer;
|
||||
|
||||
AppIconMenu(LauncherInstrumentation launcher,
|
||||
UiObject2 deepShortcutsContainer) {
|
||||
mLauncher = launcher;
|
||||
mDeepShortcutsContainer = deepShortcutsContainer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a menu item with a given number. Fails if it doesn't exist.
|
||||
*/
|
||||
public AppIconMenuItem getMenuItem(int itemNumber) {
|
||||
final List<UiObject2> menuItems = mLauncher.getObjectsInContainer(mDeepShortcutsContainer,
|
||||
"bubble_text");
|
||||
assertTrue(menuItems.size() > itemNumber);
|
||||
return new AppIconMenuItem(mLauncher, menuItems.get(itemNumber));
|
||||
}
|
||||
}
|
||||
40
tests/tapl/com/android/uiuios/tapl/AppIconMenuItem.java
Normal file
40
tests/tapl/com/android/uiuios/tapl/AppIconMenuItem.java
Normal file
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Copyright (C) 2018 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.uiuios.tapl;
|
||||
|
||||
import androidx.test.uiautomator.UiObject2;
|
||||
|
||||
/**
|
||||
* Menu item in an app icon menu.
|
||||
*/
|
||||
public class AppIconMenuItem extends Launchable {
|
||||
AppIconMenuItem(LauncherInstrumentation launcher, UiObject2 shortcut) {
|
||||
super(launcher, shortcut);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the visible text of the menu item.
|
||||
*/
|
||||
public String getText() {
|
||||
return mObject.getText();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getLongPressIndicator() {
|
||||
return "drop_target_bar";
|
||||
}
|
||||
}
|
||||
129
tests/tapl/com/android/uiuios/tapl/Background.java
Normal file
129
tests/tapl/com/android/uiuios/tapl/Background.java
Normal file
@@ -0,0 +1,129 @@
|
||||
/*
|
||||
* Copyright (C) 2018 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.uiuios.tapl;
|
||||
|
||||
import static com.android.uiuios.testing.TestProtocol.BACKGROUND_APP_STATE_ORDINAL;
|
||||
|
||||
import android.graphics.Point;
|
||||
import android.os.SystemClock;
|
||||
import android.view.MotionEvent;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import com.android.uiuios.testing.TestProtocol;
|
||||
|
||||
/**
|
||||
* Indicates the base state with a UI other than Overview running as foreground. It can also
|
||||
* indicate Launcher as long as Launcher is not in Overview state.
|
||||
*/
|
||||
public class Background extends LauncherInstrumentation.VisibleContainer {
|
||||
private static final int ZERO_BUTTON_SWIPE_UP_GESTURE_DURATION = 500;
|
||||
|
||||
Background(LauncherInstrumentation launcher) {
|
||||
super(launcher);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected LauncherInstrumentation.ContainerType getContainerType() {
|
||||
return LauncherInstrumentation.ContainerType.BACKGROUND;
|
||||
}
|
||||
|
||||
/**
|
||||
* Swipes up or presses the square button to switch to Overview.
|
||||
* Returns the base overview, which can be either in Launcher or the fallback recents.
|
||||
*
|
||||
* @return the Overview panel object.
|
||||
*/
|
||||
@NonNull
|
||||
public BaseOverview switchToOverview() {
|
||||
try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
|
||||
"want to switch from background to overview")) {
|
||||
verifyActiveContainer();
|
||||
goToOverviewUnchecked(BACKGROUND_APP_STATE_ORDINAL);
|
||||
return new BaseOverview(mLauncher);
|
||||
}
|
||||
}
|
||||
|
||||
protected void goToOverviewUnchecked(int expectedState) {
|
||||
mLauncher.getTestInfo(TestProtocol.REQUEST_ENABLE_DEBUG_TRACING);
|
||||
switch (mLauncher.getNavigationModel()) {
|
||||
case ZERO_BUTTON: {
|
||||
final int centerX = mLauncher.getDevice().getDisplayWidth() / 2;
|
||||
final int startY = getSwipeStartY();
|
||||
final int swipeHeight = mLauncher.getTestInfo(getSwipeHeightRequestName()).
|
||||
getInt(TestProtocol.TEST_INFO_RESPONSE_FIELD);
|
||||
final Point start = new Point(centerX, startY);
|
||||
final Point end =
|
||||
new Point(centerX, startY - swipeHeight - mLauncher.getTouchSlop());
|
||||
|
||||
final long downTime = SystemClock.uptimeMillis();
|
||||
mLauncher.sendPointer(downTime, downTime, MotionEvent.ACTION_DOWN, start);
|
||||
mLauncher.executeAndWaitForEvent(
|
||||
() -> mLauncher.movePointer(
|
||||
downTime,
|
||||
downTime,
|
||||
ZERO_BUTTON_SWIPE_UP_GESTURE_DURATION,
|
||||
start,
|
||||
end),
|
||||
event -> TestProtocol.PAUSE_DETECTED_MESSAGE.equals(event.getClassName()),
|
||||
"Pause wasn't detected");
|
||||
mLauncher.sendPointer(
|
||||
downTime, SystemClock.uptimeMillis(), MotionEvent.ACTION_UP, end);
|
||||
break;
|
||||
}
|
||||
|
||||
case TWO_BUTTON: {
|
||||
final int startX;
|
||||
final int startY;
|
||||
final int endX;
|
||||
final int endY;
|
||||
final int swipeLength = mLauncher.getTestInfo(getSwipeHeightRequestName()).
|
||||
getInt(TestProtocol.TEST_INFO_RESPONSE_FIELD) + mLauncher.getTouchSlop();
|
||||
|
||||
if (mLauncher.getDevice().isNaturalOrientation()) {
|
||||
startX = endX = mLauncher.getDevice().getDisplayWidth() / 2;
|
||||
startY = getSwipeStartY();
|
||||
endY = startY - swipeLength;
|
||||
} else {
|
||||
startX = getSwipeStartX();
|
||||
endX = startX - swipeLength;
|
||||
startY = endY = mLauncher.getDevice().getDisplayHeight() / 2;
|
||||
}
|
||||
|
||||
mLauncher.swipeToState(startX, startY, endX, endY, 10, expectedState);
|
||||
break;
|
||||
}
|
||||
|
||||
case THREE_BUTTON:
|
||||
mLauncher.waitForSystemUiObject("recent_apps").click();
|
||||
break;
|
||||
}
|
||||
mLauncher.getTestInfo(TestProtocol.REQUEST_DISABLE_DEBUG_TRACING);
|
||||
}
|
||||
|
||||
protected String getSwipeHeightRequestName() {
|
||||
return TestProtocol.REQUEST_BACKGROUND_TO_OVERVIEW_SWIPE_HEIGHT;
|
||||
}
|
||||
|
||||
protected int getSwipeStartX() {
|
||||
return mLauncher.getRealDisplaySize().x - 1;
|
||||
}
|
||||
|
||||
protected int getSwipeStartY() {
|
||||
return mLauncher.getRealDisplaySize().y - 1;
|
||||
}
|
||||
}
|
||||
120
tests/tapl/com/android/uiuios/tapl/BaseOverview.java
Normal file
120
tests/tapl/com/android/uiuios/tapl/BaseOverview.java
Normal file
@@ -0,0 +1,120 @@
|
||||
/*
|
||||
* Copyright (C) 2018 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.uiuios.tapl;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.test.uiautomator.BySelector;
|
||||
import androidx.test.uiautomator.Direction;
|
||||
import androidx.test.uiautomator.UiObject2;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Common overview pane for both Launcher and fallback recents
|
||||
*/
|
||||
public class BaseOverview extends LauncherInstrumentation.VisibleContainer {
|
||||
private static final int FLING_SPEED = LauncherInstrumentation.isAvd() ? 500 : 1500;
|
||||
private static final int FLINGS_FOR_DISMISS_LIMIT = 40;
|
||||
|
||||
BaseOverview(LauncherInstrumentation launcher) {
|
||||
super(launcher);
|
||||
verifyActiveContainer();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected LauncherInstrumentation.ContainerType getContainerType() {
|
||||
return LauncherInstrumentation.ContainerType.BASE_OVERVIEW;
|
||||
}
|
||||
|
||||
/**
|
||||
* Flings forward (left) and waits the fling's end.
|
||||
*/
|
||||
public void flingForward() {
|
||||
try (LauncherInstrumentation.Closable c =
|
||||
mLauncher.addContextLayer("want to fling forward in overview")) {
|
||||
LauncherInstrumentation.log("Overview.flingForward before fling");
|
||||
final UiObject2 overview = verifyActiveContainer();
|
||||
overview.setGestureMargins(mLauncher.getEdgeSensitivityWidth(), 0, 0, 0);
|
||||
overview.fling(Direction.LEFT, (int) (FLING_SPEED * mLauncher.getDisplayDensity()));
|
||||
mLauncher.waitForIdle();
|
||||
verifyActiveContainer();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Dismissed all tasks by scrolling to Clear-all button and pressing it.
|
||||
*/
|
||||
public Workspace dismissAllTasks() {
|
||||
try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
|
||||
"dismissing all tasks")) {
|
||||
final BySelector clearAllSelector = mLauncher.getLauncherObjectSelector("clear_all");
|
||||
for (int i = 0;
|
||||
i < FLINGS_FOR_DISMISS_LIMIT
|
||||
&& !verifyActiveContainer().hasObject(clearAllSelector);
|
||||
++i) {
|
||||
flingForward();
|
||||
}
|
||||
|
||||
mLauncher.getObjectInContainer(verifyActiveContainer(), clearAllSelector).click();
|
||||
try (LauncherInstrumentation.Closable c1 = mLauncher.addContextLayer(
|
||||
"dismissed all tasks")) {
|
||||
return new Workspace(mLauncher);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Flings backward (right) and waits the fling's end.
|
||||
*/
|
||||
public void flingBackward() {
|
||||
try (LauncherInstrumentation.Closable c =
|
||||
mLauncher.addContextLayer("want to fling backward in overview")) {
|
||||
LauncherInstrumentation.log("Overview.flingBackward before fling");
|
||||
final UiObject2 overview = verifyActiveContainer();
|
||||
overview.setGestureMargins(0, 0, mLauncher.getEdgeSensitivityWidth(), 0);
|
||||
overview.fling(Direction.RIGHT, (int) (FLING_SPEED * mLauncher.getDisplayDensity()));
|
||||
mLauncher.waitForIdle();
|
||||
verifyActiveContainer();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the current task in the carousel, or fails if the carousel is empty.
|
||||
*
|
||||
* @return the task in the middle of the visible tasks list.
|
||||
*/
|
||||
@NonNull
|
||||
public OverviewTask getCurrentTask() {
|
||||
try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
|
||||
"want to get current task")) {
|
||||
verifyActiveContainer();
|
||||
final List<UiObject2> taskViews = mLauncher.getDevice().findObjects(
|
||||
mLauncher.getLauncherObjectSelector("snapshot"));
|
||||
mLauncher.assertNotEquals("Unable to find a task", 0, taskViews.size());
|
||||
|
||||
// taskViews contains up to 3 task views: the 'main' (having the widest visible
|
||||
// part) one in the center, and parts of its right and left siblings. Find the
|
||||
// main task view by its width.
|
||||
final UiObject2 widestTask = Collections.max(taskViews,
|
||||
(t1, t2) -> Integer.compare(t1.getVisibleBounds().width(),
|
||||
t2.getVisibleBounds().width()));
|
||||
|
||||
return new OverviewTask(mLauncher, widestTask, this);
|
||||
}
|
||||
}
|
||||
}
|
||||
35
tests/tapl/com/android/uiuios/tapl/Folder.java
Normal file
35
tests/tapl/com/android/uiuios/tapl/Folder.java
Normal file
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright (C) 2019 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.uiuios.tapl;
|
||||
|
||||
import android.widget.FrameLayout;
|
||||
|
||||
import androidx.test.uiautomator.By;
|
||||
import androidx.test.uiautomator.BySelector;
|
||||
import androidx.test.uiautomator.UiObject2;
|
||||
|
||||
/**
|
||||
* App folder in workspace/
|
||||
*/
|
||||
public final class Folder {
|
||||
Folder(LauncherInstrumentation launcher, UiObject2 icon) {
|
||||
}
|
||||
|
||||
static BySelector getSelector(String folderName, LauncherInstrumentation launcher) {
|
||||
return By.clazz(FrameLayout.class).desc(folderName).pkg(launcher.getLauncherPackageName());
|
||||
}
|
||||
}
|
||||
61
tests/tapl/com/android/uiuios/tapl/Home.java
Normal file
61
tests/tapl/com/android/uiuios/tapl/Home.java
Normal file
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* Copyright (C) 2018 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.uiuios.tapl;
|
||||
|
||||
import static com.android.uiuios.testing.TestProtocol.OVERVIEW_STATE_ORDINAL;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
/**
|
||||
* Operations on the home screen.
|
||||
*
|
||||
* Launcher can be invoked both when its activity is in the foreground and when it is in the
|
||||
* background. This class is a parent of the two classes {@link Background} and {@link Workspace}
|
||||
* that essentially represents these two activity states. Any gestures (e.g., switchToOverview) that
|
||||
* can be performed in both of these states can be defined here.
|
||||
*/
|
||||
public abstract class Home extends Background {
|
||||
|
||||
protected Home(LauncherInstrumentation launcher) {
|
||||
super(launcher);
|
||||
verifyActiveContainer();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected LauncherInstrumentation.ContainerType getContainerType() {
|
||||
return LauncherInstrumentation.ContainerType.WORKSPACE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Swipes up or presses the square button to switch to Overview.
|
||||
*
|
||||
* @return the Overview panel object.
|
||||
*/
|
||||
@NonNull
|
||||
@Override
|
||||
public Overview switchToOverview() {
|
||||
try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
|
||||
"want to switch from home to overview")) {
|
||||
verifyActiveContainer();
|
||||
goToOverviewUnchecked(OVERVIEW_STATE_ORDINAL);
|
||||
try (LauncherInstrumentation.Closable c1 = mLauncher.addContextLayer(
|
||||
"performed the switch action")) {
|
||||
return new Overview(mLauncher);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
90
tests/tapl/com/android/uiuios/tapl/Launchable.java
Normal file
90
tests/tapl/com/android/uiuios/tapl/Launchable.java
Normal file
@@ -0,0 +1,90 @@
|
||||
/*
|
||||
* Copyright (C) 2018 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.uiuios.tapl;
|
||||
|
||||
import android.graphics.Point;
|
||||
|
||||
import androidx.test.uiautomator.By;
|
||||
import androidx.test.uiautomator.BySelector;
|
||||
import androidx.test.uiautomator.UiObject2;
|
||||
import androidx.test.uiautomator.Until;
|
||||
|
||||
import com.android.uiuios.testing.TestProtocol;
|
||||
|
||||
/**
|
||||
* Ancestor for AppIcon and AppMenuItem.
|
||||
*/
|
||||
abstract class Launchable {
|
||||
private static final int WAIT_TIME_MS = 60000;
|
||||
protected final LauncherInstrumentation mLauncher;
|
||||
|
||||
protected final UiObject2 mObject;
|
||||
|
||||
Launchable(LauncherInstrumentation launcher, UiObject2 object) {
|
||||
mObject = object;
|
||||
mLauncher = launcher;
|
||||
}
|
||||
|
||||
UiObject2 getObject() {
|
||||
return mObject;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clicks the object to launch its app.
|
||||
*/
|
||||
public Background launch(String expectedPackageName) {
|
||||
return launch(By.pkg(expectedPackageName));
|
||||
}
|
||||
|
||||
private Background launch(BySelector selector) {
|
||||
LauncherInstrumentation.log("Launchable.launch before click " +
|
||||
mObject.getVisibleCenter() + " in " + mObject.getVisibleBounds());
|
||||
mLauncher.getTestInfo(TestProtocol.REQUEST_ENABLE_DEBUG_TRACING);
|
||||
mLauncher.assertTrue(
|
||||
"Launching an app didn't open a new window: " + mObject.getText(),
|
||||
mObject.clickAndWait(Until.newWindow(), WAIT_TIME_MS));
|
||||
mLauncher.getTestInfo(TestProtocol.REQUEST_DISABLE_DEBUG_TRACING);
|
||||
mLauncher.assertTrue(
|
||||
"App didn't start: " + selector,
|
||||
mLauncher.getDevice().wait(Until.hasObject(selector),
|
||||
LauncherInstrumentation.WAIT_TIME_MS));
|
||||
return new Background(mLauncher);
|
||||
}
|
||||
|
||||
/**
|
||||
* Drags an object to the center of homescreen.
|
||||
*/
|
||||
public Workspace dragToWorkspace() {
|
||||
final Point launchableCenter = getObject().getVisibleCenter();
|
||||
final Point displaySize = mLauncher.getRealDisplaySize();
|
||||
final int width = displaySize.x / 2;
|
||||
Workspace.dragIconToWorkspace(
|
||||
mLauncher,
|
||||
this,
|
||||
new Point(
|
||||
launchableCenter.x >= width ?
|
||||
launchableCenter.x - width / 2 : launchableCenter.x + width / 2,
|
||||
displaySize.y / 2),
|
||||
getLongPressIndicator());
|
||||
try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
|
||||
"dragged launchable to workspace")) {
|
||||
return new Workspace(mLauncher);
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract String getLongPressIndicator();
|
||||
}
|
||||
829
tests/tapl/com/android/uiuios/tapl/LauncherInstrumentation.java
Normal file
829
tests/tapl/com/android/uiuios/tapl/LauncherInstrumentation.java
Normal file
@@ -0,0 +1,829 @@
|
||||
/*
|
||||
* Copyright (C) 2018 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.uiuios.tapl;
|
||||
|
||||
import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_ENABLED;
|
||||
import static android.content.pm.PackageManager.DONT_KILL_APP;
|
||||
import static android.content.pm.PackageManager.MATCH_ALL;
|
||||
import static android.content.pm.PackageManager.MATCH_DISABLED_COMPONENTS;
|
||||
|
||||
import static com.android.uiuios.tapl.TestHelpers.getOverviewPackageName;
|
||||
import static com.android.uiuios.testing.TestProtocol.BACKGROUND_APP_STATE_ORDINAL;
|
||||
import static com.android.uiuios.testing.TestProtocol.NORMAL_STATE_ORDINAL;
|
||||
|
||||
import android.app.ActivityManager;
|
||||
import android.app.Instrumentation;
|
||||
import android.app.UiAutomation;
|
||||
import android.content.ComponentName;
|
||||
import android.content.ContentResolver;
|
||||
import android.content.Context;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.ProviderInfo;
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.Point;
|
||||
import android.graphics.Rect;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.os.Parcelable;
|
||||
import android.os.SystemClock;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
import android.view.InputDevice;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.Surface;
|
||||
import android.view.ViewConfiguration;
|
||||
import android.view.WindowManager;
|
||||
import android.view.accessibility.AccessibilityEvent;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.test.uiautomator.By;
|
||||
import androidx.test.uiautomator.BySelector;
|
||||
import androidx.test.uiautomator.Configurator;
|
||||
import androidx.test.uiautomator.Direction;
|
||||
import androidx.test.uiautomator.UiDevice;
|
||||
import androidx.test.uiautomator.UiObject2;
|
||||
import androidx.test.uiautomator.Until;
|
||||
|
||||
import com.android.uiuios.testing.TestProtocol;
|
||||
import com.android.systemui.shared.system.QuickStepContract;
|
||||
|
||||
import org.junit.Assert;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.Deque;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
|
||||
/**
|
||||
* The main tapl object. The only object that can be explicitly constructed by the using code. It
|
||||
* produces all other objects.
|
||||
*/
|
||||
public final class LauncherInstrumentation {
|
||||
|
||||
private static final String TAG = "Tapl";
|
||||
private static final int ZERO_BUTTON_STEPS_FROM_BACKGROUND_TO_HOME = 20;
|
||||
private static final int GESTURE_STEP_MS = 16;
|
||||
|
||||
// Types for launcher containers that the user is interacting with. "Background" is a
|
||||
// pseudo-container corresponding to inactive launcher covered by another app.
|
||||
enum ContainerType {
|
||||
WORKSPACE, ALL_APPS, OVERVIEW, WIDGETS, BACKGROUND, BASE_OVERVIEW
|
||||
}
|
||||
|
||||
public enum NavigationModel {ZERO_BUTTON, TWO_BUTTON, THREE_BUTTON}
|
||||
|
||||
// Base class for launcher containers.
|
||||
static abstract class VisibleContainer {
|
||||
protected final LauncherInstrumentation mLauncher;
|
||||
|
||||
protected VisibleContainer(LauncherInstrumentation launcher) {
|
||||
mLauncher = launcher;
|
||||
launcher.setActiveContainer(this);
|
||||
}
|
||||
|
||||
protected abstract ContainerType getContainerType();
|
||||
|
||||
/**
|
||||
* Asserts that the launcher is in the mode matching 'this' object.
|
||||
*
|
||||
* @return UI object for the container.
|
||||
*/
|
||||
final UiObject2 verifyActiveContainer() {
|
||||
mLauncher.assertTrue("Attempt to use a stale container",
|
||||
this == sActiveContainer.get());
|
||||
return mLauncher.verifyContainerType(getContainerType());
|
||||
}
|
||||
}
|
||||
|
||||
interface Closable extends AutoCloseable {
|
||||
void close();
|
||||
}
|
||||
|
||||
private static final String WORKSPACE_RES_ID = "workspace";
|
||||
private static final String APPS_RES_ID = "apps_view";
|
||||
private static final String OVERVIEW_RES_ID = "overview_panel";
|
||||
private static final String WIDGETS_RES_ID = "widgets_list_view";
|
||||
public static final int WAIT_TIME_MS = 60000;
|
||||
private static final String SYSTEMUI_PACKAGE = "com.android.systemui";
|
||||
|
||||
private static WeakReference<VisibleContainer> sActiveContainer = new WeakReference<>(null);
|
||||
|
||||
private final UiDevice mDevice;
|
||||
private final Instrumentation mInstrumentation;
|
||||
private int mExpectedRotation = Surface.ROTATION_0;
|
||||
private final Uri mTestProviderUri;
|
||||
private final Deque<String> mDiagnosticContext = new LinkedList<>();
|
||||
|
||||
/**
|
||||
* Constructs the root of TAPL hierarchy. You get all other objects from it.
|
||||
*/
|
||||
public LauncherInstrumentation(Instrumentation instrumentation) {
|
||||
mInstrumentation = instrumentation;
|
||||
mDevice = UiDevice.getInstance(instrumentation);
|
||||
|
||||
// Launcher should run in test harness so that custom accessibility protocol between
|
||||
// Launcher and TAPL is enabled. In-process tests enable this protocol with a direct call
|
||||
// into Launcher.
|
||||
assertTrue("Device must run in a test harness",
|
||||
TestHelpers.isInLauncherProcess() || ActivityManager.isRunningInTestHarness());
|
||||
|
||||
final String testPackage = getContext().getPackageName();
|
||||
final String targetPackage = mInstrumentation.getTargetContext().getPackageName();
|
||||
|
||||
// Launcher package. As during inproc tests the tested launcher may not be selected as the
|
||||
// current launcher, choosing target package for inproc. For out-of-proc, use the installed
|
||||
// launcher package.
|
||||
final String authorityPackage = testPackage.equals(targetPackage) ?
|
||||
getLauncherPackageName() :
|
||||
targetPackage;
|
||||
|
||||
String testProviderAuthority = authorityPackage + ".TestInfo";
|
||||
mTestProviderUri = new Uri.Builder()
|
||||
.scheme(ContentResolver.SCHEME_CONTENT)
|
||||
.authority(testProviderAuthority)
|
||||
.build();
|
||||
|
||||
try {
|
||||
mDevice.executeShellCommand("pm grant " + testPackage +
|
||||
" android.permission.WRITE_SECURE_SETTINGS");
|
||||
} catch (IOException e) {
|
||||
fail(e.toString());
|
||||
}
|
||||
|
||||
|
||||
PackageManager pm = getContext().getPackageManager();
|
||||
ProviderInfo pi = pm.resolveContentProvider(
|
||||
testProviderAuthority, MATCH_ALL | MATCH_DISABLED_COMPONENTS);
|
||||
ComponentName cn = new ComponentName(pi.packageName, pi.name);
|
||||
|
||||
if (pm.getComponentEnabledSetting(cn) != COMPONENT_ENABLED_STATE_ENABLED) {
|
||||
if (TestHelpers.isInLauncherProcess()) {
|
||||
getContext().getPackageManager().setComponentEnabledSetting(
|
||||
cn, COMPONENT_ENABLED_STATE_ENABLED, DONT_KILL_APP);
|
||||
} else {
|
||||
try {
|
||||
mDevice.executeShellCommand("pm enable " + cn.flattenToString());
|
||||
} catch (IOException e) {
|
||||
fail(e.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Context getContext() {
|
||||
return mInstrumentation.getContext();
|
||||
}
|
||||
|
||||
Bundle getTestInfo(String request) {
|
||||
return getContext().getContentResolver().call(mTestProviderUri, request, null, null);
|
||||
}
|
||||
|
||||
void setActiveContainer(VisibleContainer container) {
|
||||
sActiveContainer = new WeakReference<>(container);
|
||||
}
|
||||
|
||||
public NavigationModel getNavigationModel() {
|
||||
final Context baseContext = mInstrumentation.getTargetContext();
|
||||
try {
|
||||
// Workaround, use constructed context because both the instrumentation context and the
|
||||
// app context are not constructed with resources that take overlays into account
|
||||
final Context ctx = baseContext.createPackageContext("android", 0);
|
||||
for (int i = 0; i < 100; ++i) {
|
||||
final int currentInteractionMode = getCurrentInteractionMode(ctx);
|
||||
final NavigationModel model = getNavigationModel(currentInteractionMode);
|
||||
log("Interaction mode = " + currentInteractionMode + " (" + model + ")");
|
||||
if (model != null) return model;
|
||||
Thread.sleep(100);
|
||||
}
|
||||
fail("Can't detect navigation mode");
|
||||
} catch (Exception e) {
|
||||
fail(e.toString());
|
||||
}
|
||||
return NavigationModel.THREE_BUTTON;
|
||||
}
|
||||
|
||||
public static NavigationModel getNavigationModel(int currentInteractionMode) {
|
||||
if (QuickStepContract.isGesturalMode(currentInteractionMode)) {
|
||||
return NavigationModel.ZERO_BUTTON;
|
||||
} else if (QuickStepContract.isSwipeUpMode(currentInteractionMode)) {
|
||||
return NavigationModel.TWO_BUTTON;
|
||||
} else if (QuickStepContract.isLegacyMode(currentInteractionMode)) {
|
||||
return NavigationModel.THREE_BUTTON;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static boolean isAvd() {
|
||||
return Build.MODEL.contains("Cuttlefish");
|
||||
}
|
||||
|
||||
static void log(String message) {
|
||||
Log.d(TAG, message);
|
||||
}
|
||||
|
||||
Closable addContextLayer(String piece) {
|
||||
mDiagnosticContext.addLast(piece);
|
||||
log("Added context: " + getContextDescription());
|
||||
return () -> {
|
||||
log("Removing context: " + getContextDescription());
|
||||
mDiagnosticContext.removeLast();
|
||||
};
|
||||
}
|
||||
|
||||
private void dumpViewHierarchy() {
|
||||
final ByteArrayOutputStream stream = new ByteArrayOutputStream();
|
||||
try {
|
||||
mDevice.dumpWindowHierarchy(stream);
|
||||
stream.flush();
|
||||
stream.close();
|
||||
for (String line : stream.toString().split("\\r?\\n")) {
|
||||
Log.e(TAG, line.trim());
|
||||
}
|
||||
} catch (IOException e) {
|
||||
Log.e(TAG, "error dumping XML to logcat", e);
|
||||
}
|
||||
}
|
||||
|
||||
private void fail(String message) {
|
||||
log("Hierarchy dump for: " + getContextDescription() + message);
|
||||
dumpViewHierarchy();
|
||||
Assert.fail("http://go/tapl : " + getContextDescription() + message);
|
||||
}
|
||||
|
||||
private String getContextDescription() {
|
||||
return mDiagnosticContext.isEmpty() ? "" : String.join(", ", mDiagnosticContext) + "; ";
|
||||
}
|
||||
|
||||
void assertTrue(String message, boolean condition) {
|
||||
if (!condition) {
|
||||
fail(message);
|
||||
}
|
||||
}
|
||||
|
||||
void assertNotNull(String message, Object object) {
|
||||
assertTrue(message, object != null);
|
||||
}
|
||||
|
||||
private void failEquals(String message, Object actual) {
|
||||
fail(message + ". " + "Actual: " + actual);
|
||||
}
|
||||
|
||||
private void assertEquals(String message, int expected, int actual) {
|
||||
if (expected != actual) {
|
||||
fail(message + " expected: " + expected + " but was: " + actual);
|
||||
}
|
||||
}
|
||||
|
||||
private void assertEquals(String message, String expected, String actual) {
|
||||
if (!TextUtils.equals(expected, actual)) {
|
||||
fail(message + " expected: '" + expected + "' but was: '" + actual + "'");
|
||||
}
|
||||
}
|
||||
|
||||
void assertEquals(String message, long expected, long actual) {
|
||||
if (expected != actual) {
|
||||
fail(message + " expected: " + expected + " but was: " + actual);
|
||||
}
|
||||
}
|
||||
|
||||
void assertNotEquals(String message, int unexpected, int actual) {
|
||||
if (unexpected == actual) {
|
||||
failEquals(message, actual);
|
||||
}
|
||||
}
|
||||
|
||||
public void setExpectedRotation(int expectedRotation) {
|
||||
mExpectedRotation = expectedRotation;
|
||||
}
|
||||
|
||||
public String getNavigationModeMismatchError() {
|
||||
final NavigationModel navigationModel = getNavigationModel();
|
||||
final boolean hasRecentsButton = hasSystemUiObject("recent_apps");
|
||||
final boolean hasHomeButton = hasSystemUiObject("home");
|
||||
if ((navigationModel == NavigationModel.THREE_BUTTON) != hasRecentsButton) {
|
||||
return "Presence of recents button doesn't match the interaction mode, mode="
|
||||
+ navigationModel.name() + ", hasRecents=" + hasRecentsButton;
|
||||
}
|
||||
if ((navigationModel != NavigationModel.ZERO_BUTTON) != hasHomeButton) {
|
||||
return "Presence of home button doesn't match the interaction mode, mode="
|
||||
+ navigationModel.name() + ", hasHome=" + hasHomeButton;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private UiObject2 verifyContainerType(ContainerType containerType) {
|
||||
assertEquals("Unexpected display rotation",
|
||||
mExpectedRotation, mDevice.getDisplayRotation());
|
||||
final String error = getNavigationModeMismatchError();
|
||||
assertTrue(error, error == null);
|
||||
log("verifyContainerType: " + containerType);
|
||||
|
||||
try (Closable c = addContextLayer(
|
||||
"but the current state is not " + containerType.name())) {
|
||||
switch (containerType) {
|
||||
case WORKSPACE: {
|
||||
if (mDevice.isNaturalOrientation()) {
|
||||
waitForLauncherObject(APPS_RES_ID);
|
||||
} else {
|
||||
waitUntilGone(APPS_RES_ID);
|
||||
}
|
||||
waitUntilGone(OVERVIEW_RES_ID);
|
||||
waitUntilGone(WIDGETS_RES_ID);
|
||||
return waitForLauncherObject(WORKSPACE_RES_ID);
|
||||
}
|
||||
case WIDGETS: {
|
||||
waitUntilGone(WORKSPACE_RES_ID);
|
||||
waitUntilGone(APPS_RES_ID);
|
||||
waitUntilGone(OVERVIEW_RES_ID);
|
||||
return waitForLauncherObject(WIDGETS_RES_ID);
|
||||
}
|
||||
case ALL_APPS: {
|
||||
waitUntilGone(WORKSPACE_RES_ID);
|
||||
waitUntilGone(OVERVIEW_RES_ID);
|
||||
waitUntilGone(WIDGETS_RES_ID);
|
||||
return waitForLauncherObject(APPS_RES_ID);
|
||||
}
|
||||
case OVERVIEW: {
|
||||
if (mDevice.isNaturalOrientation()) {
|
||||
waitForLauncherObject(APPS_RES_ID);
|
||||
} else {
|
||||
waitUntilGone(APPS_RES_ID);
|
||||
}
|
||||
waitUntilGone(WORKSPACE_RES_ID);
|
||||
waitUntilGone(WIDGETS_RES_ID);
|
||||
|
||||
return waitForLauncherObject(OVERVIEW_RES_ID);
|
||||
}
|
||||
case BASE_OVERVIEW: {
|
||||
return waitForFallbackLauncherObject(OVERVIEW_RES_ID);
|
||||
}
|
||||
case BACKGROUND: {
|
||||
waitUntilGone(WORKSPACE_RES_ID);
|
||||
waitUntilGone(APPS_RES_ID);
|
||||
waitUntilGone(OVERVIEW_RES_ID);
|
||||
waitUntilGone(WIDGETS_RES_ID);
|
||||
return null;
|
||||
}
|
||||
default:
|
||||
fail("Invalid state: " + containerType);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Parcelable executeAndWaitForEvent(Runnable command,
|
||||
UiAutomation.AccessibilityEventFilter eventFilter, String message) {
|
||||
try {
|
||||
final AccessibilityEvent event =
|
||||
mInstrumentation.getUiAutomation().executeAndWaitForEvent(
|
||||
command, eventFilter, WAIT_TIME_MS);
|
||||
assertNotNull("executeAndWaitForEvent returned null (this can't happen)", event);
|
||||
return event.getParcelableData();
|
||||
} catch (TimeoutException e) {
|
||||
fail(message);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
Bundle getAnswerFromLauncher(UiObject2 view, String requestTag) {
|
||||
// Send a fake set-text request to Launcher to initiate a response with requested data.
|
||||
final String responseTag = requestTag + TestProtocol.RESPONSE_MESSAGE_POSTFIX;
|
||||
return (Bundle) executeAndWaitForEvent(
|
||||
() -> view.setText(requestTag),
|
||||
event -> responseTag.equals(event.getClassName()),
|
||||
"Launcher didn't respond to request: " + requestTag);
|
||||
}
|
||||
|
||||
/**
|
||||
* Presses nav bar home button.
|
||||
*
|
||||
* @return the Workspace object.
|
||||
*/
|
||||
public Workspace pressHome() {
|
||||
// Click home, then wait for any accessibility event, then wait until accessibility events
|
||||
// stop.
|
||||
// We need waiting for any accessibility event generated after pressing Home because
|
||||
// otherwise waitForIdle may return immediately in case when there was a big enough pause in
|
||||
// accessibility events prior to pressing Home.
|
||||
final String action;
|
||||
if (getNavigationModel() == NavigationModel.ZERO_BUTTON) {
|
||||
final Point displaySize = getRealDisplaySize();
|
||||
|
||||
if (hasLauncherObject("deep_shortcuts_container")) {
|
||||
linearGesture(
|
||||
displaySize.x / 2, displaySize.y - 1,
|
||||
displaySize.x / 2, 0,
|
||||
ZERO_BUTTON_STEPS_FROM_BACKGROUND_TO_HOME);
|
||||
assertTrue("Context menu is still visible afterswiping up to home",
|
||||
!hasLauncherObject("deep_shortcuts_container"));
|
||||
}
|
||||
if (hasLauncherObject(WORKSPACE_RES_ID)) {
|
||||
log(action = "already at home");
|
||||
} else {
|
||||
log(action = "swiping up to home");
|
||||
final int finalState = mDevice.hasObject(By.pkg(getLauncherPackageName()))
|
||||
? NORMAL_STATE_ORDINAL : BACKGROUND_APP_STATE_ORDINAL;
|
||||
|
||||
swipeToState(
|
||||
displaySize.x / 2, displaySize.y - 1,
|
||||
displaySize.x / 2, 0,
|
||||
ZERO_BUTTON_STEPS_FROM_BACKGROUND_TO_HOME, finalState);
|
||||
}
|
||||
} else {
|
||||
log(action = "clicking home button");
|
||||
executeAndWaitForEvent(
|
||||
() -> {
|
||||
log("LauncherInstrumentation.pressHome before clicking");
|
||||
waitForSystemUiObject("home").click();
|
||||
},
|
||||
event -> true,
|
||||
"Pressing Home didn't produce any events");
|
||||
mDevice.waitForIdle();
|
||||
}
|
||||
try (LauncherInstrumentation.Closable c = addContextLayer(
|
||||
"performed action to switch to Home - " + action)) {
|
||||
return getWorkspace();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the Workspace object if the current state is "active home", i.e. workspace. Fails if the
|
||||
* launcher is not in that state.
|
||||
*
|
||||
* @return Workspace object.
|
||||
*/
|
||||
@NonNull
|
||||
public Workspace getWorkspace() {
|
||||
try (LauncherInstrumentation.Closable c = addContextLayer("want to get workspace object")) {
|
||||
return new Workspace(this);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the Workspace object if the current state is "background home", i.e. some other app is
|
||||
* active. Fails if the launcher is not in that state.
|
||||
*
|
||||
* @return Background object.
|
||||
*/
|
||||
@NonNull
|
||||
public Background getBackground() {
|
||||
return new Background(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the Widgets object if the current state is showing all widgets. Fails if the launcher is
|
||||
* not in that state.
|
||||
*
|
||||
* @return Widgets object.
|
||||
*/
|
||||
@NonNull
|
||||
public Widgets getAllWidgets() {
|
||||
try (LauncherInstrumentation.Closable c = addContextLayer("want to get widgets")) {
|
||||
return new Widgets(this);
|
||||
}
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public AddToHomeScreenPrompt getAddToHomeScreenPrompt() {
|
||||
try (LauncherInstrumentation.Closable c = addContextLayer("want to get widget cell")) {
|
||||
return new AddToHomeScreenPrompt(this);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the Overview object if the current state is showing the overview panel. Fails if the
|
||||
* launcher is not in that state.
|
||||
*
|
||||
* @return Overview object.
|
||||
*/
|
||||
@NonNull
|
||||
public Overview getOverview() {
|
||||
try (LauncherInstrumentation.Closable c = addContextLayer("want to get overview")) {
|
||||
return new Overview(this);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the All Apps object if the current state is showing the all apps panel opened by swiping
|
||||
* from workspace. Fails if the launcher is not in that state. Please don't call this method if
|
||||
* App Apps was opened by swiping up from Overview, as it won't fail and will return an
|
||||
* incorrect object.
|
||||
*
|
||||
* @return All Aps object.
|
||||
*/
|
||||
@NonNull
|
||||
public AllApps getAllApps() {
|
||||
try (LauncherInstrumentation.Closable c = addContextLayer("want to get all apps object")) {
|
||||
return new AllApps(this);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the All Apps object if the current state is showing the all apps panel opened by swiping
|
||||
* from overview. Fails if the launcher is not in that state. Please don't call this method if
|
||||
* App Apps was opened by swiping up from home, as it won't fail and will return an
|
||||
* incorrect object.
|
||||
*
|
||||
* @return All Aps object.
|
||||
*/
|
||||
@NonNull
|
||||
public AllAppsFromOverview getAllAppsFromOverview() {
|
||||
try (LauncherInstrumentation.Closable c = addContextLayer("want to get all apps object")) {
|
||||
return new AllAppsFromOverview(this);
|
||||
}
|
||||
}
|
||||
|
||||
void waitUntilGone(String resId) {
|
||||
assertTrue("Unexpected launcher object visible: " + resId,
|
||||
mDevice.wait(Until.gone(getLauncherObjectSelector(resId)),
|
||||
WAIT_TIME_MS));
|
||||
}
|
||||
|
||||
private boolean hasSystemUiObject(String resId) {
|
||||
return mDevice.hasObject(By.res(SYSTEMUI_PACKAGE, resId));
|
||||
}
|
||||
|
||||
@NonNull
|
||||
UiObject2 waitForSystemUiObject(String resId) {
|
||||
final UiObject2 object = mDevice.wait(
|
||||
Until.findObject(By.res(SYSTEMUI_PACKAGE, resId)), WAIT_TIME_MS);
|
||||
assertNotNull("Can't find a systemui object with id: " + resId, object);
|
||||
return object;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
UiObject2 getObjectInContainer(UiObject2 container, BySelector selector) {
|
||||
final UiObject2 object = container.findObject(selector);
|
||||
assertNotNull("Can't find an object with selector: " + selector, object);
|
||||
return object;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
List<UiObject2> getObjectsInContainer(UiObject2 container, String resName) {
|
||||
return container.findObjects(getLauncherObjectSelector(resName));
|
||||
}
|
||||
|
||||
@NonNull
|
||||
UiObject2 waitForObjectInContainer(UiObject2 container, String resName) {
|
||||
final UiObject2 object = container.wait(
|
||||
Until.findObject(getLauncherObjectSelector(resName)),
|
||||
WAIT_TIME_MS);
|
||||
assertNotNull("Can't find a launcher object id: " + resName + " in container: " +
|
||||
container.getResourceName(), object);
|
||||
return object;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
UiObject2 waitForObjectInContainer(UiObject2 container, BySelector selector) {
|
||||
final UiObject2 object = container.wait(
|
||||
Until.findObject(selector),
|
||||
WAIT_TIME_MS);
|
||||
assertNotNull("Can't find a launcher object id: " + selector + " in container: " +
|
||||
container.getResourceName(), object);
|
||||
return object;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private boolean hasLauncherObject(String resId) {
|
||||
return mDevice.hasObject(getLauncherObjectSelector(resId));
|
||||
}
|
||||
|
||||
@NonNull
|
||||
UiObject2 waitForLauncherObject(String resName) {
|
||||
return waitForObjectBySelector(getLauncherObjectSelector(resName));
|
||||
}
|
||||
|
||||
@NonNull
|
||||
UiObject2 waitForLauncherObject(BySelector selector) {
|
||||
return waitForObjectBySelector(selector.pkg(getLauncherPackageName()));
|
||||
}
|
||||
|
||||
@NonNull
|
||||
UiObject2 tryWaitForLauncherObject(BySelector selector, long timeout) {
|
||||
return tryWaitForObjectBySelector(selector.pkg(getLauncherPackageName()), timeout);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
UiObject2 waitForFallbackLauncherObject(String resName) {
|
||||
return waitForObjectBySelector(getFallbackLauncherObjectSelector(resName));
|
||||
}
|
||||
|
||||
private UiObject2 waitForObjectBySelector(BySelector selector) {
|
||||
final UiObject2 object = mDevice.wait(Until.findObject(selector), WAIT_TIME_MS);
|
||||
assertNotNull("Can't find a launcher object; selector: " + selector, object);
|
||||
return object;
|
||||
}
|
||||
|
||||
private UiObject2 tryWaitForObjectBySelector(BySelector selector, long timeout) {
|
||||
return mDevice.wait(Until.findObject(selector), timeout);
|
||||
}
|
||||
|
||||
BySelector getLauncherObjectSelector(String resName) {
|
||||
return By.res(getLauncherPackageName(), resName);
|
||||
}
|
||||
|
||||
BySelector getFallbackLauncherObjectSelector(String resName) {
|
||||
return By.res(getOverviewPackageName(), resName);
|
||||
}
|
||||
|
||||
String getLauncherPackageName() {
|
||||
return mDevice.getLauncherPackageName();
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public UiDevice getDevice() {
|
||||
return mDevice;
|
||||
}
|
||||
|
||||
void swipeToState(int startX, int startY, int endX, int endY, int steps, int expectedState) {
|
||||
final Bundle parcel = (Bundle) executeAndWaitForEvent(
|
||||
() -> linearGesture(startX, startY, endX, endY, steps),
|
||||
event -> TestProtocol.SWITCHED_TO_STATE_MESSAGE.equals(event.getClassName()),
|
||||
"Swipe failed to receive an event for the swipe end: " + startX + ", " + startY
|
||||
+ ", " + endX + ", " + endY);
|
||||
assertEquals("Swipe switched launcher to a wrong state;",
|
||||
TestProtocol.stateOrdinalToString(expectedState),
|
||||
TestProtocol.stateOrdinalToString(parcel.getInt(TestProtocol.STATE_FIELD)));
|
||||
}
|
||||
|
||||
void scroll(UiObject2 container, Direction direction, float percent, Rect margins, int steps) {
|
||||
final Rect rect = container.getVisibleBounds();
|
||||
if (margins != null) {
|
||||
rect.left += margins.left;
|
||||
rect.top += margins.top;
|
||||
rect.right -= margins.right;
|
||||
rect.bottom -= margins.bottom;
|
||||
}
|
||||
|
||||
final int startX;
|
||||
final int startY;
|
||||
final int endX;
|
||||
final int endY;
|
||||
|
||||
switch (direction) {
|
||||
case UP: {
|
||||
startX = endX = rect.centerX();
|
||||
final int vertCenter = rect.centerY();
|
||||
final float halfGestureHeight = rect.height() * percent / 2.0f;
|
||||
startY = (int) (vertCenter - halfGestureHeight);
|
||||
endY = (int) (vertCenter + halfGestureHeight);
|
||||
}
|
||||
break;
|
||||
case DOWN: {
|
||||
startX = endX = rect.centerX();
|
||||
final int vertCenter = rect.centerY();
|
||||
final float halfGestureHeight = rect.height() * percent / 2.0f;
|
||||
startY = (int) (vertCenter + halfGestureHeight);
|
||||
endY = (int) (vertCenter - halfGestureHeight);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
fail("Unsupported direction");
|
||||
return;
|
||||
}
|
||||
|
||||
executeAndWaitForEvent(
|
||||
() -> linearGesture(startX, startY, endX, endY, steps),
|
||||
event -> TestProtocol.SCROLL_FINISHED_MESSAGE.equals(event.getClassName()),
|
||||
"Didn't receive a scroll end message: " + startX + ", " + startY
|
||||
+ ", " + endX + ", " + endY);
|
||||
}
|
||||
|
||||
// Inject a swipe gesture. Inject exactly 'steps' motion points, incrementing event time by a
|
||||
// fixed interval each time.
|
||||
void linearGesture(int startX, int startY, int endX, int endY, int steps) {
|
||||
final long downTime = SystemClock.uptimeMillis();
|
||||
final Point start = new Point(startX, startY);
|
||||
final Point end = new Point(endX, endY);
|
||||
sendPointer(downTime, downTime, MotionEvent.ACTION_DOWN, start);
|
||||
final long endTime = movePointer(downTime, downTime, steps * GESTURE_STEP_MS, start, end);
|
||||
sendPointer(downTime, endTime, MotionEvent.ACTION_UP, end);
|
||||
}
|
||||
|
||||
void waitForIdle() {
|
||||
mDevice.waitForIdle();
|
||||
}
|
||||
|
||||
float getDisplayDensity() {
|
||||
return mInstrumentation.getTargetContext().getResources().getDisplayMetrics().density;
|
||||
}
|
||||
|
||||
int getTouchSlop() {
|
||||
return ViewConfiguration.get(getContext()).getScaledTouchSlop();
|
||||
}
|
||||
|
||||
public Resources getResources() {
|
||||
return getContext().getResources();
|
||||
}
|
||||
|
||||
private static MotionEvent getMotionEvent(long downTime, long eventTime, int action,
|
||||
float x, float y) {
|
||||
MotionEvent.PointerProperties properties = new MotionEvent.PointerProperties();
|
||||
properties.id = 0;
|
||||
properties.toolType = Configurator.getInstance().getToolType();
|
||||
|
||||
MotionEvent.PointerCoords coords = new MotionEvent.PointerCoords();
|
||||
coords.pressure = 1;
|
||||
coords.size = 1;
|
||||
coords.x = x;
|
||||
coords.y = y;
|
||||
|
||||
return MotionEvent.obtain(downTime, eventTime, action, 1,
|
||||
new MotionEvent.PointerProperties[]{properties},
|
||||
new MotionEvent.PointerCoords[]{coords},
|
||||
0, 0, 1.0f, 1.0f, 0, 0, InputDevice.SOURCE_TOUCHSCREEN, 0);
|
||||
}
|
||||
|
||||
void sendPointer(long downTime, long currentTime, int action, Point point) {
|
||||
final MotionEvent event = getMotionEvent(downTime, currentTime, action, point.x, point.y);
|
||||
mInstrumentation.getUiAutomation().injectInputEvent(event, true);
|
||||
event.recycle();
|
||||
}
|
||||
|
||||
long movePointer(long downTime, long startTime, long duration, Point from, Point to) {
|
||||
final Point point = new Point();
|
||||
long steps = duration / GESTURE_STEP_MS;
|
||||
long currentTime = startTime;
|
||||
for (long i = 0; i < steps; ++i) {
|
||||
sleep(GESTURE_STEP_MS);
|
||||
|
||||
currentTime += GESTURE_STEP_MS;
|
||||
final float progress = (currentTime - startTime) / (float) duration;
|
||||
|
||||
point.x = from.x + (int) (progress * (to.x - from.x));
|
||||
point.y = from.y + (int) (progress * (to.y - from.y));
|
||||
|
||||
sendPointer(downTime, currentTime, MotionEvent.ACTION_MOVE, point);
|
||||
}
|
||||
return currentTime;
|
||||
}
|
||||
|
||||
public static int getCurrentInteractionMode(Context context) {
|
||||
return getSystemIntegerRes(context, "config_navBarInteractionMode");
|
||||
}
|
||||
|
||||
private static int getSystemIntegerRes(Context context, String resName) {
|
||||
Resources res = context.getResources();
|
||||
int resId = res.getIdentifier(resName, "integer", "android");
|
||||
|
||||
if (resId != 0) {
|
||||
return res.getInteger(resId);
|
||||
} else {
|
||||
Log.e(TAG, "Failed to get system resource ID. Incompatible framework version?");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
private static int getSystemDimensionResId(Context context, String resName) {
|
||||
Resources res = context.getResources();
|
||||
int resId = res.getIdentifier(resName, "dimen", "android");
|
||||
|
||||
if (resId != 0) {
|
||||
return resId;
|
||||
} else {
|
||||
Log.e(TAG, "Failed to get system resource ID. Incompatible framework version?");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
static void sleep(int duration) {
|
||||
SystemClock.sleep(duration);
|
||||
}
|
||||
|
||||
int getEdgeSensitivityWidth() {
|
||||
try {
|
||||
final Context context = mInstrumentation.getTargetContext().createPackageContext(
|
||||
"android", 0);
|
||||
return context.getResources().getDimensionPixelSize(
|
||||
getSystemDimensionResId(context, "config_backGestureInset")) + 1;
|
||||
} catch (PackageManager.NameNotFoundException e) {
|
||||
fail("Can't get edge sensitivity: " + e);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
Point getRealDisplaySize() {
|
||||
final Point size = new Point();
|
||||
getContext().getSystemService(WindowManager.class).getDefaultDisplay().getRealSize(size);
|
||||
return size;
|
||||
}
|
||||
}
|
||||
66
tests/tapl/com/android/uiuios/tapl/Overview.java
Normal file
66
tests/tapl/com/android/uiuios/tapl/Overview.java
Normal file
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* Copyright (C) 2018 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.uiuios.tapl;
|
||||
|
||||
import static com.android.uiuios.testing.TestProtocol.ALL_APPS_STATE_ORDINAL;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.test.uiautomator.UiObject2;
|
||||
|
||||
import com.android.uiuios.tapl.LauncherInstrumentation.ContainerType;
|
||||
|
||||
/**
|
||||
* Overview pane.
|
||||
*/
|
||||
public final class Overview extends BaseOverview {
|
||||
|
||||
Overview(LauncherInstrumentation launcher) {
|
||||
super(launcher);
|
||||
verifyActiveContainer();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ContainerType getContainerType() {
|
||||
return LauncherInstrumentation.ContainerType.OVERVIEW;
|
||||
}
|
||||
|
||||
/**
|
||||
* Swipes up to All Apps.
|
||||
*
|
||||
* @return the App Apps object.
|
||||
*/
|
||||
@NonNull
|
||||
public AllAppsFromOverview switchToAllApps() {
|
||||
try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
|
||||
"want to switch from overview to all apps")) {
|
||||
verifyActiveContainer();
|
||||
|
||||
// Swipe from an app icon to the top.
|
||||
LauncherInstrumentation.log("Overview.switchToAllApps before swipe");
|
||||
final UiObject2 allApps = mLauncher.waitForLauncherObject("apps_view");
|
||||
mLauncher.swipeToState(mLauncher.getDevice().getDisplayWidth() / 2,
|
||||
allApps.getVisibleBounds().top,
|
||||
mLauncher.getDevice().getDisplayWidth() / 2,
|
||||
0, 50, ALL_APPS_STATE_ORDINAL);
|
||||
|
||||
try (LauncherInstrumentation.Closable c1 = mLauncher.addContextLayer(
|
||||
"swiped all way up from overview")) {
|
||||
return new AllAppsFromOverview(mLauncher);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
77
tests/tapl/com/android/uiuios/tapl/OverviewTask.java
Normal file
77
tests/tapl/com/android/uiuios/tapl/OverviewTask.java
Normal file
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
* Copyright (C) 2018 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.uiuios.tapl;
|
||||
|
||||
import android.graphics.Rect;
|
||||
|
||||
import androidx.test.uiautomator.UiObject2;
|
||||
import androidx.test.uiautomator.Until;
|
||||
|
||||
import com.android.uiuios.testing.TestProtocol;
|
||||
|
||||
/**
|
||||
* A recent task in the overview panel carousel.
|
||||
*/
|
||||
public final class OverviewTask {
|
||||
private static final long WAIT_TIME_MS = 60000;
|
||||
private final LauncherInstrumentation mLauncher;
|
||||
private final UiObject2 mTask;
|
||||
private final BaseOverview mOverview;
|
||||
|
||||
OverviewTask(LauncherInstrumentation launcher, UiObject2 task, BaseOverview overview) {
|
||||
mLauncher = launcher;
|
||||
mTask = task;
|
||||
mOverview = overview;
|
||||
verifyActiveContainer();
|
||||
}
|
||||
|
||||
private void verifyActiveContainer() {
|
||||
mOverview.verifyActiveContainer();
|
||||
}
|
||||
|
||||
/**
|
||||
* Swipes the task up.
|
||||
*/
|
||||
public void dismiss() {
|
||||
try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
|
||||
"want to dismiss a task")) {
|
||||
verifyActiveContainer();
|
||||
// Dismiss the task via flinging it up.
|
||||
final Rect taskBounds = mTask.getVisibleBounds();
|
||||
final int centerX = taskBounds.centerX();
|
||||
final int centerY = taskBounds.centerY();
|
||||
mLauncher.linearGesture(centerX, centerY, centerX, 0, 10);
|
||||
mLauncher.waitForIdle();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clicks at the task.
|
||||
*/
|
||||
public Background open() {
|
||||
verifyActiveContainer();
|
||||
mLauncher.getTestInfo(TestProtocol.REQUEST_ENABLE_DEBUG_TRACING);
|
||||
try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
|
||||
"clicking an overview task")) {
|
||||
mLauncher.assertTrue("Launching task didn't open a new window: " +
|
||||
mTask.getParent().getContentDescription(),
|
||||
mTask.clickAndWait(Until.newWindow(), WAIT_TIME_MS));
|
||||
}
|
||||
mLauncher.getTestInfo(TestProtocol.REQUEST_DISABLE_DEBUG_TRACING);
|
||||
return new Background(mLauncher);
|
||||
}
|
||||
}
|
||||
84
tests/tapl/com/android/uiuios/tapl/TestHelpers.java
Normal file
84
tests/tapl/com/android/uiuios/tapl/TestHelpers.java
Normal file
@@ -0,0 +1,84 @@
|
||||
/*
|
||||
* Copyright (C) 2018 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.uiuios.tapl;
|
||||
|
||||
import static androidx.test.InstrumentationRegistry.getInstrumentation;
|
||||
import static androidx.test.InstrumentationRegistry.getTargetContext;
|
||||
|
||||
import android.app.Instrumentation;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.ActivityInfo;
|
||||
import android.content.pm.ResolveInfo;
|
||||
import android.content.res.Resources;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class TestHelpers {
|
||||
|
||||
private static Boolean sIsInLauncherProcess;
|
||||
|
||||
public static boolean isInLauncherProcess() {
|
||||
if (sIsInLauncherProcess == null) {
|
||||
sIsInLauncherProcess = initIsInLauncherProcess();
|
||||
}
|
||||
return sIsInLauncherProcess;
|
||||
}
|
||||
|
||||
private static boolean initIsInLauncherProcess() {
|
||||
ActivityInfo info = getLauncherInMyProcess();
|
||||
|
||||
// If we are in the same process, we can instantiate the class name.
|
||||
try {
|
||||
Class launcherClazz = Class.forName("com.android.uiuios.Launcher");
|
||||
return launcherClazz.isAssignableFrom(Class.forName(info.name));
|
||||
} catch (Exception e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static Intent getHomeIntentInPackage(Context context) {
|
||||
return new Intent(Intent.ACTION_MAIN)
|
||||
.addCategory(Intent.CATEGORY_HOME)
|
||||
.setPackage(context.getPackageName())
|
||||
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
}
|
||||
|
||||
public static ActivityInfo getLauncherInMyProcess() {
|
||||
Instrumentation instrumentation = getInstrumentation();
|
||||
if (instrumentation.getTargetContext() == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
List<ResolveInfo> launchers = getTargetContext().getPackageManager()
|
||||
.queryIntentActivities(getHomeIntentInPackage(getTargetContext()), 0);
|
||||
if (launchers.size() != 1) {
|
||||
return null;
|
||||
}
|
||||
return launchers.get(0).activityInfo;
|
||||
}
|
||||
|
||||
public static String getOverviewPackageName() {
|
||||
Resources res = Resources.getSystem();
|
||||
int id = res.getIdentifier("config_recentsComponentName", "string", "android");
|
||||
if (id != 0) {
|
||||
return ComponentName.unflattenFromString(res.getString(id)).getPackageName();
|
||||
}
|
||||
return "com.android.systemui";
|
||||
}
|
||||
}
|
||||
24
tests/tapl/com/android/uiuios/tapl/Widget.java
Normal file
24
tests/tapl/com/android/uiuios/tapl/Widget.java
Normal file
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* Copyright (C) 2019 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.uiuios.tapl;
|
||||
|
||||
import androidx.test.uiautomator.UiObject2;
|
||||
|
||||
public class Widget {
|
||||
Widget(LauncherInstrumentation launcher, UiObject2 widget) {
|
||||
}
|
||||
}
|
||||
78
tests/tapl/com/android/uiuios/tapl/Widgets.java
Normal file
78
tests/tapl/com/android/uiuios/tapl/Widgets.java
Normal file
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
* Copyright (C) 2018 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.uiuios.tapl;
|
||||
|
||||
import androidx.test.uiautomator.Direction;
|
||||
import androidx.test.uiautomator.UiObject2;
|
||||
|
||||
import com.android.uiuios.ResourceUtils;
|
||||
|
||||
/**
|
||||
* All widgets container.
|
||||
*/
|
||||
public final class Widgets extends LauncherInstrumentation.VisibleContainer {
|
||||
private static final int FLING_SPEED = 1500;
|
||||
|
||||
Widgets(LauncherInstrumentation launcher) {
|
||||
super(launcher);
|
||||
verifyActiveContainer();
|
||||
}
|
||||
|
||||
/**
|
||||
* Flings forward (down) and waits the fling's end.
|
||||
*/
|
||||
public void flingForward() {
|
||||
try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
|
||||
"want to fling forward in widgets")) {
|
||||
LauncherInstrumentation.log("Widgets.flingForward enter");
|
||||
final UiObject2 widgetsContainer = verifyActiveContainer();
|
||||
widgetsContainer.setGestureMargins(0, 0, 0,
|
||||
ResourceUtils.getNavbarSize(ResourceUtils.NAVBAR_BOTTOM_GESTURE_SIZE,
|
||||
mLauncher.getResources()) + 1);
|
||||
widgetsContainer.fling(Direction.DOWN,
|
||||
(int) (FLING_SPEED * mLauncher.getDisplayDensity()));
|
||||
try (LauncherInstrumentation.Closable c1 = mLauncher.addContextLayer("flung forward")) {
|
||||
verifyActiveContainer();
|
||||
}
|
||||
LauncherInstrumentation.log("Widgets.flingForward exit");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Flings backward (up) and waits the fling's end.
|
||||
*/
|
||||
public void flingBackward() {
|
||||
try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
|
||||
"want to fling backwards in widgets")) {
|
||||
LauncherInstrumentation.log("Widgets.flingBackward enter");
|
||||
final UiObject2 widgetsContainer = verifyActiveContainer();
|
||||
widgetsContainer.setGestureMargin(100);
|
||||
widgetsContainer.fling(Direction.UP,
|
||||
(int) (FLING_SPEED * mLauncher.getDisplayDensity()));
|
||||
mLauncher.waitForIdle();
|
||||
try (LauncherInstrumentation.Closable c1 = mLauncher.addContextLayer("flung back")) {
|
||||
verifyActiveContainer();
|
||||
}
|
||||
LauncherInstrumentation.log("Widgets.flingBackward exit");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected LauncherInstrumentation.ContainerType getContainerType() {
|
||||
return LauncherInstrumentation.ContainerType.WIDGETS;
|
||||
}
|
||||
}
|
||||
240
tests/tapl/com/android/uiuios/tapl/Workspace.java
Normal file
240
tests/tapl/com/android/uiuios/tapl/Workspace.java
Normal file
@@ -0,0 +1,240 @@
|
||||
/*
|
||||
* Copyright (C) 2018 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.uiuios.tapl;
|
||||
|
||||
import static com.android.uiuios.testing.TestProtocol.ALL_APPS_STATE_ORDINAL;
|
||||
|
||||
import static junit.framework.TestCase.assertTrue;
|
||||
|
||||
import android.graphics.Point;
|
||||
import android.os.SystemClock;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.MotionEvent;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.test.uiautomator.By;
|
||||
import androidx.test.uiautomator.Direction;
|
||||
import androidx.test.uiautomator.UiObject2;
|
||||
|
||||
import com.android.uiuios.testing.TestProtocol;
|
||||
|
||||
/**
|
||||
* Operations on the workspace screen.
|
||||
*/
|
||||
public final class Workspace extends Home {
|
||||
private static final float FLING_SPEED =
|
||||
LauncherInstrumentation.isAvd() ? 1500.0F : 3500.0F;
|
||||
private static final int DRAG_DURACTION = 2000;
|
||||
private final UiObject2 mHotseat;
|
||||
|
||||
Workspace(LauncherInstrumentation launcher) {
|
||||
super(launcher);
|
||||
mHotseat = launcher.waitForLauncherObject("hotseat");
|
||||
}
|
||||
|
||||
/**
|
||||
* Swipes up to All Apps.
|
||||
*
|
||||
* @return the App Apps object.
|
||||
*/
|
||||
@NonNull
|
||||
public AllApps switchToAllApps() {
|
||||
try (LauncherInstrumentation.Closable c =
|
||||
mLauncher.addContextLayer("want to switch from workspace to all apps")) {
|
||||
verifyActiveContainer();
|
||||
final UiObject2 hotseat = mHotseat;
|
||||
final Point start = hotseat.getVisibleCenter();
|
||||
start.y = hotseat.getVisibleBounds().bottom - 1;
|
||||
final int swipeHeight = mLauncher.getTestInfo(
|
||||
TestProtocol.REQUEST_HOME_TO_ALL_APPS_SWIPE_HEIGHT).
|
||||
getInt(TestProtocol.TEST_INFO_RESPONSE_FIELD);
|
||||
LauncherInstrumentation.log(
|
||||
"switchToAllApps: swipeHeight = " + swipeHeight + ", slop = "
|
||||
+ mLauncher.getTouchSlop());
|
||||
|
||||
mLauncher.getTestInfo(TestProtocol.REQUEST_ENABLE_DEBUG_TRACING);
|
||||
mLauncher.swipeToState(
|
||||
start.x,
|
||||
start.y,
|
||||
start.x,
|
||||
start.y - swipeHeight - mLauncher.getTouchSlop(),
|
||||
60,
|
||||
ALL_APPS_STATE_ORDINAL);
|
||||
mLauncher.getTestInfo(TestProtocol.REQUEST_DISABLE_DEBUG_TRACING);
|
||||
|
||||
try (LauncherInstrumentation.Closable c1 = mLauncher.addContextLayer(
|
||||
"swiped to all apps")) {
|
||||
return new AllApps(mLauncher);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an icon for the app, if currently visible.
|
||||
*
|
||||
* @param appName name of the app
|
||||
* @return app icon, if found, null otherwise.
|
||||
*/
|
||||
@Nullable
|
||||
public AppIcon tryGetWorkspaceAppIcon(String appName) {
|
||||
try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
|
||||
"want to get a workspace icon")) {
|
||||
final UiObject2 workspace = verifyActiveContainer();
|
||||
final UiObject2 icon = workspace.findObject(
|
||||
AppIcon.getAppIconSelector(appName, mLauncher));
|
||||
return icon != null ? new AppIcon(mLauncher, icon) : null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns an icon for the app; fails if the icon doesn't exist.
|
||||
*
|
||||
* @param appName name of the app
|
||||
* @return app icon.
|
||||
*/
|
||||
@NonNull
|
||||
public AppIcon getWorkspaceAppIcon(String appName) {
|
||||
return new AppIcon(mLauncher,
|
||||
mLauncher.getObjectInContainer(
|
||||
verifyActiveContainer(),
|
||||
AppIcon.getAppIconSelector(appName, mLauncher)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures that workspace is scrollable. If it's not, drags an icon icons from hotseat to the
|
||||
* second screen.
|
||||
*/
|
||||
public void ensureWorkspaceIsScrollable() {
|
||||
final UiObject2 workspace = verifyActiveContainer();
|
||||
if (!isWorkspaceScrollable(workspace)) {
|
||||
try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
|
||||
"dragging icon to a second page of workspace to make it scrollable")) {
|
||||
dragIconToWorkspace(
|
||||
mLauncher,
|
||||
getHotseatAppIcon("Chrome"),
|
||||
new Point(mLauncher.getDevice().getDisplayWidth(),
|
||||
workspace.getVisibleBounds().centerY()),
|
||||
"deep_shortcuts_container");
|
||||
verifyActiveContainer();
|
||||
}
|
||||
}
|
||||
assertTrue("Home screen workspace didn't become scrollable",
|
||||
isWorkspaceScrollable(workspace));
|
||||
}
|
||||
|
||||
private boolean isWorkspaceScrollable(UiObject2 workspace) {
|
||||
return workspace.isScrollable();
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public AppIcon getHotseatAppIcon(String appName) {
|
||||
return new AppIcon(mLauncher, mLauncher.getObjectInContainer(
|
||||
mHotseat, AppIcon.getAppIconSelector(appName, mLauncher)));
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public Folder getHotseatFolder(String appName) {
|
||||
return new Folder(mLauncher, mLauncher.getObjectInContainer(
|
||||
mHotseat, Folder.getSelector(appName, mLauncher)));
|
||||
}
|
||||
|
||||
static void dragIconToWorkspace(
|
||||
LauncherInstrumentation launcher, Launchable launchable, Point dest,
|
||||
String longPressIndicator) {
|
||||
launcher.getTestInfo(TestProtocol.REQUEST_ENABLE_DEBUG_TRACING);
|
||||
LauncherInstrumentation.log("dragIconToWorkspace: begin");
|
||||
final Point launchableCenter = launchable.getObject().getVisibleCenter();
|
||||
final long downTime = SystemClock.uptimeMillis();
|
||||
launcher.sendPointer(downTime, downTime, MotionEvent.ACTION_DOWN, launchableCenter);
|
||||
LauncherInstrumentation.log("dragIconToWorkspace: sent down");
|
||||
launcher.waitForLauncherObject(longPressIndicator);
|
||||
LauncherInstrumentation.log("dragIconToWorkspace: indicator");
|
||||
launcher.movePointer(
|
||||
downTime, SystemClock.uptimeMillis(), DRAG_DURACTION, launchableCenter, dest);
|
||||
LauncherInstrumentation.log("dragIconToWorkspace: moved pointer");
|
||||
launcher.sendPointer(
|
||||
downTime, SystemClock.uptimeMillis(), MotionEvent.ACTION_UP, dest);
|
||||
LauncherInstrumentation.log("dragIconToWorkspace: end");
|
||||
launcher.waitUntilGone("drop_target_bar");
|
||||
launcher.getTestInfo(TestProtocol.REQUEST_DISABLE_DEBUG_TRACING);
|
||||
}
|
||||
|
||||
/**
|
||||
* Flings to get to screens on the right. Waits for scrolling and a possible overscroll
|
||||
* recoil to complete.
|
||||
*/
|
||||
public void flingForward() {
|
||||
final UiObject2 workspace = verifyActiveContainer();
|
||||
workspace.setGestureMargins(0, 0, mLauncher.getEdgeSensitivityWidth(), 0);
|
||||
workspace.fling(Direction.RIGHT, (int) (FLING_SPEED * mLauncher.getDisplayDensity()));
|
||||
mLauncher.waitForIdle();
|
||||
verifyActiveContainer();
|
||||
}
|
||||
|
||||
/**
|
||||
* Flings to get to screens on the left. Waits for scrolling and a possible overscroll
|
||||
* recoil to complete.
|
||||
*/
|
||||
public void flingBackward() {
|
||||
final UiObject2 workspace = verifyActiveContainer();
|
||||
workspace.setGestureMargins(mLauncher.getEdgeSensitivityWidth(), 0, 0, 0);
|
||||
workspace.fling(Direction.LEFT, (int) (FLING_SPEED * mLauncher.getDisplayDensity()));
|
||||
mLauncher.waitForIdle();
|
||||
verifyActiveContainer();
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens widgets container by pressing Ctrl+W.
|
||||
*
|
||||
* @return the widgets container.
|
||||
*/
|
||||
@NonNull
|
||||
public Widgets openAllWidgets() {
|
||||
verifyActiveContainer();
|
||||
mLauncher.getDevice().pressKeyCode(KeyEvent.KEYCODE_W, KeyEvent.META_CTRL_ON);
|
||||
try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer("pressed Ctrl+W")) {
|
||||
return new Widgets(mLauncher);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getSwipeHeightRequestName() {
|
||||
return TestProtocol.REQUEST_HOME_TO_OVERVIEW_SWIPE_HEIGHT;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getSwipeStartY() {
|
||||
return mLauncher.getRealDisplaySize().y - 1;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Widget tryGetWidget(String label, long timeout) {
|
||||
final UiObject2 widget = mLauncher.tryWaitForLauncherObject(
|
||||
By.clazz("com.android.uiuios.widget.LauncherAppWidgetHostView").desc(label),
|
||||
timeout);
|
||||
return widget != null ? new Widget(mLauncher, widget) : null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Widget tryGetPendingWidget(long timeout) {
|
||||
final UiObject2 widget = mLauncher.tryWaitForLauncherObject(
|
||||
By.clazz("com.android.uiuios.widget.PendingAppWidgetHostView"), timeout);
|
||||
return widget != null ? new Widget(mLauncher, widget) : null;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user