From 5261776d3a22f30eb21a316dc49973a38d17fb7b Mon Sep 17 00:00:00 2001 From: Benno Lin Date: Fri, 3 Sep 2021 20:18:34 +0000 Subject: [PATCH 1/9] Update TAPL to operate on folder icons. The change updates features listed as follow: 1. Create a new class for folder icon. 2. Implement function to drag a icon to the other. (create a folder) 3. Find folders in a workspace. Bug: 199120092 Test: Launcher3Tests:com.android.launcher3.ui.TaplTestsLauncher3#testDragToFolder Change-Id: I3044025f8297f6e442446d70238f4b56f38a798a --- .../compat/AccessibilityManagerCompat.java | 15 ++- src/com/android/launcher3/folder/Folder.java | 5 +- .../testing/TestInformationHandler.java | 10 ++ .../launcher3/testing/TestProtocol.java | 2 + .../launcher3/ui/TaplTestsLauncher3.java | 44 +++++++ .../com/android/launcher3/tapl/AppIcon.java | 44 ++++++- .../com/android/launcher3/tapl/Folder.java | 76 ++++++++++++ .../launcher3/tapl/FolderDragTarget.java | 25 ++++ .../android/launcher3/tapl/FolderIcon.java | 64 ++++++++++ .../tapl/LauncherInstrumentation.java | 61 +++++++--- .../com/android/launcher3/tapl/Workspace.java | 111 ++++++++++++++---- 11 files changed, 413 insertions(+), 44 deletions(-) create mode 100644 tests/tapl/com/android/launcher3/tapl/Folder.java create mode 100644 tests/tapl/com/android/launcher3/tapl/FolderDragTarget.java create mode 100644 tests/tapl/com/android/launcher3/tapl/FolderIcon.java diff --git a/src/com/android/launcher3/compat/AccessibilityManagerCompat.java b/src/com/android/launcher3/compat/AccessibilityManagerCompat.java index 97052b29c5..dd5812302d 100644 --- a/src/com/android/launcher3/compat/AccessibilityManagerCompat.java +++ b/src/com/android/launcher3/compat/AccessibilityManagerCompat.java @@ -41,11 +41,10 @@ public class AccessibilityManagerCompat { } /** - * * @param target The view the accessibility event is initialized on. * If null, this method has no effect. - * @param type See TYPE_ constants defined in {@link AccessibilityEvent}. - * @param text Optional text to add to the event, which will be announced to the user. + * @param type See TYPE_ constants defined in {@link AccessibilityEvent}. + * @param text Optional text to add to the event, which will be announced to the user. */ public static void sendCustomAccessibilityEvent(@Nullable View target, int type, @Nullable String text) { @@ -97,6 +96,16 @@ public class AccessibilityManagerCompat { null); } + /** + * Notify running tests of a folder opened. + */ + public static void sendFolderOpenedEventToTest(Context context) { + final AccessibilityManager accessibilityManager = getAccessibilityManagerForTest(context); + if (accessibilityManager == null) return; + + sendEventToTest(accessibilityManager, context, TestProtocol.FOLDER_OPENED_MESSAGE, null); + } + private static void sendEventToTest( AccessibilityManager accessibilityManager, Context context, String eventTag, Bundle data) { diff --git a/src/com/android/launcher3/folder/Folder.java b/src/com/android/launcher3/folder/Folder.java index 879739f171..daef68226d 100644 --- a/src/com/android/launcher3/folder/Folder.java +++ b/src/com/android/launcher3/folder/Folder.java @@ -78,6 +78,7 @@ import com.android.launcher3.Utilities; import com.android.launcher3.accessibility.AccessibleDragListenerAdapter; import com.android.launcher3.accessibility.FolderAccessibilityHelper; import com.android.launcher3.anim.KeyboardInsetAnimationCallback; +import com.android.launcher3.compat.AccessibilityManagerCompat; import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.dragndrop.DragController; import com.android.launcher3.dragndrop.DragController.DragListener; @@ -687,6 +688,7 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo public void onAnimationEnd(Animator animation) { mState = STATE_OPEN; announceAccessibilityChanges(); + AccessibilityManagerCompat.sendFolderOpenedEventToTest(getContext()); mContent.setFocusOnFirstChild(); } @@ -1265,7 +1267,8 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo PendingAddShortcutInfo pasi = d.dragInfo instanceof PendingAddShortcutInfo ? (PendingAddShortcutInfo) d.dragInfo : null; - WorkspaceItemInfo pasiSi = pasi != null ? pasi.activityInfo.createWorkspaceItemInfo() : null; + WorkspaceItemInfo pasiSi = + pasi != null ? pasi.activityInfo.createWorkspaceItemInfo() : null; if (pasi != null && pasiSi == null) { // There is no WorkspaceItemInfo, so we have to go through a configuration activity. pasi.container = mInfo.id; diff --git a/src/com/android/launcher3/testing/TestInformationHandler.java b/src/com/android/launcher3/testing/TestInformationHandler.java index 5a9c074f96..17d925c1f9 100644 --- a/src/com/android/launcher3/testing/TestInformationHandler.java +++ b/src/com/android/launcher3/testing/TestInformationHandler.java @@ -21,6 +21,7 @@ import static com.android.launcher3.util.Executors.MAIN_EXECUTOR; import android.annotation.TargetApi; import android.app.Activity; import android.content.Context; +import android.content.res.Resources; import android.graphics.Insets; import android.os.Build; import android.os.Bundle; @@ -148,6 +149,14 @@ public class TestInformationHandler implements ResourceBasedOverride { TestProtocol.TEST_INFO_RESPONSE_FIELD, TestLogging.sHadEventsNotFromTest); return response; + case TestProtocol.REQUEST_START_DRAG_THRESHOLD: { + final Resources resources = mContext.getResources(); + response.putInt(TestProtocol.TEST_INFO_RESPONSE_FIELD, + resources.getDimensionPixelSize(R.dimen.deep_shortcuts_start_drag_threshold) + + resources.getDimensionPixelSize(R.dimen.pre_drag_view_scale)); + return response; + } + default: return null; } @@ -193,6 +202,7 @@ public class TestInformationHandler implements ResourceBasedOverride { /** * Generic interface for setting a fiend in bundle + * * @param the type of value being set */ public interface BundleSetter { diff --git a/src/com/android/launcher3/testing/TestProtocol.java b/src/com/android/launcher3/testing/TestProtocol.java index 5bf0342fcc..0c5da231b5 100644 --- a/src/com/android/launcher3/testing/TestProtocol.java +++ b/src/com/android/launcher3/testing/TestProtocol.java @@ -25,6 +25,7 @@ public final class TestProtocol { public static final String SCROLL_FINISHED_MESSAGE = "TAPL_SCROLL_FINISHED"; public static final String PAUSE_DETECTED_MESSAGE = "TAPL_PAUSE_DETECTED"; public static final String DISMISS_ANIMATION_ENDS_MESSAGE = "TAPL_DISMISS_ANIMATION_ENDS"; + public static final String FOLDER_OPENED_MESSAGE = "TAPL_FOLDER_OPENED"; public static final int NORMAL_STATE_ORDINAL = 0; public static final int SPRING_LOADED_STATE_ORDINAL = 1; public static final int OVERVIEW_STATE_ORDINAL = 2; @@ -99,6 +100,7 @@ public final class TestProtocol { public static final String REQUEST_CLEAR_DATA = "clear-data"; public static final String REQUEST_IS_TABLET = "is-tablet"; public static final String REQUEST_IS_TWO_PANELS = "is-two-panel"; + public static final String REQUEST_START_DRAG_THRESHOLD = "start-drag-threshold"; public static final String REQUEST_GET_ACTIVITIES_CREATED_COUNT = "get-activities-created-count"; public static final String REQUEST_GET_ACTIVITIES = "get-activities"; diff --git a/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java b/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java index 881f50cafb..2fa84b2430 100644 --- a/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java +++ b/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java @@ -34,6 +34,8 @@ import com.android.launcher3.tapl.AllApps; import com.android.launcher3.tapl.AppIcon; import com.android.launcher3.tapl.AppIconMenu; import com.android.launcher3.tapl.AppIconMenuItem; +import com.android.launcher3.tapl.Folder; +import com.android.launcher3.tapl.FolderIcon; import com.android.launcher3.tapl.Widgets; import com.android.launcher3.tapl.Workspace; import com.android.launcher3.views.OptionsPopupView; @@ -369,6 +371,48 @@ public class TaplTestsLauncher3 extends AbstractLauncherUiTest { } } + private AppIcon createShortcutIfNotExist(String name) { + AppIcon appIcon = mLauncher.getWorkspace().tryGetWorkspaceAppIcon(name); + if (appIcon == null) { + AllApps allApps = mLauncher.getWorkspace().switchToAllApps(); + allApps.freeze(); + try { + appIcon = allApps.getAppIcon(name); + appIcon.dragToWorkspace(false, false); + } finally { + allApps.unfreeze(); + } + appIcon = mLauncher.getWorkspace().getWorkspaceAppIcon(name); + } + return appIcon; + } + + @Test + @PortraitLandscape + public void testDragToFolder() throws Exception { + final AppIcon playStoreIcon = createShortcutIfNotExist("Play Store"); + final AppIcon gmailIcon = createShortcutIfNotExist("Gmail"); + + FolderIcon folderIcon = gmailIcon.dragToIcon(playStoreIcon); + + Folder folder = folderIcon.open(); + folder.getAppIcon("Play Store"); + folder.getAppIcon("Gmail"); + Workspace workspace = folder.close(); + + assertNull("Gmail should be moved to a folder.", + workspace.tryGetWorkspaceAppIcon("Gmail")); + assertNull("Play Store should be moved to a folder.", + workspace.tryGetWorkspaceAppIcon("Play Store")); + + final AppIcon youTubeIcon = createShortcutIfNotExist("YouTube"); + + folderIcon = youTubeIcon.dragToIcon(folderIcon); + folder = folderIcon.open(); + folder.getAppIcon("YouTube"); + folder.close(); + } + public static String getAppPackageName() { return getInstrumentation().getContext().getPackageName(); } diff --git a/tests/tapl/com/android/launcher3/tapl/AppIcon.java b/tests/tapl/com/android/launcher3/tapl/AppIcon.java index 21099b4934..6da59da66c 100644 --- a/tests/tapl/com/android/launcher3/tapl/AppIcon.java +++ b/tests/tapl/com/android/launcher3/tapl/AppIcon.java @@ -16,8 +16,11 @@ package com.android.launcher3.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.UiObject2; @@ -29,7 +32,7 @@ import java.util.regex.Pattern; /** * App icon, whether in all apps or in workspace/ */ -public final class AppIcon extends Launchable { +public final class AppIcon extends Launchable implements FolderDragTarget { private static final Pattern LONG_CLICK_EVENT = Pattern.compile("onAllAppsItemLongClick"); @@ -61,6 +64,29 @@ public final class AppIcon extends Launchable { } } + /** + * Drag the AppIcon to the given position of other icon. The drag must result in a folder. + * + * @param target the destination icon. + */ + @NonNull + public FolderIcon dragToIcon(FolderDragTarget target) { + try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck(); + LauncherInstrumentation.Closable c = mLauncher.addContextLayer("want to drag icon")) { + final Rect dropBounds = target.getDropLocationBounds(); + Workspace.dragIconToWorkspace( + mLauncher, this, + () -> { + final Rect bounds = target.getDropLocationBounds(); + return new Point(bounds.centerX(), bounds.centerY()); + }, + getLongPressIndicator()); + FolderIcon result = target.getTargetFolder(dropBounds); + mLauncher.assertTrue("Can't find the target folder.", result != null); + return result; + } + } + @Override protected void addExpectedEventsForLongClick() { mLauncher.expectEvent(TestProtocol.SEQUENCE_MAIN, LONG_CLICK_EVENT); @@ -80,4 +106,20 @@ public final class AppIcon extends Launchable { protected String launchableType() { return "app icon"; } + + @Override + public Rect getDropLocationBounds() { + return mLauncher.getVisibleBounds(mObject); + } + + @Override + public FolderIcon getTargetFolder(Rect bounds) { + for (FolderIcon folderIcon : mLauncher.getWorkspace().getFolderIcons()) { + final Rect folderIconBounds = folderIcon.getDropLocationBounds(); + if (bounds.contains(folderIconBounds.centerX(), folderIconBounds.centerY())) { + return folderIcon; + } + } + return null; + } } diff --git a/tests/tapl/com/android/launcher3/tapl/Folder.java b/tests/tapl/com/android/launcher3/tapl/Folder.java new file mode 100644 index 0000000000..dba308dbf7 --- /dev/null +++ b/tests/tapl/com/android/launcher3/tapl/Folder.java @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2021 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.Point; +import android.graphics.Rect; +import android.os.SystemClock; +import android.view.MotionEvent; + +import androidx.annotation.NonNull; +import androidx.test.uiautomator.UiObject2; + +public class Folder { + + protected static final String FOLDER_CONTENT_RES_ID = "folder_content"; + + private final UiObject2 mContainer; + private final LauncherInstrumentation mLauncher; + + Folder(LauncherInstrumentation launcher) { + this.mLauncher = launcher; + this.mContainer = launcher.waitForLauncherObject(FOLDER_CONTENT_RES_ID); + } + + /** + * Find an app icon with given name or raise assertion error. + */ + @NonNull + public AppIcon getAppIcon(String appName) { + try (LauncherInstrumentation.Closable ignored = mLauncher.addContextLayer( + "Want to get app icon in folder")) { + return new AppIcon(mLauncher, + mLauncher.waitForObjectInContainer( + mContainer, + AppIcon.getAppIconSelector(appName, mLauncher))); + } + } + + private void touchOutsideFolder() { + Rect containerBounds = mLauncher.getVisibleBounds(this.mContainer); + final long downTime = SystemClock.uptimeMillis(); + Point containerLeftTopCorner = new Point(containerBounds.left - 1, containerBounds.top - 1); + mLauncher.sendPointer(downTime, downTime, MotionEvent.ACTION_DOWN, + containerLeftTopCorner, LauncherInstrumentation.GestureScope.INSIDE); + mLauncher.sendPointer(downTime, downTime, MotionEvent.ACTION_UP, + containerLeftTopCorner, LauncherInstrumentation.GestureScope.INSIDE); + } + + /** + * CLose opened folder if possible. It throws assertion error if the folder is already closed. + */ + public Workspace close() { + try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck(); + LauncherInstrumentation.Closable c = mLauncher.addContextLayer( + "Want to close opened folder")) { + mLauncher.waitForLauncherObject(FOLDER_CONTENT_RES_ID); + touchOutsideFolder(); + mLauncher.waitUntilLauncherObjectGone(FOLDER_CONTENT_RES_ID); + return mLauncher.getWorkspace(); + } + } +} diff --git a/tests/tapl/com/android/launcher3/tapl/FolderDragTarget.java b/tests/tapl/com/android/launcher3/tapl/FolderDragTarget.java new file mode 100644 index 0000000000..d797418ab4 --- /dev/null +++ b/tests/tapl/com/android/launcher3/tapl/FolderDragTarget.java @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2021 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; + +public interface FolderDragTarget { + Rect getDropLocationBounds(); + + FolderIcon getTargetFolder(Rect bounds); +} diff --git a/tests/tapl/com/android/launcher3/tapl/FolderIcon.java b/tests/tapl/com/android/launcher3/tapl/FolderIcon.java new file mode 100644 index 0000000000..2e79d70ee6 --- /dev/null +++ b/tests/tapl/com/android/launcher3/tapl/FolderIcon.java @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2021 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.annotation.NonNull; +import androidx.test.uiautomator.UiObject2; + +import com.android.launcher3.testing.TestProtocol; + +/** + * Folder Icon, an app folder in workspace. + */ +public class FolderIcon implements FolderDragTarget { + + protected final UiObject2 mObject; + protected final LauncherInstrumentation mLauncher; + + FolderIcon(LauncherInstrumentation launcher, UiObject2 icon) { + mObject = icon; + mLauncher = launcher; + } + + /** + * Open and return a folder or raise assertion error. + */ + @NonNull + public Folder open() { + try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck(); + LauncherInstrumentation.Closable c = mLauncher.addContextLayer("open folder")) { + mLauncher.executeAndWaitForLauncherEvent(() -> mLauncher.clickLauncherObject(mObject), + event -> TestProtocol.FOLDER_OPENED_MESSAGE.equals( + event.getClassName().toString()), + () -> "Fail to open folder.", + "open folder"); + } + return new Folder(mLauncher); + } + + @Override + public Rect getDropLocationBounds() { + return mLauncher.getVisibleBounds(mObject.getParent()); + } + + @Override + public FolderIcon getTargetFolder(Rect bounds) { + return this; + } +} diff --git a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java index 9ee0e25bd7..1e954e41d0 100644 --- a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java +++ b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java @@ -21,6 +21,7 @@ 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.launcher3.tapl.Folder.FOLDER_CONTENT_RES_ID; import static com.android.launcher3.tapl.TestHelpers.getOverviewPackageName; import static com.android.launcher3.testing.TestProtocol.NORMAL_STATE_ORDINAL; @@ -80,6 +81,7 @@ import java.util.Collections; import java.util.Deque; import java.util.LinkedList; import java.util.List; +import java.util.Optional; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import java.util.function.Consumer; @@ -767,6 +769,47 @@ public final class LauncherInstrumentation { } } + /** + * Get the resource ID of visible floating view. + */ + private Optional getFloatingResId() { + if (hasLauncherObject(CONTEXT_MENU_RES_ID)) { + return Optional.of(CONTEXT_MENU_RES_ID); + } + if (hasLauncherObject(FOLDER_CONTENT_RES_ID)) { + return Optional.of(FOLDER_CONTENT_RES_ID); + } + return Optional.empty(); + } + + /** + * Using swiping up gesture to dismiss closable floating views, such as Menu or Folder Content. + */ + private void swipeUpToCloseFloatingView(boolean gestureStartFromLauncher) { + final Point displaySize = getRealDisplaySize(); + + final Optional floatingRes = getFloatingResId(); + + if (!floatingRes.isPresent()) { + return; + } + + GestureScope gestureScope = gestureStartFromLauncher + ? (isTablet() ? GestureScope.INSIDE : GestureScope.INSIDE_TO_OUTSIDE) + : GestureScope.OUTSIDE_WITH_PILFER; + linearGesture( + displaySize.x / 2, displaySize.y - 1, + displaySize.x / 2, 0, + ZERO_BUTTON_STEPS_FROM_BACKGROUND_TO_HOME, + false, gestureScope); + + try (LauncherInstrumentation.Closable c1 = addContextLayer( + String.format("Swiped up from floating view %s to home", floatingRes.get()))) { + waitUntilLauncherObjectGone(floatingRes.get()); + waitForLauncherObject(getAnyObjectSelector()); + } + } + /** * Presses nav bar home button. * @@ -791,21 +834,9 @@ public final class LauncherInstrumentation { ? !isLauncher3() || hasLauncherObject(WORKSPACE_RES_ID) : isLauncherVisible(); - if (hasLauncherObject(CONTEXT_MENU_RES_ID)) { - GestureScope gestureScope = gestureStartFromLauncher - ? (isTablet() ? GestureScope.INSIDE : GestureScope.INSIDE_TO_OUTSIDE) - : GestureScope.OUTSIDE_WITH_PILFER; - linearGesture( - displaySize.x / 2, displaySize.y - 1, - displaySize.x / 2, 0, - ZERO_BUTTON_STEPS_FROM_BACKGROUND_TO_HOME, - false, gestureScope); - try (LauncherInstrumentation.Closable c1 = addContextLayer( - "Swiped up from context menu to home")) { - waitUntilLauncherObjectGone(CONTEXT_MENU_RES_ID); - waitForLauncherObject(getAnyObjectSelector()); - } - } + // CLose floating views before going back to home. + swipeUpToCloseFloatingView(gestureStartFromLauncher); + if (hasLauncherObject(WORKSPACE_RES_ID)) { log(action = "already at home"); } else { diff --git a/tests/tapl/com/android/launcher3/tapl/Workspace.java b/tests/tapl/com/android/launcher3/tapl/Workspace.java index 288c85319b..0145690909 100644 --- a/tests/tapl/com/android/launcher3/tapl/Workspace.java +++ b/tests/tapl/com/android/launcher3/tapl/Workspace.java @@ -36,7 +36,10 @@ import androidx.test.uiautomator.UiObject2; import com.android.launcher3.testing.TestProtocol; +import java.util.List; +import java.util.function.Supplier; import java.util.regex.Pattern; +import java.util.stream.Collectors; /** * Operations on the workspace screen. @@ -170,40 +173,100 @@ public final class Workspace extends Home { mHotseat, AppIcon.getAppIconSelector(appName, mLauncher))); } - static void dragIconToWorkspace( - LauncherInstrumentation launcher, Launchable launchable, Point dest, - String longPressIndicator, boolean startsActivity, boolean isWidgetShortcut, - Runnable expectLongClickEvents) { - LauncherInstrumentation.log("dragIconToWorkspace: begin"); - final Point launchableCenter = launchable.getObject().getVisibleCenter(); - final long downTime = SystemClock.uptimeMillis(); - launcher.runToState( - () -> { - launcher.sendPointer(downTime, downTime, MotionEvent.ACTION_DOWN, - launchableCenter, LauncherInstrumentation.GestureScope.INSIDE); - LauncherInstrumentation.log("dragIconToWorkspace: sent down"); - expectLongClickEvents.run(); - launcher.waitForLauncherObject(longPressIndicator); - LauncherInstrumentation.log("dragIconToWorkspace: indicator"); - launcher.movePointer(launchableCenter, dest, 10, downTime, true, - LauncherInstrumentation.GestureScope.INSIDE); - }, - SPRING_LOADED_STATE_ORDINAL, - "long-pressing and moving"); - LauncherInstrumentation.log("dragIconToWorkspace: moved pointer"); + private static int getStartDragThreshold(LauncherInstrumentation launcher) { + return launcher.getTestInfo(TestProtocol.REQUEST_START_DRAG_THRESHOLD).getInt( + TestProtocol.TEST_INFO_RESPONSE_FIELD); + } + + /** + * Finds folder icons in the current workspace. + * + * @return a list of folder icons. + */ + List getFolderIcons() { + final UiObject2 workspace = verifyActiveContainer(); + return mLauncher.getObjectsInContainer(workspace, "folder_icon_name").stream().map( + o -> new FolderIcon(mLauncher, o)).collect(Collectors.toList()); + } + + /** + * Drag an icon up with a short distance that makes workspace go to spring loaded state. + * + * @return the position after dragging. + */ + private static Point dragIconToSpringLoaded(LauncherInstrumentation launcher, long downTime, + UiObject2 icon, + String longPressIndicator, Runnable expectLongClickEvents) { + final Point iconCenter = icon.getVisibleCenter(); + final Point dragStartCenter = new Point(iconCenter.x, + iconCenter.y - getStartDragThreshold(launcher)); + + launcher.runToState(() -> { + launcher.sendPointer(downTime, downTime, MotionEvent.ACTION_DOWN, + iconCenter, LauncherInstrumentation.GestureScope.INSIDE); + LauncherInstrumentation.log("dragIconToSpringLoaded: sent down"); + expectLongClickEvents.run(); + launcher.waitForLauncherObject(longPressIndicator); + LauncherInstrumentation.log("dragIconToSpringLoaded: indicator"); + launcher.movePointer(iconCenter, dragStartCenter, 10, downTime, true, + LauncherInstrumentation.GestureScope.INSIDE); + }, SPRING_LOADED_STATE_ORDINAL, "long-pressing and triggering drag start"); + return dragStartCenter; + } + + private static void dropDraggedIcon(LauncherInstrumentation launcher, Point dest, long downTime, + @Nullable Runnable expectedEvents) { launcher.runToState( () -> launcher.sendPointer( downTime, SystemClock.uptimeMillis(), MotionEvent.ACTION_UP, dest, LauncherInstrumentation.GestureScope.INSIDE), NORMAL_STATE_ORDINAL, "sending UP event"); - if (startsActivity || isWidgetShortcut) { - launcher.expectEvent(TestProtocol.SEQUENCE_MAIN, LauncherInstrumentation.EVENT_START); + if (expectedEvents != null) { + expectedEvents.run(); } - LauncherInstrumentation.log("dragIconToWorkspace: end"); + LauncherInstrumentation.log("dropIcon: end"); launcher.waitUntilLauncherObjectGone("drop_target_bar"); } + static void dragIconToWorkspace(LauncherInstrumentation launcher, Launchable launchable, + Point dest, String longPressIndicator, boolean startsActivity, boolean isWidgetShortcut, + Runnable expectLongClickEvents) { + Runnable expectDropEvents = null; + if (startsActivity || isWidgetShortcut) { + expectDropEvents = () -> launcher.expectEvent(TestProtocol.SEQUENCE_MAIN, + LauncherInstrumentation.EVENT_START); + } + dragIconToWorkspace(launcher, launchable, () -> dest, longPressIndicator, + expectLongClickEvents, expectDropEvents); + } + + /** + * Drag icon in workspace to else where. + * This function expects the launchable is inside the workspace and there is no drop event. + */ + static void dragIconToWorkspace(LauncherInstrumentation launcher, Launchable launchable, + Supplier destSupplier, String longPressIndicator) { + dragIconToWorkspace(launcher, launchable, destSupplier, longPressIndicator, + () -> launcher.expectEvent(TestProtocol.SEQUENCE_MAIN, LONG_CLICK_EVENT), null); + } + + static void dragIconToWorkspace( + LauncherInstrumentation launcher, Launchable launchable, Supplier dest, + String longPressIndicator, Runnable expectLongClickEvents, + @Nullable Runnable expectDropEvents) { + try (LauncherInstrumentation.Closable ignored = launcher.addContextLayer( + "want to drag icon to workspace")) { + final long downTime = SystemClock.uptimeMillis(); + final Point dragStartCenter = dragIconToSpringLoaded(launcher, downTime, + launchable.getObject(), longPressIndicator, expectLongClickEvents); + final Point targetDest = dest.get(); + launcher.movePointer(dragStartCenter, targetDest, 10, downTime, true, + LauncherInstrumentation.GestureScope.INSIDE); + dropDraggedIcon(launcher, targetDest, downTime, expectDropEvents); + } + } + /** * Flings to get to screens on the right. Waits for scrolling and a possible overscroll * recoil to complete. From 2b74e3c290067139c9012b25f2a874bf7c8353d9 Mon Sep 17 00:00:00 2001 From: Bill Lin Date: Mon, 25 Oct 2021 14:40:51 +0800 Subject: [PATCH 2/9] Allow One-handed gesture when densityDpi > 600 This logic was aim to prevent tablet device enable One-handed gesture so added displayInfo.densityDpi < DisplayMetrics.DENSITY_600 before. However, it seems this densityDpi=600 does not able to represent to tablet device, instead we already have system property "ro.support_one_handed_mode" which provides feasibility to config false on tablet project, as the reason we can safe to remove this condition. Bug: 203936659 Test: adb shell wm density 600 , and observe OHM gesture is available Test: atest WMShellUnitTests Change-Id: Ic7ae10e8a47d26b9bb39ab80e22d591d74f89ae5 Merged-In: Ic7ae10e8a47d26b9bb39ab80e22d591d74f89ae5 --- .../com/android/quickstep/RecentsAnimationDeviceState.java | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java b/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java index 444d77a612..fde6902381 100644 --- a/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java +++ b/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java @@ -58,8 +58,6 @@ import android.os.SystemProperties; import android.os.UserManager; import android.provider.Settings; import android.text.TextUtils; -import android.util.DisplayMetrics; -import android.util.Log; import android.view.MotionEvent; import android.view.Surface; @@ -67,7 +65,6 @@ import androidx.annotation.BinderThread; import com.android.launcher3.R; import com.android.launcher3.Utilities; -import com.android.launcher3.testing.TestProtocol; import com.android.launcher3.util.DisplayController; import com.android.launcher3.util.DisplayController.DisplayInfoChangeListener; import com.android.launcher3.util.DisplayController.Info; @@ -591,8 +588,7 @@ public class RecentsAnimationDeviceState implements final Info displayInfo = mDisplayController.getInfo(); return (mRotationTouchHelper.touchInOneHandedModeRegion(ev) && displayInfo.rotation != Surface.ROTATION_90 - && displayInfo.rotation != Surface.ROTATION_270 - && displayInfo.densityDpi < DisplayMetrics.DENSITY_600); + && displayInfo.rotation != Surface.ROTATION_270); } return false; } From 05659a3d10c8b498592d85ccddd91c64fd927ac7 Mon Sep 17 00:00:00 2001 From: Alex Chau Date: Tue, 2 Nov 2021 13:11:05 +0000 Subject: [PATCH 3/9] Add logging of draw / DeviceProfile params for a crash Bug: 203530620 Test: manual Change-Id: I376157ee3f688b16a05b59a0093f06519533f6c7 --- .../widget/DeferredAppWidgetHostView.java | 22 +++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/src/com/android/launcher3/widget/DeferredAppWidgetHostView.java b/src/com/android/launcher3/widget/DeferredAppWidgetHostView.java index 9c32e421e6..57f8bc7717 100644 --- a/src/com/android/launcher3/widget/DeferredAppWidgetHostView.java +++ b/src/com/android/launcher3/widget/DeferredAppWidgetHostView.java @@ -16,6 +16,7 @@ package com.android.launcher3.widget; +import android.annotation.SuppressLint; import android.appwidget.AppWidgetProviderInfo; import android.content.Context; import android.graphics.Canvas; @@ -30,6 +31,9 @@ import android.widget.RemoteViews; import com.android.launcher3.R; +import java.io.PrintWriter; +import java.io.StringWriter; + /** * A widget host views created while the host has not bind to the system service. */ @@ -75,8 +79,22 @@ public class DeferredAppWidgetHostView extends LauncherAppWidgetHostView { && mSetupTextLayout.getWidth() == availableWidth) { return; } - mSetupTextLayout = new StaticLayout(info.label, mPaint, availableWidth, - Layout.Alignment.ALIGN_CENTER, 1, 0, true); + try { + mSetupTextLayout = new StaticLayout(info.label, mPaint, availableWidth, + Layout.Alignment.ALIGN_CENTER, 1, 0, true); + } catch (IllegalArgumentException e) { + @SuppressLint("DrawAllocation") StringWriter stringWriter = new StringWriter(); + @SuppressLint("DrawAllocation") PrintWriter printWriter = new PrintWriter(stringWriter); + mActivity.getDeviceProfile().dump(/*prefix=*/"", printWriter); + printWriter.flush(); + String message = "b/203530620 " + + "- availableWidth: " + availableWidth + + ", getMeasuredWidth: " + getMeasuredWidth() + + ", getPaddingLeft: " + getPaddingLeft() + + ", getPaddingRight: " + getPaddingRight() + + ", deviceProfile: " + stringWriter.toString(); + throw new IllegalArgumentException(message, e); + } } @Override From 08c3e8f52603b4228ec747435c5a3d464ecb4f35 Mon Sep 17 00:00:00 2001 From: Alina Zaidi Date: Mon, 1 Nov 2021 19:36:41 +0000 Subject: [PATCH 4/9] Use equals() to compare UserHandle when building LauncherAtom#ItemInfo. Bug: 204756057 Test: Manually verified correct behave through logs Change-Id: Ic314e38978893984bac89988bfc3229a21d0214e --- src/com/android/launcher3/model/data/ItemInfo.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/com/android/launcher3/model/data/ItemInfo.java b/src/com/android/launcher3/model/data/ItemInfo.java index 97398de3ff..b72f462a27 100644 --- a/src/com/android/launcher3/model/data/ItemInfo.java +++ b/src/com/android/launcher3/model/data/ItemInfo.java @@ -373,7 +373,7 @@ public class ItemInfo { protected LauncherAtom.ItemInfo.Builder getDefaultItemInfoBuilder() { LauncherAtom.ItemInfo.Builder itemBuilder = LauncherAtom.ItemInfo.newBuilder(); - itemBuilder.setIsWork(user != Process.myUserHandle()); + itemBuilder.setIsWork(!Process.myUserHandle().equals(user)); return itemBuilder; } From 21f6189cd5d3847f6a70c04c27904423c3c9c32c Mon Sep 17 00:00:00 2001 From: Zak Cohen Date: Tue, 2 Nov 2021 14:17:15 -0700 Subject: [PATCH 5/9] Add temporary logging to try and debug: b/202414125 Bug: 202414125 Test: Local Change-Id: I641f9877972f3f584e5fc618d89c65c52f0d1da0 --- quickstep/src/com/android/quickstep/SystemUiProxy.java | 3 +++ .../src/com/android/quickstep/util/ImageActionUtils.java | 4 ++++ src/com/android/launcher3/testing/TestProtocol.java | 1 + 3 files changed, 8 insertions(+) diff --git a/quickstep/src/com/android/quickstep/SystemUiProxy.java b/quickstep/src/com/android/quickstep/SystemUiProxy.java index d9319a9b00..b6f9d5846d 100644 --- a/quickstep/src/com/android/quickstep/SystemUiProxy.java +++ b/quickstep/src/com/android/quickstep/SystemUiProxy.java @@ -37,6 +37,7 @@ import android.view.RemoteAnimationAdapter; import android.view.RemoteAnimationTarget; import android.view.SurfaceControl; +import com.android.launcher3.testing.TestProtocol; import com.android.launcher3.util.MainThreadInitializedObject; import com.android.launcher3.util.SplitConfigurationOptions; import com.android.systemui.shared.recents.ISystemUiProxy; @@ -444,6 +445,8 @@ public class SystemUiProxy implements ISystemUiProxy, } catch (RemoteException e) { Log.w(TAG, "Failed call handleImageBundleAsScreenshot"); } + } else if (TestProtocol.sDebugTracing) { + Log.d(TestProtocol.NO_SCREENSHOT, "sysuiproxy, no proxy available"); } } diff --git a/quickstep/src/com/android/quickstep/util/ImageActionUtils.java b/quickstep/src/com/android/quickstep/util/ImageActionUtils.java index de7dbd64f5..b0c68c550e 100644 --- a/quickstep/src/com/android/quickstep/util/ImageActionUtils.java +++ b/quickstep/src/com/android/quickstep/util/ImageActionUtils.java @@ -49,6 +49,7 @@ import androidx.core.content.FileProvider; import com.android.internal.app.ChooserActivity; import com.android.launcher3.BuildConfig; +import com.android.launcher3.testing.TestProtocol; import com.android.quickstep.SystemUiProxy; import com.android.systemui.shared.recents.model.Task; import com.android.systemui.shared.recents.utilities.BitmapUtil; @@ -77,6 +78,9 @@ public class ImageActionUtils { public static void saveScreenshot(SystemUiProxy systemUiProxy, Bitmap screenshot, Rect screenshotBounds, Insets visibleInsets, Task.TaskKey task) { + if (TestProtocol.sDebugTracing) { + Log.d(TestProtocol.NO_SCREENSHOT, "image action utils calling into sysuiproxy"); + } systemUiProxy.handleImageBundleAsScreenshot(BitmapUtil.hardwareBitmapToBundle(screenshot), screenshotBounds, visibleInsets, task); } diff --git a/src/com/android/launcher3/testing/TestProtocol.java b/src/com/android/launcher3/testing/TestProtocol.java index 5bf0342fcc..8cb7f539fc 100644 --- a/src/com/android/launcher3/testing/TestProtocol.java +++ b/src/com/android/launcher3/testing/TestProtocol.java @@ -122,4 +122,5 @@ public final class TestProtocol { public static final String TASK_VIEW_ID_CRASH = "b/195430732"; public static final String NO_DROP_TARGET = "b/195031154"; public static final String NULL_INT_SET = "b/200572078"; + public static final String NO_SCREENSHOT = "b/202414125"; } From 3fabe054c229be7fffd759ca4b2c80242ea29b6e Mon Sep 17 00:00:00 2001 From: Vinit Nayak Date: Tue, 2 Nov 2021 22:08:33 +0000 Subject: [PATCH 6/9] Revert "Align OverviewActionsView for 3 button taskbar" This reverts commit 97d64ea8a1b681e44b757dd3ae9f6f28bcb55512. Reason for revert: b/204891006 Change-Id: I9d40a5c8c9bce6b818779ebaf748bb9bd9231caf --- .../taskbar/NavbarButtonsViewController.java | 16 ++++---- .../quickstep/views/OverviewActionsView.java | 38 ++----------------- .../android/quickstep/views/RecentsView.java | 2 +- 3 files changed, 12 insertions(+), 44 deletions(-) diff --git a/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java b/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java index 02caad561d..11349bba87 100644 --- a/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java +++ b/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java @@ -226,14 +226,6 @@ public class NavbarButtonsViewController { mPropertyHolders.forEach(StatePropertyHolder::endAnimation); } - public void onDestroy() { - mPropertyHolders.clear(); - mControllers.rotationButtonController.unregisterListeners(); - if (mFloatingRotationButton != null) { - mFloatingRotationButton.hide(); - } - } - private void initButtons(ViewGroup navContainer, ViewGroup endContainer, TaskbarNavButtonController navButtonController) { @@ -430,6 +422,14 @@ public class NavbarButtonsViewController { return mFloatingRotationButtonBounds.contains((int) ev.getX(), (int) ev.getY()); } + public void onDestroy() { + mPropertyHolders.clear(); + mControllers.rotationButtonController.unregisterListeners(); + if (mFloatingRotationButton != null) { + mFloatingRotationButton.hide(); + } + } + private class RotationButtonListener implements RotationButton.RotationButtonUpdatesCallback { @Override public void onVisibilityChanged(boolean isVisible) { diff --git a/quickstep/src/com/android/quickstep/views/OverviewActionsView.java b/quickstep/src/com/android/quickstep/views/OverviewActionsView.java index 16c3c7f9e9..76d3591325 100644 --- a/quickstep/src/com/android/quickstep/views/OverviewActionsView.java +++ b/quickstep/src/com/android/quickstep/views/OverviewActionsView.java @@ -16,8 +16,6 @@ package com.android.quickstep.views; -import static com.android.quickstep.SysUINavigationMode.Mode.THREE_BUTTONS; - import android.content.Context; import android.content.res.Configuration; import android.graphics.Rect; @@ -32,7 +30,6 @@ import androidx.annotation.Nullable; import com.android.launcher3.DeviceProfile; import com.android.launcher3.Insettable; import com.android.launcher3.R; -import com.android.launcher3.uioverrides.ApiWrapper; import com.android.launcher3.util.MultiValueAlpha; import com.android.launcher3.util.MultiValueAlpha.AlphaProperty; import com.android.quickstep.SysUINavigationMode; @@ -149,7 +146,7 @@ public class OverviewActionsView extends FrameLayo public void setInsets(Rect insets) { mInsets.set(insets); updateVerticalMargin(SysUINavigationMode.getMode(getContext())); - updatePaddingAndTranslations(); + updateHorizontalPadding(); } public void updateHiddenFlags(@ActionsHiddenFlags int visibilityFlags, boolean enable) { @@ -192,37 +189,8 @@ public class OverviewActionsView extends FrameLayo return mMultiValueAlpha.getProperty(INDEX_FULLSCREEN_ALPHA); } - /** - * Aligns OverviewActionsView vertically with and offsets horizontal position based on - * 3 button nav container in taskbar. - */ - private void updatePaddingAndTranslations() { - boolean alignFor3ButtonTaskbar = mDp.isTaskbarPresent && - SysUINavigationMode.getMode(getContext()) == THREE_BUTTONS; - if (alignFor3ButtonTaskbar) { - // Add extra horizontal spacing - int additionalPadding = ApiWrapper.getHotseatEndOffset(getContext()); - if (isLayoutRtl()) { - setPadding(mInsets.left + additionalPadding, 0, mInsets.right, 0); - } else { - setPadding(mInsets.left, 0, mInsets.right + additionalPadding, 0); - } - - // Align vertically, using taskbar height + mDp.taskbarOffsetY() to guestimate - // where the button nav top is - View startActionView = findViewById(R.id.action_buttons); - int marginBottom = getOverviewActionsBottomMarginPx( - SysUINavigationMode.getMode(getContext()), mDp); - int actionsTop = (mDp.heightPx - marginBottom - mInsets.bottom); - int navTop = mDp.heightPx - (mDp.taskbarSize + mDp.getTaskbarOffsetY()); - int transY = navTop - actionsTop - + ((mDp.taskbarSize - startActionView.getHeight()) / 2); - setTranslationY(transY); - } else { - setPadding(mInsets.left, 0, mInsets.right, 0); - setTranslationX(0); - setTranslationY(0); - } + private void updateHorizontalPadding() { + setPadding(mInsets.left, 0, mInsets.right, 0); } /** Updates vertical margins for different navigation mode or configuration changes. */ diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java index d825fada57..1a3bfa9958 100644 --- a/quickstep/src/com/android/quickstep/views/RecentsView.java +++ b/quickstep/src/com/android/quickstep/views/RecentsView.java @@ -1094,7 +1094,7 @@ public abstract class RecentsView Date: Fri, 8 Oct 2021 05:31:33 +0000 Subject: [PATCH 7/9] Implement pressing back button and swiping back This CL implements a pressBack function to press back button or swiping back in gesture navigation mode. Bug: 199120092 Test: atest -c Launcher3Tests:com.android.launcher3.ui.TaplTestsLauncher3#testPressBack && atest -c NexusLauncherTests:com.android.quickstep.TaplTestsQuickstep#testPressBack Change-Id: I001cea17d09ae1ab7952d04ee394a2afa5bf1e67 --- .../android/quickstep/TaplTestsQuickstep.java | 28 +++++++++++ .../launcher3/ui/AbstractLauncherUiTest.java | 3 +- .../launcher3/ui/TaplTestsLauncher3.java | 21 ++++++++ .../tapl/LauncherInstrumentation.java | 49 ++++++++++++++++++- 4 files changed, 99 insertions(+), 2 deletions(-) diff --git a/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java b/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java index 4895b107e1..437a19b1f1 100644 --- a/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java +++ b/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java @@ -31,6 +31,7 @@ import androidx.test.uiautomator.Until; import com.android.launcher3.Launcher; import com.android.launcher3.LauncherState; +import com.android.launcher3.tapl.AllApps; import com.android.launcher3.tapl.Background; import com.android.launcher3.tapl.LauncherInstrumentation.NavigationModel; import com.android.launcher3.tapl.Overview; @@ -49,6 +50,9 @@ import org.junit.runner.RunWith; @LargeTest @RunWith(AndroidJUnit4.class) public class TaplTestsQuickstep extends AbstractQuickStepTest { + + private static final String APP_NAME = "LauncherTestApp"; + @Before public void setUp() throws Exception { super.setUp(); @@ -286,6 +290,30 @@ public class TaplTestsQuickstep extends AbstractQuickStepTest { getAndAssertBackground(); } + // TODO(b/204830798): test with all navigation modes(add @NavigationModeSwitch annotation) + // after the bug resolved. + @Test + @PortraitLandscape + @ScreenRecord + public void testPressBack() throws Exception { + mLauncher.getWorkspace().switchToAllApps(); + mLauncher.pressBack(); + mLauncher.getWorkspace(); + waitForState("Launcher internal state didn't switch to Home", () -> LauncherState.NORMAL); + + AllApps allApps = mLauncher.getWorkspace().switchToAllApps(); + allApps.freeze(); + try { + allApps.getAppIcon(APP_NAME).dragToWorkspace(false, false); + } finally { + allApps.unfreeze(); + } + mLauncher.getWorkspace().getWorkspaceAppIcon(APP_NAME).launch(getAppPackageName()); + mLauncher.pressBack(); + mLauncher.getWorkspace(); + waitForState("Launcher internal state didn't switch to Home", () -> LauncherState.NORMAL); + } + @Test @PortraitLandscape public void testOverviewForTablet() throws Exception { diff --git a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java index 0ffbeeb95e..82163cbfdb 100644 --- a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java +++ b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java @@ -211,7 +211,8 @@ public abstract class AbstractLauncherUiTest { } protected TestRule getRulesInsideActivityMonitor() { - final RuleChain inner = RuleChain.outerRule(new PortraitLandscapeRunner(this)) + final RuleChain inner = RuleChain + .outerRule(new PortraitLandscapeRunner(this)) .around(new FailureWatcher(mDevice, mLauncher)); return TestHelpers.isInLauncherProcess() diff --git a/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java b/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java index 2fa84b2430..4007c26f54 100644 --- a/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java +++ b/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java @@ -413,6 +413,27 @@ public class TaplTestsLauncher3 extends AbstractLauncherUiTest { folder.close(); } + @Test + @PortraitLandscape + public void testPressBack() throws Exception { + mLauncher.getWorkspace().switchToAllApps(); + mLauncher.pressBack(); + mLauncher.getWorkspace(); + waitForState("Launcher internal state didn't switch to Home", () -> LauncherState.NORMAL); + + AllApps allApps = mLauncher.getWorkspace().switchToAllApps(); + allApps.freeze(); + try { + allApps.getAppIcon(APP_NAME).dragToWorkspace(false, false); + } finally { + allApps.unfreeze(); + } + mLauncher.getWorkspace().getWorkspaceAppIcon(APP_NAME).launch(getAppPackageName()); + mLauncher.pressBack(); + mLauncher.getWorkspace(); + waitForState("Launcher internal state didn't switch to Home", () -> LauncherState.NORMAL); + } + public static String getAppPackageName() { return getInstrumentation().getContext().getPackageName(); } diff --git a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java index 3ac5fa5bbd..e2d023825c 100644 --- a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java +++ b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java @@ -76,6 +76,7 @@ import java.io.ByteArrayOutputStream; import java.io.IOException; import java.lang.ref.WeakReference; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.Deque; @@ -110,6 +111,9 @@ public final class LauncherInstrumentation { static final Pattern EVENT_TOUCH_DOWN_TIS = getTouchEventPatternTIS("ACTION_DOWN"); static final Pattern EVENT_TOUCH_UP_TIS = getTouchEventPatternTIS("ACTION_UP"); + static final Pattern EVENT_KEY_BACK_DOWN = getKeyEventPattern("ACTION_DOWN", "KEYCODE_BACK"); + static final Pattern EVENT_KEY_BACK_UP = getKeyEventPattern("ACTION_UP", "KEYCODE_BACK"); + private final String mLauncherPackage; private Boolean mIsLauncher3; private long mTestStartTime = -1; @@ -125,7 +129,8 @@ public final class LauncherInstrumentation { // Where the gesture happens: outside of Launcher, inside or from inside to outside and // whether the gesture recognition triggers pilfer. public enum GestureScope { - OUTSIDE_WITHOUT_PILFER, OUTSIDE_WITH_PILFER, INSIDE, INSIDE_TO_OUTSIDE + OUTSIDE_WITHOUT_PILFER, OUTSIDE_WITH_PILFER, INSIDE, INSIDE_TO_OUTSIDE, + INSIDE_TO_OUTSIDE_WITHOUT_PILFER, } // Base class for launcher containers. @@ -195,6 +200,10 @@ public final class LauncherInstrumentation { return getTouchEventPattern("TouchInteractionService.onInputEvent", action); } + private static Pattern getKeyEventPattern(String action, String keyCode) { + return Pattern.compile("Key event: KeyEvent.*action=" + action + ".*keyCode=" + keyCode); + } + /** * Constructs the root of TAPL hierarchy. You get all other objects from it. */ @@ -879,6 +888,38 @@ public final class LauncherInstrumentation { } } + /** + * Press navbar back button or swipe back if in gesture navigation mode. + */ + public void pressBack() { + try (Closable e = eventsCheck(); Closable c = addContextLayer("want to press back")) { + waitForLauncherInitialized(); + final boolean launcherVisible = + isTablet() ? isLauncherContainerVisible() : isLauncherVisible(); + if (getNavigationModel() == NavigationModel.ZERO_BUTTON) { + final Point displaySize = getRealDisplaySize(); + final GestureScope gestureScope = + launcherVisible ? GestureScope.INSIDE_TO_OUTSIDE_WITHOUT_PILFER + : GestureScope.OUTSIDE_WITHOUT_PILFER; + linearGesture(0, displaySize.y / 2, displaySize.x / 2, displaySize.y / 2, + 10, false, gestureScope); + } else { + waitForNavigationUiObject("back").click(); + if (isTablet()) { + expectEvent(TestProtocol.SEQUENCE_MAIN, EVENT_TOUCH_DOWN); + expectEvent(TestProtocol.SEQUENCE_MAIN, EVENT_TOUCH_UP); + } else if (!isLauncher3() && getNavigationModel() == NavigationModel.TWO_BUTTON) { + expectEvent(TestProtocol.SEQUENCE_TIS, EVENT_TOUCH_DOWN_TIS); + expectEvent(TestProtocol.SEQUENCE_TIS, EVENT_TOUCH_UP_TIS); + } + } + if (launcherVisible) { + expectEvent(TestProtocol.SEQUENCE_MAIN, EVENT_KEY_BACK_DOWN); + expectEvent(TestProtocol.SEQUENCE_MAIN, EVENT_KEY_BACK_UP); + } + } + } + private static BySelector getAnyObjectSelector() { return By.textStartsWith(""); } @@ -888,6 +929,11 @@ public final class LauncherInstrumentation { return hasLauncherObject(getAnyObjectSelector()); } + boolean isLauncherContainerVisible() { + final String[] containerResources = {WORKSPACE_RES_ID, OVERVIEW_RES_ID, APPS_RES_ID}; + return Arrays.stream(containerResources).anyMatch(r -> hasLauncherObject(r)); + } + /** * Gets the Workspace object if the current state is "active home", i.e. workspace. Fails if the * launcher is not in that state. @@ -1414,6 +1460,7 @@ public final class LauncherInstrumentation { break; case MotionEvent.ACTION_UP: if (notLauncher3 && gestureScope != GestureScope.INSIDE + && gestureScope != GestureScope.INSIDE_TO_OUTSIDE_WITHOUT_PILFER && (gestureScope == GestureScope.OUTSIDE_WITH_PILFER || gestureScope == GestureScope.INSIDE_TO_OUTSIDE)) { expectEvent(TestProtocol.SEQUENCE_PILFER, EVENT_PILFER_POINTERS); From 571df892f700019652d9acf272abad2c63886dd6 Mon Sep 17 00:00:00 2001 From: ryanlwlin Date: Thu, 7 Oct 2021 21:54:01 +0800 Subject: [PATCH 8/9] DO NOT MERGE Fix the cutout of magnification border If the window is unmagnifiable, it might cause the cutout of magnification region. To fix it, we set the flag when the touchable region is not the entier window frame. Bug: 196510717 Test: manual test on gestural and 3-button navigation Change-Id: Ida2bb5bf120038ac9153e12790b93bdec195adbc --- .../taskbar/TaskbarActivityContext.java | 16 ++++++++++++++++ .../taskbar/TaskbarDragLayerController.java | 4 ++++ 2 files changed, 20 insertions(+) diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java index 72d9d5b0b1..ae647dbcaa 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java @@ -510,4 +510,20 @@ public class TaskbarActivityContext extends ContextThemeWrapper implements Activ protected boolean isUserSetupComplete() { return mIsUserSetupComplete; } + + /** + * Called when we determine the touchable region. + * + * @param exclude {@code true} then the magnification region computation will omit the window. + */ + public void excludeFromMagnificationRegion(boolean exclude) { + if (exclude) { + mWindowLayoutParams.privateFlags |= + WindowManager.LayoutParams.PRIVATE_FLAG_EXCLUDE_FROM_SCREEN_MAGNIFICATION; + } else { + mWindowLayoutParams.privateFlags &= + ~WindowManager.LayoutParams.PRIVATE_FLAG_EXCLUDE_FROM_SCREEN_MAGNIFICATION; + } + mWindowManager.updateViewLayout(mDragLayer, mWindowLayoutParams); + } } diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayerController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayerController.java index cec892f7a7..8c6185cb0f 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayerController.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayerController.java @@ -137,12 +137,14 @@ public class TaskbarDragLayerController { // Always have nav buttons be touchable mControllers.navbarButtonsViewController.addVisibleButtonsRegion( mTaskbarDragLayer, insetsInfo.touchableRegion); + boolean insetsIsTouchableRegion = true; if (mTaskbarDragLayer.getAlpha() < AlphaUpdateListener.ALPHA_CUTOFF_THRESHOLD) { // Let touches pass through us. insetsInfo.setTouchableInsets(TOUCHABLE_INSETS_REGION); } else if (mControllers.navbarButtonsViewController.isImeVisible()) { insetsInfo.setTouchableInsets(TOUCHABLE_INSETS_CONTENT); + insetsIsTouchableRegion = false; } else if (!mControllers.uiController.isTaskbarTouchable()) { // Let touches pass through us. insetsInfo.setTouchableInsets(TOUCHABLE_INSETS_REGION); @@ -151,9 +153,11 @@ public class TaskbarDragLayerController { // Taskbar has some touchable elements, take over the full taskbar area insetsInfo.setTouchableInsets(mActivity.isTaskbarWindowFullscreen() ? TOUCHABLE_INSETS_FRAME : TOUCHABLE_INSETS_CONTENT); + insetsIsTouchableRegion = false; } else { insetsInfo.setTouchableInsets(TOUCHABLE_INSETS_REGION); } + mActivity.excludeFromMagnificationRegion(insetsIsTouchableRegion); } /** From c91a43e110b96ad0e675f95e4d89cdae32262a74 Mon Sep 17 00:00:00 2001 From: Alex Chau Date: Wed, 3 Nov 2021 14:19:23 +0000 Subject: [PATCH 9/9] Revert "DO NOT MERGE Fix the cutout of magnification border" Revert "Do NOT MERGE Fix magnification border includes taskbar" Revert submission 16003793-magnification_border Bug: 196510717 Reason for revert: Caused NexusLauncherTests and NexusLauncherOutOfProcTests to stop running Reverted Changes: Ibbc9c51ea:Do NOT MERGE Fix magnification border includes tas... Ida2bb5bf1:DO NOT MERGE Fix the cutout of magnification borde... Change-Id: I6b2123aedd2a2f23142a34f158d2d9ab71948a18 --- .../taskbar/TaskbarActivityContext.java | 16 ---------------- .../taskbar/TaskbarDragLayerController.java | 4 ---- 2 files changed, 20 deletions(-) diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java index ae647dbcaa..72d9d5b0b1 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java @@ -510,20 +510,4 @@ public class TaskbarActivityContext extends ContextThemeWrapper implements Activ protected boolean isUserSetupComplete() { return mIsUserSetupComplete; } - - /** - * Called when we determine the touchable region. - * - * @param exclude {@code true} then the magnification region computation will omit the window. - */ - public void excludeFromMagnificationRegion(boolean exclude) { - if (exclude) { - mWindowLayoutParams.privateFlags |= - WindowManager.LayoutParams.PRIVATE_FLAG_EXCLUDE_FROM_SCREEN_MAGNIFICATION; - } else { - mWindowLayoutParams.privateFlags &= - ~WindowManager.LayoutParams.PRIVATE_FLAG_EXCLUDE_FROM_SCREEN_MAGNIFICATION; - } - mWindowManager.updateViewLayout(mDragLayer, mWindowLayoutParams); - } } diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayerController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayerController.java index 8c6185cb0f..cec892f7a7 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayerController.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayerController.java @@ -137,14 +137,12 @@ public class TaskbarDragLayerController { // Always have nav buttons be touchable mControllers.navbarButtonsViewController.addVisibleButtonsRegion( mTaskbarDragLayer, insetsInfo.touchableRegion); - boolean insetsIsTouchableRegion = true; if (mTaskbarDragLayer.getAlpha() < AlphaUpdateListener.ALPHA_CUTOFF_THRESHOLD) { // Let touches pass through us. insetsInfo.setTouchableInsets(TOUCHABLE_INSETS_REGION); } else if (mControllers.navbarButtonsViewController.isImeVisible()) { insetsInfo.setTouchableInsets(TOUCHABLE_INSETS_CONTENT); - insetsIsTouchableRegion = false; } else if (!mControllers.uiController.isTaskbarTouchable()) { // Let touches pass through us. insetsInfo.setTouchableInsets(TOUCHABLE_INSETS_REGION); @@ -153,11 +151,9 @@ public class TaskbarDragLayerController { // Taskbar has some touchable elements, take over the full taskbar area insetsInfo.setTouchableInsets(mActivity.isTaskbarWindowFullscreen() ? TOUCHABLE_INSETS_FRAME : TOUCHABLE_INSETS_CONTENT); - insetsIsTouchableRegion = false; } else { insetsInfo.setTouchableInsets(TOUCHABLE_INSETS_REGION); } - mActivity.excludeFromMagnificationRegion(insetsIsTouchableRegion); } /**