diff --git a/quickstep/res/values/dimens.xml b/quickstep/res/values/dimens.xml index 959fea7572..4e8c3210c3 100644 --- a/quickstep/res/values/dimens.xml +++ b/quickstep/res/values/dimens.xml @@ -292,6 +292,8 @@ 108dp 316dp 4dp + 10dp + 1dp 112dp 88dp 40dp diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java index 7e0530bcc0..37f62841bf 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java @@ -928,6 +928,13 @@ public class TaskbarActivityContext extends BaseTaskbarContext { } } + /** + * Returns whether the taskbar is currently visually stashed. + */ + public boolean isTaskbarStashed() { + return mControllers.taskbarStashController.isStashed(); + } + /** * Called when we detect a long press in the nav region before passing the gesture slop. * @return Whether taskbar handled the long press, and thus should cancel the gesture. @@ -972,10 +979,23 @@ public class TaskbarActivityContext extends BaseTaskbarContext { /** * Called when we detect a motion down or up/cancel in the nav region while stashed. + * * @param animateForward Whether to animate towards the unstashed hint state or back to stashed. */ public void startTaskbarUnstashHint(boolean animateForward) { - mControllers.taskbarStashController.startUnstashHint(animateForward); + // TODO(b/270395798): Clean up forceUnstash after removing long-press unstashing code. + startTaskbarUnstashHint(animateForward, /* forceUnstash = */ false); + } + + /** + * Called when we detect a motion down or up/cancel in the nav region while stashed. + * + * @param animateForward Whether to animate towards the unstashed hint state or back to stashed. + * @param forceUnstash Whether we force the unstash hint. + */ + public void startTaskbarUnstashHint(boolean animateForward, boolean forceUnstash) { + // TODO(b/270395798): Clean up forceUnstash after removing long-press unstashing code. + mControllers.taskbarStashController.startUnstashHint(animateForward, forceUnstash); } /** @@ -1123,4 +1143,9 @@ public class TaskbarActivityContext extends BaseTaskbarContext { public int getTaskbarAllAppsScroll() { return mControllers.taskbarAllAppsController.getTaskbarAllAppsScroll(); } + + @VisibleForTesting + public float getStashedTaskbarScale() { + return mControllers.stashedHandleViewController.getStashedHandleHintScale().value; + } } diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java index b2f93787e0..5de5904638 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java @@ -856,15 +856,18 @@ public class TaskbarStashController implements TaskbarControllers.LoggableTaskba /** * Creates and starts a partial unstash animation, hinting at the new state that will trigger * when long press is detected. + * * @param animateForward Whether we are going towards the new unstashed state or returning to * the stashed state. + * @param forceUnstash Whether we force the unstash hint to animate. */ - public void startUnstashHint(boolean animateForward) { + protected void startUnstashHint(boolean animateForward, boolean forceUnstash) { if (!isStashed()) { // Already unstashed, no need to hint in that direction. return; } - if (!canCurrentlyManuallyUnstash()) { + // TODO(b/270395798): Clean up after removing long-press unstashing code path. + if (!canCurrentlyManuallyUnstash() && !forceUnstash) { // If any other flags are causing us to be stashed, long press won't cause us to // unstash, so don't hint that it will. return; diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashViaTouchController.kt b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashViaTouchController.kt index 2373142074..1cc667211c 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashViaTouchController.kt +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashViaTouchController.kt @@ -25,7 +25,7 @@ import com.android.launcher3.touch.SingleAxisSwipeDetector.DIRECTION_NEGATIVE import com.android.launcher3.touch.SingleAxisSwipeDetector.VERTICAL import com.android.launcher3.util.DisplayController import com.android.launcher3.util.TouchController -import com.android.quickstep.inputconsumers.TaskbarStashInputConsumer +import com.android.quickstep.inputconsumers.TaskbarUnstashInputConsumer /** * A helper [TouchController] for [TaskbarDragLayerController], specifically to handle touch events @@ -34,7 +34,7 @@ import com.android.quickstep.inputconsumers.TaskbarStashInputConsumer * or [MotionEvent.ACTION_OUTSIDE]. * - Touches inside Transient Taskbar bounds will stash if it is detected as a swipe down gesture. * - * Note: touches to *unstash* Taskbar are handled by [TaskbarStashInputConsumer]. + * Note: touches to *unstash* Taskbar are handled by [TaskbarUnstashInputConsumer]. */ class TaskbarStashViaTouchController(val controllers: TaskbarControllers) : TouchController { diff --git a/quickstep/src/com/android/quickstep/InputConsumer.java b/quickstep/src/com/android/quickstep/InputConsumer.java index 64c92959e4..6b189cf4d6 100644 --- a/quickstep/src/com/android/quickstep/InputConsumer.java +++ b/quickstep/src/com/android/quickstep/InputConsumer.java @@ -41,6 +41,7 @@ public interface InputConsumer { int TYPE_ONE_HANDED = 1 << 11; int TYPE_TASKBAR_STASH = 1 << 12; int TYPE_STATUS_BAR = 1 << 13; + int TYPE_CURSOR_HOVER = 1 << 14; String[] NAMES = new String[] { "TYPE_NO_OP", // 0 @@ -57,6 +58,7 @@ public interface InputConsumer { "TYPE_ONE_HANDED", // 11 "TYPE_TASKBAR_STASH", // 12 "TYPE_STATUS_BAR", // 13 + "TYPE_CURSOR_HOVER", // 14 }; InputConsumer NO_OP = () -> TYPE_NO_OP; diff --git a/quickstep/src/com/android/quickstep/QuickstepTestInformationHandler.java b/quickstep/src/com/android/quickstep/QuickstepTestInformationHandler.java index 4e892e2722..8247035e9c 100644 --- a/quickstep/src/com/android/quickstep/QuickstepTestInformationHandler.java +++ b/quickstep/src/com/android/quickstep/QuickstepTestInformationHandler.java @@ -116,6 +116,16 @@ public class QuickstepTestInformationHandler extends TestInformationHandler { return response; } + case TestProtocol.REQUEST_STASHED_TASKBAR_SCALE: { + runOnTISBinder(tisBinder -> { + response.putFloat(TestProtocol.TEST_INFO_RESPONSE_FIELD, + tisBinder.getTaskbarManager() + .getCurrentActivityContext() + .getStashedTaskbarScale()); + }); + return response; + } + case TestProtocol.REQUEST_TASKBAR_ALL_APPS_TOP_PADDING: { return getTISBinderUIProperty(Bundle::putInt, tisBinder -> tisBinder.getTaskbarManager() diff --git a/quickstep/src/com/android/quickstep/TouchInteractionService.java b/quickstep/src/com/android/quickstep/TouchInteractionService.java index 038c6743dd..8246d262a6 100644 --- a/quickstep/src/com/android/quickstep/TouchInteractionService.java +++ b/quickstep/src/com/android/quickstep/TouchInteractionService.java @@ -29,6 +29,7 @@ import static com.android.launcher3.config.FeatureFlags.ENABLE_TRACKPAD_GESTURE; import static com.android.launcher3.util.Executors.MAIN_EXECUTOR; import static com.android.quickstep.GestureState.DEFAULT_STATE; import static com.android.quickstep.GestureState.TrackpadGestureType.getTrackpadGestureType; +import static com.android.quickstep.InputConsumer.TYPE_CURSOR_HOVER; import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.FLAG_USING_OTHER_ACTIVITY_INPUT_CONSUMER; import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.MOTION_DOWN; import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.MOTION_MOVE; @@ -109,7 +110,7 @@ import com.android.quickstep.inputconsumers.ResetGestureInputConsumer; import com.android.quickstep.inputconsumers.ScreenPinnedInputConsumer; import com.android.quickstep.inputconsumers.StatusBarInputConsumer; import com.android.quickstep.inputconsumers.SysUiOverlayInputConsumer; -import com.android.quickstep.inputconsumers.TaskbarStashInputConsumer; +import com.android.quickstep.inputconsumers.TaskbarUnstashInputConsumer; import com.android.quickstep.util.ActiveGestureLog; import com.android.quickstep.util.ActiveGestureLog.CompoundString; import com.android.quickstep.util.ProtoTracer; @@ -637,12 +638,17 @@ public class TouchInteractionService extends Service TraceHelper.FLAG_ALLOW_BINDER_TRACKING); final int action = event.getActionMasked(); - if (action == ACTION_DOWN) { + // Note this will create a new consumer every mouse click, as after ACTION_UP from the click + // an ACTION_HOVER_ENTER will fire as well. + boolean isHoverActionWithoutConsumer = + event.isHoverEvent() && (mUncheckedConsumer.getType() & TYPE_CURSOR_HOVER) == 0; + if (action == ACTION_DOWN || isHoverActionWithoutConsumer) { mRotationTouchHelper.setOrientationTransformIfNeeded(event); - if (!mDeviceState.isOneHandedModeActive() + if ((!mDeviceState.isOneHandedModeActive() && mRotationTouchHelper.isInSwipeUpTouchRegion(event, - mOverviewComponentObserver.getActivityInterface())) { + mOverviewComponentObserver.getActivityInterface())) + || isHoverActionWithoutConsumer) { // Clone the previous gesture state since onConsumerAboutToBeSwitched might trigger // onConsumerInactive and wipe the previous gesture state GestureState prevGestureState = new GestureState(mGestureState); @@ -719,6 +725,8 @@ public class TouchInteractionService extends Service if (action == ACTION_POINTER_DOWN) { mGestureState.setTrackpadGestureType(getTrackpadGestureType(event)); } + } else if (event.isHoverEvent()) { + mUncheckedConsumer.onHoverEvent(event); } else { mUncheckedConsumer.onMotionEvent(event); } @@ -842,7 +850,7 @@ public class TouchInteractionService extends Service base = tryCreateAssistantInputConsumer(base, newGestureState, event, reasonString); } - // If Taskbar is present, we listen for long press to unstash it. + // If Taskbar is present, we listen for long press or cursor hover events to unstash it. TaskbarActivityContext tac = mTaskbarManager.getCurrentActivityContext(); if (tac != null) { // Present always on large screen or on small screen w/ flag @@ -853,8 +861,8 @@ public class TouchInteractionService extends Service .append(reasonPrefix) .append(SUBSTRING_PREFIX) .append("TaskbarActivityContext != null, " - + "using TaskbarStashInputConsumer"); - base = new TaskbarStashInputConsumer(this, base, mInputMonitorCompat, tac); + + "using TaskbarUnstashInputConsumer"); + base = new TaskbarUnstashInputConsumer(this, base, mInputMonitorCompat, tac); } } diff --git a/quickstep/src/com/android/quickstep/inputconsumers/TaskbarStashInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/TaskbarUnstashInputConsumer.java similarity index 69% rename from quickstep/src/com/android/quickstep/inputconsumers/TaskbarStashInputConsumer.java rename to quickstep/src/com/android/quickstep/inputconsumers/TaskbarUnstashInputConsumer.java index 51c2b4829a..65c825c0f6 100644 --- a/quickstep/src/com/android/quickstep/inputconsumers/TaskbarStashInputConsumer.java +++ b/quickstep/src/com/android/quickstep/inputconsumers/TaskbarUnstashInputConsumer.java @@ -19,17 +19,20 @@ import static android.view.MotionEvent.INVALID_POINTER_ID; import static com.android.launcher3.MotionEventsUtils.isTrackpadMotionEvent; import static com.android.launcher3.Utilities.squaredHypot; +import static com.android.launcher3.config.FeatureFlags.ENABLE_CURSOR_HOVER_STATES; import static com.android.launcher3.taskbar.TaskbarAutohideSuspendController.FLAG_AUTOHIDE_SUSPEND_TOUCHING; import android.content.Context; import android.content.res.Resources; import android.graphics.PointF; +import android.graphics.Rect; import android.view.GestureDetector; import android.view.GestureDetector.SimpleOnGestureListener; import android.view.MotionEvent; import androidx.annotation.Nullable; +import com.android.launcher3.DeviceProfile; import com.android.launcher3.R; import com.android.launcher3.Utilities; import com.android.launcher3.taskbar.TaskbarActivityContext; @@ -40,9 +43,11 @@ import com.android.quickstep.InputConsumer; import com.android.systemui.shared.system.InputMonitorCompat; /** - * Listens for a long press, and cancels the current gesture if that causes Taskbar to be unstashed. + * Listens for touch and hover events to unstash the Taskbar. + * + *

Cancels the current gesture if the long press causes the Taskbar to be unstashed. */ -public class TaskbarStashInputConsumer extends DelegateInputConsumer { +public class TaskbarUnstashInputConsumer extends DelegateInputConsumer { private final TaskbarActivityContext mTaskbarActivityContext; private final GestureDetector mLongPressDetector; @@ -64,9 +69,15 @@ public class TaskbarStashInputConsumer extends DelegateInputConsumer { private final boolean mIsTransientTaskbar; + private boolean mIsStashedTaskbarHovered = false; + private final Rect mStashedTaskbarHandleBounds = new Rect(); + private final Rect mBottomEdgeBounds = new Rect(); + private final int mBottomScreenEdge; + private final int mStashedTaskbarBottomEdge; + private final @Nullable TransitionCallback mTransitionCallback; - public TaskbarStashInputConsumer(Context context, InputConsumer delegate, + public TaskbarUnstashInputConsumer(Context context, InputConsumer delegate, InputMonitorCompat inputMonitor, TaskbarActivityContext taskbarActivityContext) { super(delegate, inputMonitor); mTaskbarActivityContext = taskbarActivityContext; @@ -90,6 +101,11 @@ public class TaskbarStashInputConsumer extends DelegateInputConsumer { } }); + mBottomScreenEdge = res.getDimensionPixelSize( + R.dimen.taskbar_stashed_screen_edge_hover_deadzone_height); + mStashedTaskbarBottomEdge = + res.getDimensionPixelSize(R.dimen.taskbar_stashed_below_hover_deadzone_height); + mTransitionCallback = mIsTransientTaskbar ? taskbarActivityContext.getTranslationCallbacks() : null; @@ -97,7 +113,7 @@ public class TaskbarStashInputConsumer extends DelegateInputConsumer { @Override public int getType() { - return TYPE_TASKBAR_STASH | mDelegate.getType(); + return TYPE_TASKBAR_STASH | TYPE_CURSOR_HOVER | mDelegate.getType(); } @Override @@ -213,4 +229,73 @@ public class TaskbarStashInputConsumer extends DelegateInputConsumer { } } } + + /** + * Listen for hover events for the stashed taskbar. + * + *

When hovered over the stashed taskbar handle, show the unstash hint. + *

When the cursor is touching the bottom edge below the stashed taskbar, unstash it. + *

When the cursor is within a defined threshold of the screen's bottom edge outside of + * the stashed taskbar, unstash it. + */ + @Override + public void onHoverEvent(MotionEvent ev) { + if (!ENABLE_CURSOR_HOVER_STATES.get() || mTaskbarActivityContext == null + || !mTaskbarActivityContext.isTaskbarStashed()) { + return; + } + + if (mIsStashedTaskbarHovered) { + updateHoveredTaskbarState((int) ev.getX(), (int) ev.getY()); + } else { + updateUnhoveredTaskbarState((int) ev.getX(), (int) ev.getY()); + } + } + + private void updateHoveredTaskbarState(int x, int y) { + DeviceProfile dp = mTaskbarActivityContext.getDeviceProfile(); + mStashedTaskbarHandleBounds.set( + (dp.widthPx - (int) mUnstashArea) / 2, + dp.heightPx - dp.stashedTaskbarHeight, + (int) (((dp.widthPx - mUnstashArea) / 2) + mUnstashArea), + dp.heightPx); + mBottomEdgeBounds.set(mStashedTaskbarHandleBounds); + mBottomEdgeBounds.top = dp.heightPx - mStashedTaskbarBottomEdge; + + if (mBottomEdgeBounds.contains(x, y)) { + // If hovering stashed taskbar and then hover screen bottom edge, unstash it. + mTaskbarActivityContext.onSwipeToUnstashTaskbar(); + mIsStashedTaskbarHovered = false; + } else if (!mStashedTaskbarHandleBounds.contains(x, y)) { + // If exit hovering stashed taskbar, remove hint. + startStashedTaskbarHover(/* isHovered = */ false); + } + } + + private void updateUnhoveredTaskbarState(int x, int y) { + DeviceProfile dp = mTaskbarActivityContext.getDeviceProfile(); + mStashedTaskbarHandleBounds.set( + (dp.widthPx - (int) mUnstashArea) / 2, + dp.heightPx - dp.stashedTaskbarHeight, + (int) (((dp.widthPx - mUnstashArea) / 2) + mUnstashArea), + dp.heightPx); + mBottomEdgeBounds.set( + 0, + dp.heightPx - mBottomScreenEdge, + dp.widthPx, + dp.heightPx); + + if (mStashedTaskbarHandleBounds.contains(x, y)) { + // If enter hovering stashed taskbar, start hint. + startStashedTaskbarHover(/* isHovered = */ true); + } else if (mBottomEdgeBounds.contains(x, y)) { + // If hover screen's bottom edge not below the stashed taskbar, unstash it. + mTaskbarActivityContext.onSwipeToUnstashTaskbar(); + } + } + + private void startStashedTaskbarHover(boolean isHovered) { + mTaskbarActivityContext.startTaskbarUnstashHint(isHovered, /* forceUnstash = */ true); + mIsStashedTaskbarHovered = isHovered; + } } diff --git a/quickstep/tests/src/com/android/quickstep/TaplTestsTaskbar.java b/quickstep/tests/src/com/android/quickstep/TaplTestsTaskbar.java index 4540eee936..39fe6b5aba 100644 --- a/quickstep/tests/src/com/android/quickstep/TaplTestsTaskbar.java +++ b/quickstep/tests/src/com/android/quickstep/TaplTestsTaskbar.java @@ -17,6 +17,7 @@ package com.android.quickstep; import static androidx.test.InstrumentationRegistry.getInstrumentation; +import static com.android.launcher3.config.FeatureFlags.ENABLE_CURSOR_HOVER_STATES; import static com.android.quickstep.TaskbarModeSwitchRule.Mode.PERSISTENT; import static com.android.quickstep.TaskbarModeSwitchRule.Mode.TRANSIENT; @@ -269,6 +270,39 @@ public class TaplTestsTaskbar extends AbstractQuickStepTest { .dragToSplitscreen(TEST_APP_PACKAGE, CALCULATOR_APP_PACKAGE); } + @Test + @TaskbarModeSwitch(mode = TRANSIENT) + public void testShowTaskbarUnstashHintOnHover() { + try (AutoCloseable flag = TestUtil.overrideFlag(ENABLE_CURSOR_HOVER_STATES, true)) { + getTaskbar().getAppIcon(TEST_APP_NAME).launch(TEST_APP_PACKAGE); + mLauncher.getLaunchedAppState().hoverToShowTaskbarUnstashHint(); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + @Test + @TaskbarModeSwitch(mode = TRANSIENT) + public void testUnstashTaskbarOnScreenBottomEdgeHover() { + try (AutoCloseable flag = TestUtil.overrideFlag(ENABLE_CURSOR_HOVER_STATES, true)) { + getTaskbar().getAppIcon(TEST_APP_NAME).launch(TEST_APP_PACKAGE); + mLauncher.getLaunchedAppState().hoverScreenBottomEdgeToUnstashTaskbar(); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + @Test + @TaskbarModeSwitch(mode = TRANSIENT) + public void testHoverBelowHintedTaskbarToUnstash() { + try (AutoCloseable flag = TestUtil.overrideFlag(ENABLE_CURSOR_HOVER_STATES, true)) { + getTaskbar().getAppIcon(TEST_APP_NAME).launch(TEST_APP_PACKAGE); + mLauncher.getLaunchedAppState().hoverBelowHintedTaskbarToUnstash(); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + private Taskbar getTaskbar() { Taskbar taskbar = mLauncher.getLaunchedAppState().getTaskbar(); List taskbarIconNames = taskbar.getIconNames(); diff --git a/tests/shared/com/android/launcher3/testing/shared/TestProtocol.java b/tests/shared/com/android/launcher3/testing/shared/TestProtocol.java index 36255b4628..2c3ea347d7 100644 --- a/tests/shared/com/android/launcher3/testing/shared/TestProtocol.java +++ b/tests/shared/com/android/launcher3/testing/shared/TestProtocol.java @@ -90,6 +90,7 @@ public final class TestProtocol { public static final String REQUEST_DISABLE_TRANSIENT_TASKBAR = "disable-transient-taskbar"; public static final String REQUEST_UNSTASH_TASKBAR_IF_STASHED = "unstash-taskbar-if-stashed"; public static final String REQUEST_STASHED_TASKBAR_HEIGHT = "stashed-taskbar-height"; + public static final String REQUEST_STASHED_TASKBAR_SCALE = "taskbar-stash-handle-scale"; public static final String REQUEST_RECREATE_TASKBAR = "recreate-taskbar"; public static final String REQUEST_APP_LIST_FREEZE_FLAGS = "app-list-freeze-flags"; public static final String REQUEST_APPS_LIST_SCROLL_Y = "apps-list-scroll-y"; diff --git a/tests/tapl/com/android/launcher3/tapl/LaunchedAppState.java b/tests/tapl/com/android/launcher3/tapl/LaunchedAppState.java index 4a3507ed69..d1a270450a 100644 --- a/tests/tapl/com/android/launcher3/tapl/LaunchedAppState.java +++ b/tests/tapl/com/android/launcher3/tapl/LaunchedAppState.java @@ -22,13 +22,17 @@ import static com.android.launcher3.testing.shared.TestProtocol.REQUEST_DISABLE_ import static com.android.launcher3.testing.shared.TestProtocol.REQUEST_ENABLE_BLOCK_TIMEOUT; import static com.android.launcher3.testing.shared.TestProtocol.REQUEST_ENABLE_MANUAL_TASKBAR_STASHING; import static com.android.launcher3.testing.shared.TestProtocol.REQUEST_STASHED_TASKBAR_HEIGHT; +import static com.android.launcher3.testing.shared.TestProtocol.REQUEST_STASHED_TASKBAR_SCALE; import android.graphics.Point; import android.graphics.Rect; import android.os.SystemClock; import android.view.MotionEvent; +import android.view.ViewConfiguration; import androidx.test.uiautomator.By; +import androidx.test.uiautomator.Condition; +import androidx.test.uiautomator.UiDevice; import com.android.launcher3.testing.shared.TestProtocol; @@ -40,6 +44,18 @@ public final class LaunchedAppState extends Background { // More drag steps than Launchables to give the window manager time to register the drag. private static final int DEFAULT_DRAG_STEPS = 35; + // UNSTASHED_TASKBAR_HANDLE_HINT_SCALE value from TaskbarStashController. + private static final float UNSTASHED_TASKBAR_HANDLE_HINT_SCALE = 1.1f; + + private final Condition mStashedTaskbarHintScaleCondition = + device -> mLauncher.getTestInfo(REQUEST_STASHED_TASKBAR_SCALE).getFloat( + TestProtocol.TEST_INFO_RESPONSE_FIELD) - UNSTASHED_TASKBAR_HANDLE_HINT_SCALE + < 0.00001f; + + private final Condition mStashedTaskbarDefaultScaleCondition = + device -> mLauncher.getTestInfo(REQUEST_STASHED_TASKBAR_SCALE).getFloat( + TestProtocol.TEST_INFO_RESPONSE_FIELD) - 1f < 0.00001f; + LaunchedAppState(LauncherInstrumentation launcher) { super(launcher); } @@ -178,4 +194,86 @@ public final class LaunchedAppState extends Background { } } } + + /** + * Emulate the cursor hovering the screen edge to unstash the taskbar. + * + *

This unstashing occurs when not actively hovering the taskbar. + */ + public void hoverScreenBottomEdgeToUnstashTaskbar() { + try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck(); + LauncherInstrumentation.Closable c = mLauncher.addContextLayer( + "cursor hover entering screen edge to unstash taskbar")) { + mLauncher.getDevice().wait(mStashedTaskbarDefaultScaleCondition, + ViewConfiguration.DEFAULT_LONG_PRESS_TIMEOUT); + + long downTime = SystemClock.uptimeMillis(); + int leftEdge = 10; + Point taskbarUnstashArea = new Point(leftEdge, mLauncher.getRealDisplaySize().y - 1); + mLauncher.sendPointer(downTime, downTime, MotionEvent.ACTION_HOVER_ENTER, + new Point(taskbarUnstashArea.x, taskbarUnstashArea.y), null); + + mLauncher.waitForSystemLauncherObject(TASKBAR_RES_ID); + } + } + + /** + * Emulate the cursor hovering the taskbar to get unstash hint, then hovering below to unstash. + */ + public void hoverBelowHintedTaskbarToUnstash() { + try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck(); + LauncherInstrumentation.Closable c = mLauncher.addContextLayer( + "cursor hover entering stashed taskbar")) { + long downTime = SystemClock.uptimeMillis(); + Point stashedTaskbarHintArea = new Point(mLauncher.getRealDisplaySize().x / 2, + mLauncher.getRealDisplaySize().y - 1); + mLauncher.sendPointer(downTime, downTime, MotionEvent.ACTION_HOVER_ENTER, + new Point(stashedTaskbarHintArea.x, stashedTaskbarHintArea.y), null); + + mLauncher.getDevice().wait(mStashedTaskbarHintScaleCondition, + LauncherInstrumentation.WAIT_TIME_MS); + + try (LauncherInstrumentation.Closable c1 = mLauncher.addContextLayer( + "cursor hover enter below taskbar to unstash")) { + downTime = SystemClock.uptimeMillis(); + Point taskbarUnstashArea = new Point(mLauncher.getRealDisplaySize().x / 2, + mLauncher.getRealDisplaySize().y - 1); + mLauncher.sendPointer(downTime, downTime, MotionEvent.ACTION_HOVER_EXIT, + new Point(taskbarUnstashArea.x, taskbarUnstashArea.y), null); + + mLauncher.waitForSystemLauncherObject(TASKBAR_RES_ID); + } + } + } + + /** + * Emulate the cursor entering and exiting a hover over the taskbar. + */ + public void hoverToShowTaskbarUnstashHint() { + try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck(); + LauncherInstrumentation.Closable c = mLauncher.addContextLayer( + "cursor hover entering stashed taskbar")) { + long downTime = SystemClock.uptimeMillis(); + Point stashedTaskbarHintArea = new Point(mLauncher.getRealDisplaySize().x / 2, + mLauncher.getRealDisplaySize().y - 1); + mLauncher.sendPointer(downTime, downTime, MotionEvent.ACTION_HOVER_ENTER, + new Point(stashedTaskbarHintArea.x, stashedTaskbarHintArea.y), null); + + mLauncher.getDevice().wait(mStashedTaskbarHintScaleCondition, + LauncherInstrumentation.WAIT_TIME_MS); + + try (LauncherInstrumentation.Closable c1 = mLauncher.addContextLayer( + "cursor hover exiting stashed taskbar")) { + Point outsideStashedTaskbarHintArea = new Point( + mLauncher.getRealDisplaySize().x / 2, + mLauncher.getRealDisplaySize().y - 500); + mLauncher.sendPointer(downTime, downTime, MotionEvent.ACTION_HOVER_EXIT, + new Point(outsideStashedTaskbarHintArea.x, outsideStashedTaskbarHintArea.y), + null); + + mLauncher.getDevice().wait(mStashedTaskbarDefaultScaleCondition, + LauncherInstrumentation.WAIT_TIME_MS); + } + } + } } diff --git a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java index c4f82695d5..80fded5872 100644 --- a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java +++ b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java @@ -107,6 +107,8 @@ 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_TOUCH_CANCEL_TIS = getTouchEventPatternTIS("ACTION_CANCEL"); + static final Pattern EVENT_HOVER_ENTER_TIS = getTouchEventPatternTIS("ACTION_HOVER_ENTER"); + static final Pattern EVENT_HOVER_EXIT_TIS = getTouchEventPatternTIS("ACTION_HOVER_EXIT"); private static final Pattern EVENT_KEY_BACK_DOWN = getKeyEventPattern("ACTION_DOWN", "KEYCODE_BACK"); @@ -140,7 +142,9 @@ public final class LauncherInstrumentation { * Represents a point in the code at which a callback can run. */ public enum CALLBACK_RUN_POINT { - CALLBACK_HOLD_BEFORE_DROP + CALLBACK_HOLD_BEFORE_DROP, + CALLBACK_HOVER_ENTER, + CALLBACK_HOVER_EXIT, } private Consumer mCallbackAtRunPoint = null; @@ -1682,6 +1686,12 @@ public final class LauncherInstrumentation { ? EVENT_TOUCH_CANCEL_TIS : EVENT_TOUCH_UP_TIS); } break; + case MotionEvent.ACTION_HOVER_ENTER: + expectEvent(TestProtocol.SEQUENCE_TIS, EVENT_HOVER_ENTER_TIS); + break; + case MotionEvent.ACTION_HOVER_EXIT: + expectEvent(TestProtocol.SEQUENCE_TIS, EVENT_HOVER_EXIT_TIS); + break; } final MotionEvent event = getMotionEvent(downTime, currentTime, action, point.x, point.y);