From 8a054061ef4d1cade5047038f9185d46db6fe5b0 Mon Sep 17 00:00:00 2001 From: Tony Wickham Date: Tue, 28 Jan 2020 12:00:14 -0800 Subject: [PATCH 1/3] Change LauncherState to Supplier in tests This prevents the test from statically initializing LauncherState and all its static dependencies, which might lead to runtime exceptions in out-of-proc tests. Change-Id: I0e4e09dfb31a8b256c2c0c0b3d1d2ecd0cc92230 --- .../quickstep/DigitalWellBeingToastTest.java | 2 +- .../android/quickstep/TaplTestsQuickstep.java | 39 +++++++++++-------- .../launcher3/compat/PromiseIconUiTest.java | 2 +- .../launcher3/ui/AbstractLauncherUiTest.java | 9 +++-- .../launcher3/ui/TaplTestsLauncher3.java | 21 +++++----- 5 files changed, 42 insertions(+), 31 deletions(-) diff --git a/quickstep/tests/src/com/android/quickstep/DigitalWellBeingToastTest.java b/quickstep/tests/src/com/android/quickstep/DigitalWellBeingToastTest.java index a7c33a9549..3e84a76695 100644 --- a/quickstep/tests/src/com/android/quickstep/DigitalWellBeingToastTest.java +++ b/quickstep/tests/src/com/android/quickstep/DigitalWellBeingToastTest.java @@ -68,7 +68,7 @@ public class DigitalWellBeingToastTest extends AbstractQuickStepTest { private DigitalWellBeingToast getToast() { executeOnLauncher(launcher -> launcher.getStateManager().goToState(OVERVIEW)); - waitForState("Launcher internal state didn't switch to Overview", OVERVIEW); + waitForState("Launcher internal state didn't switch to Overview", () -> OVERVIEW); final TaskView task = getOnceNotNull("No latest task", launcher -> getLatestTask(launcher)); return getFromLauncher(launcher -> { diff --git a/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java b/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java index 71d77fc61e..2804c0e1ab 100644 --- a/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java +++ b/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java @@ -82,7 +82,8 @@ public class TaplTestsQuickstep extends AbstractQuickStepTest { @Ignore // Enable after b/131115533 public void testPressRecentAppsLauncherAndGetOverview() throws RemoteException { mDevice.pressRecentApps(); - waitForState("Launcher internal state didn't switch to Overview", LauncherState.OVERVIEW); + waitForState("Launcher internal state didn't switch to Overview", + () -> LauncherState.OVERVIEW); assertNotNull("getOverview() returned null", mLauncher.getOverview()); } @@ -93,7 +94,8 @@ public class TaplTestsQuickstep extends AbstractQuickStepTest { public void testWorkspaceSwitchToAllApps() { assertNotNull("switchToAllApps() returned null", mLauncher.getWorkspace().switchToAllApps()); - assertTrue("Launcher internal state is not All Apps", isInState(LauncherState.ALL_APPS)); + assertTrue("Launcher internal state is not All Apps", + isInState(() -> LauncherState.ALL_APPS)); } @Test @@ -112,7 +114,7 @@ public class TaplTestsQuickstep extends AbstractQuickStepTest { // mLauncher.pressHome() also tests an important case of pressing home while in background. Overview overview = mLauncher.pressHome().switchToOverview(); assertTrue("Launcher internal state didn't switch to Overview", - isInState(LauncherState.OVERVIEW)); + isInState(() -> LauncherState.OVERVIEW)); executeOnLauncher( launcher -> assertTrue("Don't have at least 3 tasks", getTaskCount(launcher) >= 3)); @@ -121,14 +123,16 @@ public class TaplTestsQuickstep extends AbstractQuickStepTest { 0, getCurrentOverviewPage(launcher))); overview.flingForward(); - assertTrue("Launcher internal state is not Overview", isInState(LauncherState.OVERVIEW)); + assertTrue("Launcher internal state is not Overview", + isInState(() -> LauncherState.OVERVIEW)); final Integer currentTaskAfterFlingForward = getFromLauncher( launcher -> getCurrentOverviewPage(launcher)); executeOnLauncher(launcher -> assertTrue("Current task in Overview is still 0", currentTaskAfterFlingForward > 0)); overview.flingBackward(); - assertTrue("Launcher internal state is not Overview", isInState(LauncherState.OVERVIEW)); + assertTrue("Launcher internal state is not Overview", + isInState(() -> LauncherState.OVERVIEW)); executeOnLauncher(launcher -> assertTrue("Flinging back in Overview did nothing", getCurrentOverviewPage(launcher) < currentTaskAfterFlingForward)); @@ -147,7 +151,7 @@ public class TaplTestsQuickstep extends AbstractQuickStepTest { // Test dismissing a task. overview = mLauncher.pressHome().switchToOverview(); assertTrue("Launcher internal state didn't switch to Overview", - isInState(LauncherState.OVERVIEW)); + isInState(() -> LauncherState.OVERVIEW)); final Integer numTasks = getFromLauncher(launcher -> getTaskCount(launcher)); task = overview.getCurrentTask(); assertNotNull("overview.getCurrentTask() returned null (2)", task); @@ -162,29 +166,29 @@ public class TaplTestsQuickstep extends AbstractQuickStepTest { final AllAppsFromOverview allApps = overview.switchToAllApps(); assertNotNull("overview.switchToAllApps() returned null (1)", allApps); assertTrue("Launcher internal state is not All Apps (1)", - isInState(LauncherState.ALL_APPS)); + isInState(() -> LauncherState.ALL_APPS)); overview = allApps.switchBackToOverview(); assertNotNull("allApps.switchBackToOverview() returned null", overview); assertTrue("Launcher internal state didn't switch to Overview", - isInState(LauncherState.OVERVIEW)); + isInState(() -> LauncherState.OVERVIEW)); // Test UIDevice.pressBack() overview.switchToAllApps(); assertNotNull("overview.switchToAllApps() returned null (2)", allApps); assertTrue("Launcher internal state is not All Apps (2)", - isInState(LauncherState.ALL_APPS)); + isInState(() -> LauncherState.ALL_APPS)); mDevice.pressBack(); mLauncher.getOverview(); } // Test UIDevice.pressHome, once we are in AllApps. mDevice.pressHome(); - waitForState("Launcher internal state didn't switch to Home", LauncherState.NORMAL); + waitForState("Launcher internal state didn't switch to Home", () -> LauncherState.NORMAL); // Test dismissing all tasks. mLauncher.getWorkspace().switchToOverview().dismissAllTasks(); - waitForState("Launcher internal state didn't switch to Home", LauncherState.NORMAL); + waitForState("Launcher internal state didn't switch to Home", () -> LauncherState.NORMAL); executeOnLauncher( launcher -> assertEquals("Still have tasks after dismissing all", 0, getTaskCount(launcher))); @@ -202,7 +206,8 @@ public class TaplTestsQuickstep extends AbstractQuickStepTest { public void testAppIconLaunchFromAllAppsFromOverview() throws Exception { final AllApps allApps = mLauncher.getWorkspace().switchToOverview().switchToAllApps(); - assertTrue("Launcher internal state is not All Apps", isInState(LauncherState.ALL_APPS)); + assertTrue("Launcher internal state is not All Apps", + isInState(() -> LauncherState.ALL_APPS)); TaplTestsLauncher3.runIconLaunchFromAllAppsTest(this, allApps); } @@ -214,7 +219,7 @@ public class TaplTestsQuickstep extends AbstractQuickStepTest { assertNotNull("Workspace.switchToOverview() returned null", mLauncher.pressHome().switchToOverview()); assertTrue("Launcher internal state didn't switch to Overview", - isInState(LauncherState.OVERVIEW)); + isInState(() -> LauncherState.OVERVIEW)); } @Test @@ -226,7 +231,7 @@ public class TaplTestsQuickstep extends AbstractQuickStepTest { assertNotNull("Background.switchToOverview() returned null", background.switchToOverview()); assertTrue("Launcher internal state didn't switch to Overview", - isInState(LauncherState.OVERVIEW)); + isInState(() -> LauncherState.OVERVIEW)); } private Background getAndAssertBackground() { @@ -249,9 +254,11 @@ public class TaplTestsQuickstep extends AbstractQuickStepTest { TaplTestsLauncher3.runAllAppsTest(this, mLauncher.getAllApps()); // Testing pressHome. - assertTrue("Launcher internal state is not All Apps", isInState(LauncherState.ALL_APPS)); + assertTrue("Launcher internal state is not All Apps", + isInState(() -> LauncherState.ALL_APPS)); assertNotNull("pressHome returned null", mLauncher.pressHome()); - assertTrue("Launcher internal state is not Home", isInState(LauncherState.NORMAL)); + assertTrue("Launcher internal state is not Home", + isInState(() -> LauncherState.NORMAL)); assertNotNull("getHome returned null", mLauncher.getWorkspace()); } diff --git a/tests/src/com/android/launcher3/compat/PromiseIconUiTest.java b/tests/src/com/android/launcher3/compat/PromiseIconUiTest.java index efbd9c99c3..33066e403e 100644 --- a/tests/src/com/android/launcher3/compat/PromiseIconUiTest.java +++ b/tests/src/com/android/launcher3/compat/PromiseIconUiTest.java @@ -49,7 +49,7 @@ public class PromiseIconUiTest extends AbstractLauncherUiTest { super.setUp(); mDevice.pressHome(); waitForLauncherCondition("Launcher didn't start", launcher -> launcher != null); - waitForState("Launcher internal state didn't switch to Home", LauncherState.NORMAL); + waitForState("Launcher internal state didn't switch to Home", () -> LauncherState.NORMAL); mSessionId = -1; } diff --git a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java index 19997eb028..25670e7ee0 100644 --- a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java +++ b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java @@ -84,6 +84,7 @@ import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.function.Consumer; import java.util.function.Function; +import java.util.function.Supplier; /** * Base class for all instrumentation tests providing various utility methods. @@ -281,9 +282,9 @@ public abstract class AbstractLauncherUiTest { // Cannot be used in TaplTests between a Tapl call injecting a gesture and a tapl call expecting // the results of that gesture because the wait can hide flakeness. - protected void waitForState(String message, LauncherState state) { + protected void waitForState(String message, Supplier state) { waitForLauncherCondition(message, - launcher -> launcher.getStateManager().getCurrentStableState() == state); + launcher -> launcher.getStateManager().getCurrentStableState() == state.get()); } protected void waitForResumed(String message) { @@ -430,9 +431,9 @@ public abstract class AbstractLauncherUiTest { return !launcher.hasBeenResumed(); } - protected boolean isInState(LauncherState state) { + protected boolean isInState(Supplier state) { if (!TestHelpers.isInLauncherProcess()) return true; - return getFromLauncher(launcher -> launcher.getStateManager().getState() == state); + return getFromLauncher(launcher -> launcher.getStateManager().getState() == state.get()); } protected int getAllAppsScroll(Launcher launcher) { diff --git a/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java b/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java index 61f515052a..2c1883b43a 100644 --- a/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java +++ b/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java @@ -73,7 +73,8 @@ public class TaplTestsLauncher3 extends AbstractLauncherUiTest { test.clearLauncherData(); test.mDevice.pressHome(); test.waitForLauncherCondition("Launcher didn't start", launcher -> launcher != null); - test.waitForState("Launcher internal state didn't switch to Home", LauncherState.NORMAL); + test.waitForState("Launcher internal state didn't switch to Home", + () -> LauncherState.NORMAL); test.waitForResumed("Launcher internal state is still Background"); // Check that we switched to home. test.mLauncher.getWorkspace(); @@ -143,7 +144,7 @@ public class TaplTestsLauncher3 extends AbstractLauncherUiTest { assertTrue( "Launcher internal state is not All Apps", - test.isInState(LauncherState.ALL_APPS)); + test.isInState(() -> LauncherState.ALL_APPS)); // Test flinging forward and backward. test.executeOnLauncher(launcher -> assertEquals( @@ -152,7 +153,7 @@ public class TaplTestsLauncher3 extends AbstractLauncherUiTest { allApps.flingForward(); assertTrue("Launcher internal state is not All Apps", - test.isInState(LauncherState.ALL_APPS)); + test.isInState(() -> LauncherState.ALL_APPS)); final Integer flingForwardY = test.getFromLauncher( launcher -> test.getAllAppsScroll(launcher)); test.executeOnLauncher( @@ -162,7 +163,7 @@ public class TaplTestsLauncher3 extends AbstractLauncherUiTest { allApps.flingBackward(); assertTrue( "Launcher internal state is not All Apps", - test.isInState(LauncherState.ALL_APPS)); + test.isInState(() -> LauncherState.ALL_APPS)); final Integer flingBackwardY = test.getFromLauncher( launcher -> test.getAllAppsScroll(launcher)); test.executeOnLauncher(launcher -> assertTrue("flingBackward() didn't scroll App Apps", @@ -179,7 +180,7 @@ public class TaplTestsLauncher3 extends AbstractLauncherUiTest { assertTrue( "Launcher internal state is not All Apps", - test.isInState(LauncherState.ALL_APPS)); + test.isInState(() -> LauncherState.ALL_APPS)); } finally { allApps.unfreeze(); } @@ -190,7 +191,8 @@ public class TaplTestsLauncher3 extends AbstractLauncherUiTest { public void testWorkspaceSwitchToAllApps() { assertNotNull("switchToAllApps() returned null", mLauncher.getWorkspace().switchToAllApps()); - assertTrue("Launcher internal state is not All Apps", isInState(LauncherState.ALL_APPS)); + assertTrue("Launcher internal state is not All Apps", + isInState(() -> LauncherState.ALL_APPS)); } @Test @@ -216,7 +218,7 @@ public class TaplTestsLauncher3 extends AbstractLauncherUiTest { // Test flinging workspace. workspace.flingBackward(); - assertTrue("Launcher internal state is not Home", isInState(LauncherState.NORMAL)); + assertTrue("Launcher internal state is not Home", isInState(() -> LauncherState.NORMAL)); executeOnLauncher( launcher -> assertEquals("Flinging back didn't switch workspace to page #0", 0, getCurrentWorkspacePage(launcher))); @@ -225,7 +227,7 @@ public class TaplTestsLauncher3 extends AbstractLauncherUiTest { executeOnLauncher( launcher -> assertEquals("Flinging forward didn't switch workspace to page #1", 1, getCurrentWorkspacePage(launcher))); - assertTrue("Launcher internal state is not Home", isInState(LauncherState.NORMAL)); + assertTrue("Launcher internal state is not Home", isInState(() -> LauncherState.NORMAL)); // Test starting a workspace app. final AppIcon app = workspace.getWorkspaceAppIcon("Chrome"); @@ -251,7 +253,8 @@ public class TaplTestsLauncher3 extends AbstractLauncherUiTest { @PortraitLandscape public void testAppIconLaunchFromAllAppsFromHome() throws Exception { final AllApps allApps = mLauncher.getWorkspace().switchToAllApps(); - assertTrue("Launcher internal state is not All Apps", isInState(LauncherState.ALL_APPS)); + assertTrue("Launcher internal state is not All Apps", + isInState(() -> LauncherState.ALL_APPS)); runIconLaunchFromAllAppsTest(this, allApps); } From 4fdba14cfb737dc3d571ae35c761283b8f660c3e Mon Sep 17 00:00:00 2001 From: Tony Wickham Date: Mon, 4 Nov 2019 16:23:51 -0800 Subject: [PATCH 2/3] Two-zone model: swipe up from nav bar vs above it When ENABLE_OVERVIEW_ACTIONS flag is enabled, swiping up from the nav bar goes to overview if you hold, or the first home screen if you don't. - Added NoButtonNavBarToOverviewTouchController, which extends FlingAndHoldTouchController but only works if you start from the nav bar. Otherwise it falls back to PortraitStatesTouchController to handle normal state transition to all apps. - Added HintState. This is where the workspace/hotseat/qsb scale down when you swipe up from the nav bar, to hint that you're about to either go to overview or the first home screen. - Added getQsbScaleAndTranslation() to allow Overview and Hint states to treat it as part of the hotseat. - Since Overview needs to show above the QSB as it's animating, bring Overview to the front and update OverviewScrim to handle the case where there's no view above Overview to draw the scrim beneath. - ENABLE_OVERVIEW_ACTIONS is always false in 2-button mode, since the shelf is crucial to that mode. Bug: 143361609 Change-Id: I743481bb239dc77f7024dc98ba68a43534da2637 --- .../uioverrides/QuickstepLauncher.java | 7 +- .../uioverrides/states/OverviewState.java | 26 ++- .../FlingAndHoldTouchController.java | 85 +++++---- ...ButtonNavbarToOverviewTouchController.java | 174 ++++++++++++++++++ .../NoButtonQuickSwitchTouchController.java | 8 +- .../android/quickstep/util/ShelfPeekAnim.java | 3 +- .../QuickstepProcessInitializer.java | 22 +++ .../quickstep/views/ShelfScrimView.java | 14 +- src/com/android/launcher3/Launcher.java | 5 + .../android/launcher3/LauncherAnimUtils.java | 5 +- src/com/android/launcher3/LauncherState.java | 14 +- src/com/android/launcher3/Utilities.java | 3 +- src/com/android/launcher3/Workspace.java | 3 +- .../WorkspaceStateTransitionAnimation.java | 30 ++- .../anim/AnimatorPlaybackController.java | 13 +- .../launcher3/config/FeatureFlags.java | 3 +- .../launcher3/dragndrop/DragLayer.java | 3 + .../launcher3/graphics/OverviewScrim.java | 7 +- .../android/launcher3/states/HintState.java | 54 ++++++ .../launcher3/testing/TestProtocol.java | 3 + .../AbstractStateChangeTouchController.java | 6 +- 21 files changed, 419 insertions(+), 69 deletions(-) create mode 100644 quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonNavbarToOverviewTouchController.java create mode 100644 src/com/android/launcher3/states/HintState.java diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/QuickstepLauncher.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/QuickstepLauncher.java index 4b5ba951b3..c359423085 100644 --- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/QuickstepLauncher.java +++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/QuickstepLauncher.java @@ -37,6 +37,7 @@ import com.android.launcher3.popup.SystemShortcut; import com.android.launcher3.uioverrides.touchcontrollers.FlingAndHoldTouchController; import com.android.launcher3.uioverrides.touchcontrollers.LandscapeEdgeSwipeController; import com.android.launcher3.uioverrides.touchcontrollers.NavBarToHomeTouchController; +import com.android.launcher3.uioverrides.touchcontrollers.NoButtonNavbarToOverviewTouchController; import com.android.launcher3.uioverrides.touchcontrollers.NoButtonQuickSwitchTouchController; import com.android.launcher3.uioverrides.touchcontrollers.OverviewToAllAppsTouchController; import com.android.launcher3.uioverrides.touchcontrollers.PortraitStatesTouchController; @@ -224,7 +225,11 @@ public class QuickstepLauncher extends BaseQuickstepLauncher { if (mode == NO_BUTTON) { list.add(new NoButtonQuickSwitchTouchController(this)); list.add(new NavBarToHomeTouchController(this)); - list.add(new FlingAndHoldTouchController(this)); + if (FeatureFlags.ENABLE_OVERVIEW_ACTIONS.get()) { + list.add(new NoButtonNavbarToOverviewTouchController(this)); + } else { + list.add(new FlingAndHoldTouchController(this)); + } } else { if (getDeviceProfile().isVerticalBarLayout()) { list.add(new OverviewToAllAppsTouchController(this)); diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/OverviewState.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/OverviewState.java index 73c0c97f08..9c78af9af1 100644 --- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/OverviewState.java +++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/OverviewState.java @@ -30,11 +30,13 @@ import static com.android.launcher3.anim.Interpolators.ACCEL; import static com.android.launcher3.anim.Interpolators.DEACCEL_2; import static com.android.launcher3.anim.Interpolators.OVERSHOOT_1_2; import static com.android.launcher3.anim.Interpolators.OVERSHOOT_1_7; +import static com.android.launcher3.config.FeatureFlags.ENABLE_OVERVIEW_ACTIONS; import static com.android.launcher3.logging.LoggerUtils.newContainerTarget; import static com.android.launcher3.states.RotationHelper.REQUEST_ROTATE; import android.graphics.Rect; import android.view.View; +import android.view.animation.Interpolator; import com.android.launcher3.AbstractFloatingView; import com.android.launcher3.DeviceProfile; @@ -44,7 +46,6 @@ import com.android.launcher3.R; import com.android.launcher3.Workspace; import com.android.launcher3.allapps.DiscoveryBounce; import com.android.launcher3.anim.AnimatorSetBuilder; -import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.userevent.nano.LauncherLogProto.Action; import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType; import com.android.quickstep.SysUINavigationMode; @@ -115,6 +116,15 @@ public class OverviewState extends LauncherState { return new ScaleAndTranslation(1f, 0f, 0f); } + @Override + public ScaleAndTranslation getQsbScaleAndTranslation(Launcher launcher) { + if (this == OVERVIEW && ENABLE_OVERVIEW_ACTIONS.get()) { + // Treat the QSB as part of the hotseat so they move together. + return getHotseatScaleAndTranslation(launcher); + } + return super.getQsbScaleAndTranslation(launcher); + } + @Override public void onStateEnabled(Launcher launcher) { AbstractFloatingView.closeAllOpenViews(launcher); @@ -141,7 +151,7 @@ public class OverviewState extends LauncherState { if (launcher.getDeviceProfile().isVerticalBarLayout()) { return VERTICAL_SWIPE_INDICATOR | RECENTS_CLEAR_ALL_BUTTON; } else { - if (FeatureFlags.ENABLE_OVERVIEW_ACTIONS.get()) { + if (ENABLE_OVERVIEW_ACTIONS.get()) { return VERTICAL_SWIPE_INDICATOR | RECENTS_CLEAR_ALL_BUTTON; } @@ -195,9 +205,10 @@ public class OverviewState extends LauncherState { @Override public void prepareForAtomicAnimation(Launcher launcher, LauncherState fromState, AnimatorSetBuilder builder) { - if (fromState == NORMAL && this == OVERVIEW) { + if ((fromState == NORMAL || fromState == HINT_STATE) && this == OVERVIEW) { if (SysUINavigationMode.getMode(launcher) == SysUINavigationMode.Mode.NO_BUTTON) { - builder.setInterpolator(ANIM_WORKSPACE_SCALE, ACCEL); + builder.setInterpolator(ANIM_WORKSPACE_SCALE, + fromState == NORMAL ? ACCEL : OVERSHOOT_1_2); builder.setInterpolator(ANIM_WORKSPACE_TRANSLATE, ACCEL); } else { builder.setInterpolator(ANIM_WORKSPACE_SCALE, OVERSHOOT_1_2); @@ -210,8 +221,11 @@ public class OverviewState extends LauncherState { } builder.setInterpolator(ANIM_WORKSPACE_FADE, OVERSHOOT_1_2); builder.setInterpolator(ANIM_OVERVIEW_SCALE, OVERSHOOT_1_2); - builder.setInterpolator(ANIM_OVERVIEW_TRANSLATE_X, OVERSHOOT_1_7); - builder.setInterpolator(ANIM_OVERVIEW_TRANSLATE_Y, OVERSHOOT_1_7); + Interpolator translationInterpolator = ENABLE_OVERVIEW_ACTIONS.get() + ? OVERSHOOT_1_2 + : OVERSHOOT_1_7; + builder.setInterpolator(ANIM_OVERVIEW_TRANSLATE_X, translationInterpolator); + builder.setInterpolator(ANIM_OVERVIEW_TRANSLATE_Y, translationInterpolator); builder.setInterpolator(ANIM_OVERVIEW_FADE, OVERSHOOT_1_2); } } diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/FlingAndHoldTouchController.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/FlingAndHoldTouchController.java index d388f495bf..ff1b5f61e0 100644 --- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/FlingAndHoldTouchController.java +++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/FlingAndHoldTouchController.java @@ -60,7 +60,7 @@ public class FlingAndHoldTouchController extends PortraitStatesTouchController { private static final long PEEK_OUT_ANIM_DURATION = 100; private static final float MAX_DISPLACEMENT_PERCENT = 0.75f; - private final MotionPauseDetector mMotionPauseDetector; + protected final MotionPauseDetector mMotionPauseDetector; private final float mMotionPauseMinDisplacement; private final float mMotionPauseMaxDisplacement; @@ -85,37 +85,39 @@ public class FlingAndHoldTouchController extends PortraitStatesTouchController { super.onDragStart(start); if (handlingOverviewAnim()) { - mMotionPauseDetector.setOnMotionPauseListener(isPaused -> { - RecentsView recentsView = mLauncher.getOverviewPanel(); - recentsView.setOverviewStateEnabled(isPaused); - if (mPeekAnim != null) { - mPeekAnim.cancel(); - } - LauncherState fromState = isPaused ? NORMAL : OVERVIEW_PEEK; - LauncherState toState = isPaused ? OVERVIEW_PEEK : NORMAL; - long peekDuration = isPaused ? PEEK_IN_ANIM_DURATION : PEEK_OUT_ANIM_DURATION; - mPeekAnim = mLauncher.getStateManager().createAtomicAnimation(fromState, toState, - new AnimatorSetBuilder(), ATOMIC_OVERVIEW_PEEK_COMPONENT, peekDuration); - mPeekAnim.addListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - mPeekAnim = null; - } - }); - mPeekAnim.start(); - VibratorWrapper.INSTANCE.get(mLauncher).vibrate(OVERVIEW_HAPTIC); - - mLauncher.getDragLayer().getScrim().animateToSysuiMultiplier(isPaused ? 0 : 1, - peekDuration, 0); - }); + mMotionPauseDetector.setOnMotionPauseListener(this::onMotionPauseChanged); } } + protected void onMotionPauseChanged(boolean isPaused) { + RecentsView recentsView = mLauncher.getOverviewPanel(); + recentsView.setOverviewStateEnabled(isPaused); + if (mPeekAnim != null) { + mPeekAnim.cancel(); + } + LauncherState fromState = isPaused ? NORMAL : OVERVIEW_PEEK; + LauncherState toState = isPaused ? OVERVIEW_PEEK : NORMAL; + long peekDuration = isPaused ? PEEK_IN_ANIM_DURATION : PEEK_OUT_ANIM_DURATION; + mPeekAnim = mLauncher.getStateManager().createAtomicAnimation(fromState, toState, + new AnimatorSetBuilder(), ATOMIC_OVERVIEW_PEEK_COMPONENT, peekDuration); + mPeekAnim.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + mPeekAnim = null; + } + }); + mPeekAnim.start(); + VibratorWrapper.INSTANCE.get(mLauncher).vibrate(OVERVIEW_HAPTIC); + + mLauncher.getDragLayer().getScrim().animateToSysuiMultiplier(isPaused ? 0 : 1, + peekDuration, 0); + } + /** * @return Whether we are handling the overview animation, rather than * having it as part of the existing animation to the target state. */ - private boolean handlingOverviewAnim() { + protected boolean handlingOverviewAnim() { int stateFlags = SystemUiProxy.INSTANCE.get(mLauncher).getLastSystemUiStateFlags(); return mStartState == NORMAL && (stateFlags & SYSUI_STATE_OVERVIEW_DISABLED) == 0; } @@ -162,7 +164,8 @@ public class FlingAndHoldTouchController extends PortraitStatesTouchController { @Override public boolean onDrag(float displacement, MotionEvent event) { float upDisplacement = -displacement; - mMotionPauseDetector.setDisallowPause(upDisplacement < mMotionPauseMinDisplacement + mMotionPauseDetector.setDisallowPause(!handlingOverviewAnim() + || upDisplacement < mMotionPauseMinDisplacement || upDisplacement > mMotionPauseMaxDisplacement); mMotionPauseDetector.addPosition(displacement, event.getEventTime()); return super.onDrag(displacement, event); @@ -171,19 +174,7 @@ public class FlingAndHoldTouchController extends PortraitStatesTouchController { @Override public void onDragEnd(float velocity) { if (mMotionPauseDetector.isPaused() && handlingOverviewAnim()) { - if (mPeekAnim != null) { - mPeekAnim.cancel(); - } - - Animator overviewAnim = mLauncher.getAppTransitionManager().createStateElementAnimation( - INDEX_PAUSE_TO_OVERVIEW_ANIM); - overviewAnim.addListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - onSwipeInteractionCompleted(OVERVIEW, Touch.SWIPE); - } - }); - overviewAnim.start(); + goToOverviewOnDragEnd(velocity); } else { super.onDragEnd(velocity); } @@ -195,6 +186,22 @@ public class FlingAndHoldTouchController extends PortraitStatesTouchController { mMotionPauseDetector.clear(); } + protected void goToOverviewOnDragEnd(float velocity) { + if (mPeekAnim != null) { + mPeekAnim.cancel(); + } + + Animator overviewAnim = mLauncher.getAppTransitionManager().createStateElementAnimation( + INDEX_PAUSE_TO_OVERVIEW_ANIM); + overviewAnim.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + onSwipeInteractionCompleted(OVERVIEW, Touch.SWIPE); + } + }); + overviewAnim.start(); + } + @Override protected void goToTargetState(LauncherState targetState, int logAction) { if (mPeekAnim != null && mPeekAnim.isStarted()) { diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonNavbarToOverviewTouchController.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonNavbarToOverviewTouchController.java new file mode 100644 index 0000000000..a993f3f53b --- /dev/null +++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonNavbarToOverviewTouchController.java @@ -0,0 +1,174 @@ +/* + * Copyright (C) 2020 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.uioverrides.touchcontrollers; + +import static com.android.launcher3.LauncherState.HINT_STATE; +import static com.android.launcher3.LauncherState.NORMAL; +import static com.android.launcher3.LauncherState.OVERVIEW; +import static com.android.launcher3.LauncherStateManager.ATOMIC_OVERVIEW_PEEK_COMPONENT; +import static com.android.launcher3.Utilities.EDGE_NAV_BAR; +import static com.android.launcher3.util.VibratorWrapper.OVERVIEW_HAPTIC; + +import android.animation.Animator; +import android.animation.AnimatorSet; +import android.animation.ValueAnimator; +import android.view.MotionEvent; + +import com.android.launcher3.Launcher; +import com.android.launcher3.LauncherState; +import com.android.launcher3.LauncherStateManager; +import com.android.launcher3.Utilities; +import com.android.launcher3.anim.AnimationSuccessListener; +import com.android.launcher3.anim.AnimatorSetBuilder; +import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch; +import com.android.launcher3.util.VibratorWrapper; +import com.android.quickstep.util.StaggeredWorkspaceAnim; + +/** + * Touch controller which handles swipe and hold from the nav bar to go to Overview. Swiping above + * the nav bar falls back to go to All Apps. Swiping from the nav bar without holding goes to the + * first home screen instead of to Overview. + */ +public class NoButtonNavbarToOverviewTouchController extends FlingAndHoldTouchController { + + private boolean mDidTouchStartInNavBar; + private boolean mReachedOverview; + + public NoButtonNavbarToOverviewTouchController(Launcher l) { + super(l); + } + + @Override + protected boolean canInterceptTouch(MotionEvent ev) { + mDidTouchStartInNavBar = (ev.getEdgeFlags() & EDGE_NAV_BAR) != 0; + return super.canInterceptTouch(ev); + } + + @Override + protected LauncherState getTargetState(LauncherState fromState, boolean isDragTowardPositive) { + if (fromState == NORMAL && mDidTouchStartInNavBar) { + return HINT_STATE; + } else if (fromState == OVERVIEW && isDragTowardPositive) { + // Don't allow swiping up to all apps. + return OVERVIEW; + } + return super.getTargetState(fromState, isDragTowardPositive); + } + + @Override + protected float initCurrentAnimation(int animComponents) { + float progressMultiplier = super.initCurrentAnimation(animComponents); + if (mToState == HINT_STATE) { + // Track the drag across the entire height of the screen. + progressMultiplier = -1 / getShiftRange(); + } + return progressMultiplier; + } + + @Override + public void onDragStart(boolean start) { + super.onDragStart(start); + + mReachedOverview = false; + } + + @Override + protected void updateSwipeCompleteAnimation(ValueAnimator animator, long expectedDuration, + LauncherState targetState, float velocity, boolean isFling) { + super.updateSwipeCompleteAnimation(animator, expectedDuration, targetState, velocity, + isFling); + if (targetState == HINT_STATE) { + // Normally we compute the duration based on the velocity and distance to the given + // state, but since the hint state tracks the entire screen without a clear endpoint, we + // need to manually set the duration to a reasonable value. + animator.setDuration(HINT_STATE.transitionDuration); + } + } + + @Override + protected void onMotionPauseChanged(boolean isPaused) { + if (mCurrentAnimation == null) { + return; + } + mCurrentAnimation.dispatchOnCancelWithoutCancelRunnable(() -> { + mLauncher.getStateManager().goToState(OVERVIEW, true, () -> { + mReachedOverview = true; + maybeSwipeInteractionToOverviewComplete(); + }); + }); + VibratorWrapper.INSTANCE.get(mLauncher).vibrate(OVERVIEW_HAPTIC); + } + + private void maybeSwipeInteractionToOverviewComplete() { + if (mReachedOverview && mDetector.isSettlingState()) { + onSwipeInteractionCompleted(OVERVIEW, Touch.SWIPE); + } + } + + @Override + protected boolean handlingOverviewAnim() { + return mDidTouchStartInNavBar && super.handlingOverviewAnim(); + } + + @Override + public boolean onDrag(float displacement, MotionEvent event) { + if (mMotionPauseDetector.isPaused()) { + // Stay in Overview. + return true; + } + return super.onDrag(displacement, event); + } + + @Override + protected void goToOverviewOnDragEnd(float velocity) { + float velocityDp = dpiFromPx(velocity); + boolean isFling = Math.abs(velocityDp) > 1; + LauncherStateManager stateManager = mLauncher.getStateManager(); + if (isFling) { + // When flinging, go back to home instead of overview. + if (velocity > 0) { + stateManager.goToState(NORMAL, true, + () -> onSwipeInteractionCompleted(NORMAL, Touch.FLING)); + } else { + StaggeredWorkspaceAnim staggeredWorkspaceAnim = new StaggeredWorkspaceAnim( + mLauncher, velocity, false /* animateOverviewScrim */); + staggeredWorkspaceAnim.start(); + + // StaggeredWorkspaceAnim doesn't animate overview, so we handle it here. + stateManager.cancelAnimation(); + AnimatorSetBuilder builder = new AnimatorSetBuilder(); + long duration = OVERVIEW.transitionDuration; + AnimatorSet anim = stateManager.createAtomicAnimation( + stateManager.getState(), NORMAL, builder, + ATOMIC_OVERVIEW_PEEK_COMPONENT, duration); + anim.addListener(new AnimationSuccessListener() { + @Override + public void onAnimationSuccess(Animator animator) { + onSwipeInteractionCompleted(NORMAL, Touch.SWIPE); + } + }); + anim.start(); + } + } else { + maybeSwipeInteractionToOverviewComplete(); + } + } + + private float dpiFromPx(float pixels) { + return Utilities.dpiFromPx(pixels, mLauncher.getResources().getDisplayMetrics()); + } +} diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonQuickSwitchTouchController.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonQuickSwitchTouchController.java index 4e08df9c70..8628db0107 100644 --- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonQuickSwitchTouchController.java +++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonQuickSwitchTouchController.java @@ -62,6 +62,7 @@ import com.android.launcher3.Utilities; import com.android.launcher3.allapps.AllAppsTransitionController; import com.android.launcher3.anim.AnimatorPlaybackController; import com.android.launcher3.anim.AnimatorSetBuilder; +import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.graphics.OverviewScrim; import com.android.launcher3.touch.BaseSwipeDetector; import com.android.launcher3.touch.BothAxesSwipeDetector; @@ -181,6 +182,12 @@ public class NoButtonQuickSwitchTouchController implements TouchController, @Override public void onMotionPauseChanged(boolean isPaused) { + VibratorWrapper.INSTANCE.get(mLauncher).vibrate(OVERVIEW_HAPTIC); + + if (FeatureFlags.ENABLE_OVERVIEW_ACTIONS.get()) { + return; + } + ShelfAnimState shelfState = isPaused ? PEEK : HIDE; if (shelfState == PEEK) { // Some shelf elements (e.g. qsb) were hidden, but we need them visible when peeking. @@ -197,7 +204,6 @@ public class NoButtonQuickSwitchTouchController implements TouchController, } mShelfPeekAnim.setShelfState(shelfState, ShelfPeekAnim.INTERPOLATOR, ShelfPeekAnim.DURATION); - VibratorWrapper.INSTANCE.get(mLauncher).vibrate(OVERVIEW_HAPTIC); } private void setupAnimators() { diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/ShelfPeekAnim.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/ShelfPeekAnim.java index 41be6834f7..217eca559d 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/ShelfPeekAnim.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/ShelfPeekAnim.java @@ -25,6 +25,7 @@ import android.animation.AnimatorListenerAdapter; import android.view.animation.Interpolator; import com.android.launcher3.Launcher; +import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.uioverrides.states.OverviewState; /** @@ -48,7 +49,7 @@ public class ShelfPeekAnim { * Animates to the given state, canceling the previous animation if it was still running. */ public void setShelfState(ShelfAnimState shelfState, Interpolator interpolator, long duration) { - if (mShelfState == shelfState) { + if (mShelfState == shelfState || FeatureFlags.ENABLE_OVERVIEW_ACTIONS.get()) { return; } mLauncher.getStateManager().cancelStateElementAnimation(INDEX_SHELF_ANIM); diff --git a/quickstep/src/com/android/quickstep/QuickstepProcessInitializer.java b/quickstep/src/com/android/quickstep/QuickstepProcessInitializer.java index befeee0db9..9817e32ae9 100644 --- a/quickstep/src/com/android/quickstep/QuickstepProcessInitializer.java +++ b/quickstep/src/com/android/quickstep/QuickstepProcessInitializer.java @@ -15,6 +15,11 @@ */ package com.android.quickstep; +import static android.content.Context.MODE_PRIVATE; + +import static com.android.launcher3.config.FeatureFlags.ENABLE_OVERVIEW_ACTIONS; +import static com.android.launcher3.config.FeatureFlags.FLAGS_PREF_NAME; + import android.content.Context; import android.content.pm.PackageManager; import android.os.UserManager; @@ -22,6 +27,7 @@ import android.util.Log; import com.android.launcher3.BuildConfig; import com.android.launcher3.MainProcessInitializer; +import com.android.launcher3.config.FeatureFlags; import com.android.systemui.shared.system.ThreadedRendererCompat; @SuppressWarnings("unused") @@ -50,5 +56,21 @@ public class QuickstepProcessInitializer extends MainProcessInitializer { // Elevate GPU priority for Quickstep and Remote animations. ThreadedRendererCompat.setContextPriority(ThreadedRendererCompat.EGL_CONTEXT_PRIORITY_HIGH_IMG); + + // Force disable some feature flags based on the system ui navigation mode. + SysUINavigationMode.Mode currMode = SysUINavigationMode.INSTANCE.get(context) + .addModeChangeListener(mode -> disableFeatureFlagsForSysuiNavMode(context, mode)); + disableFeatureFlagsForSysuiNavMode(context, currMode); + } + + private void disableFeatureFlagsForSysuiNavMode(Context ctx, SysUINavigationMode.Mode mode) { + if (mode == SysUINavigationMode.Mode.TWO_BUTTONS) { + ctx.getSharedPreferences(FLAGS_PREF_NAME, MODE_PRIVATE) + .edit() + .putBoolean(ENABLE_OVERVIEW_ACTIONS.key, false) + .apply(); + + FeatureFlags.initialize(ctx); + } } } diff --git a/quickstep/src/com/android/quickstep/views/ShelfScrimView.java b/quickstep/src/com/android/quickstep/views/ShelfScrimView.java index 7885f5cb9b..7ed1e21dfb 100644 --- a/quickstep/src/com/android/quickstep/views/ShelfScrimView.java +++ b/quickstep/src/com/android/quickstep/views/ShelfScrimView.java @@ -41,6 +41,8 @@ import com.android.launcher3.LauncherState; import com.android.launcher3.R; import com.android.launcher3.Utilities; import com.android.launcher3.anim.Interpolators; +import com.android.launcher3.config.FeatureFlags; +import com.android.launcher3.uioverrides.states.OverviewState; import com.android.launcher3.util.Themes; import com.android.launcher3.views.ScrimView; import com.android.quickstep.SysUINavigationMode; @@ -155,12 +157,18 @@ public class ShelfScrimView extends ScrimView mRemainingScreenPathValid = false; mShiftRange = mLauncher.getAllAppsController().getShiftRange(); + Context context = getContext(); if ((OVERVIEW.getVisibleElements(mLauncher) & ALL_APPS_HEADER_EXTRA) == 0) { - mMidProgress = 1; mDragHandleProgress = 1; - mMidAlpha = 0; + if (FeatureFlags.ENABLE_OVERVIEW_ACTIONS.get()) { + // Fade in all apps background quickly to distinguish from swiping from nav bar. + mMidAlpha = Themes.getAttrInteger(context, R.attr.allAppsInterimScrimAlpha); + mMidProgress = OverviewState.getDefaultVerticalProgress(mLauncher); + } else { + mMidAlpha = 0; + mMidProgress = 1; + } } else { - Context context = getContext(); mMidAlpha = Themes.getAttrInteger(context, R.attr.allAppsInterimScrimAlpha); mMidProgress = OVERVIEW.getVerticalProgress(mLauncher); Rect hotseatPadding = dp.getHotseatLayoutPadding(); diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index 06f3453981..2ccdd6f4a9 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -1180,6 +1180,11 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns, mDropTargetBar.setup(mDragController); mAllAppsController.setupViews(mAppsView); + + if (FeatureFlags.ENABLE_OVERVIEW_ACTIONS.get()) { + // Overview is above all other launcher elements, including qsb, so move it to the top. + mOverviewPanel.bringToFront(); + } } /** diff --git a/src/com/android/launcher3/LauncherAnimUtils.java b/src/com/android/launcher3/LauncherAnimUtils.java index 74362ed5a3..cdfd257d6d 100644 --- a/src/com/android/launcher3/LauncherAnimUtils.java +++ b/src/com/android/launcher3/LauncherAnimUtils.java @@ -16,6 +16,8 @@ package com.android.launcher3; +import static com.android.launcher3.config.FeatureFlags.ENABLE_OVERVIEW_ACTIONS; + import android.graphics.drawable.Drawable; import android.util.FloatProperty; import android.util.Property; @@ -28,9 +30,10 @@ public class LauncherAnimUtils { * easier access from static classes and enums */ public static final int ALL_APPS_TRANSITION_MS = 320; - public static final int OVERVIEW_TRANSITION_MS = 250; + public static final int OVERVIEW_TRANSITION_MS = ENABLE_OVERVIEW_ACTIONS.get() ? 380 : 250; public static final int SPRING_LOADED_TRANSITION_MS = 150; public static final int SPRING_LOADED_EXIT_DELAY = 500; + public static final int HINT_TRANSITION_MS = 80; // The progress of an animation to all apps must be at least this far along to snap to all apps. public static final float MIN_PROGRESS_TO_ALL_APPS = 0.5f; diff --git a/src/com/android/launcher3/LauncherState.java b/src/com/android/launcher3/LauncherState.java index d2b447b4b0..8b80cba7ad 100644 --- a/src/com/android/launcher3/LauncherState.java +++ b/src/com/android/launcher3/LauncherState.java @@ -30,9 +30,11 @@ import static com.android.launcher3.anim.Interpolators.ACCEL_2; import static com.android.launcher3.anim.Interpolators.DEACCEL; import static com.android.launcher3.anim.Interpolators.DEACCEL_1_7; import static com.android.launcher3.anim.Interpolators.clampToProgress; +import static com.android.launcher3.config.FeatureFlags.ENABLE_OVERVIEW_ACTIONS; import static com.android.launcher3.states.RotationHelper.REQUEST_NONE; import static com.android.launcher3.testing.TestProtocol.ALL_APPS_STATE_ORDINAL; import static com.android.launcher3.testing.TestProtocol.BACKGROUND_APP_STATE_ORDINAL; +import static com.android.launcher3.testing.TestProtocol.HINT_STATE_ORDINAL; import static com.android.launcher3.testing.TestProtocol.NORMAL_STATE_ORDINAL; import static com.android.launcher3.testing.TestProtocol.OVERVIEW_PEEK_STATE_ORDINAL; import static com.android.launcher3.testing.TestProtocol.OVERVIEW_STATE_ORDINAL; @@ -42,6 +44,7 @@ import static com.android.launcher3.testing.TestProtocol.SPRING_LOADED_STATE_ORD import android.view.animation.Interpolator; import com.android.launcher3.anim.AnimatorSetBuilder; +import com.android.launcher3.states.HintState; import com.android.launcher3.states.SpringLoadedState; import com.android.launcher3.uioverrides.states.AllAppsState; import com.android.launcher3.uioverrides.states.OverviewState; @@ -88,7 +91,7 @@ public class LauncherState { } }; - private static final LauncherState[] sAllStates = new LauncherState[7]; + private static final LauncherState[] sAllStates = new LauncherState[8]; /** * TODO: Create a separate class for NORMAL state. @@ -104,6 +107,7 @@ public class LauncherState { public static final LauncherState SPRING_LOADED = new SpringLoadedState( SPRING_LOADED_STATE_ORDINAL); public static final LauncherState ALL_APPS = new AllAppsState(ALL_APPS_STATE_ORDINAL); + public static final LauncherState HINT_STATE = new HintState(HINT_STATE_ORDINAL); public static final LauncherState OVERVIEW = new OverviewState(OVERVIEW_STATE_ORDINAL); public static final LauncherState OVERVIEW_PEEK = @@ -212,6 +216,10 @@ public class LauncherState { return launcher.getOverviewScaleAndTranslationForNormalState(); } + public ScaleAndTranslation getQsbScaleAndTranslation(Launcher launcher) { + return new ScaleAndTranslation(1, 0, 0); + } + public float getOverviewFullscreenProgress() { return 0; } @@ -319,6 +327,10 @@ public class LauncherState { if (!isHotseatVisible) { hotseat.setScaleX(0.92f); hotseat.setScaleY(0.92f); + if (ENABLE_OVERVIEW_ACTIONS.get()) { + launcher.getAppsView().setScaleX(0.92f); + launcher.getAppsView().setScaleY(0.92f); + } } } else if (this == NORMAL && fromState == OVERVIEW_PEEK) { // Keep fully visible until the very end (when overview is offscreen) to make invisible. diff --git a/src/com/android/launcher3/Utilities.java b/src/com/android/launcher3/Utilities.java index af9a1b4e35..8bfc02802c 100644 --- a/src/com/android/launcher3/Utilities.java +++ b/src/com/android/launcher3/Utilities.java @@ -75,7 +75,6 @@ import com.android.launcher3.views.Transposable; import com.android.launcher3.widget.PendingAddShortcutInfo; import java.lang.reflect.Method; -import java.util.Arrays; import java.util.List; import java.util.Locale; import java.util.regex.Matcher; @@ -382,7 +381,7 @@ public final class Utilities { return res.getConfiguration().getLayoutDirection() == View.LAYOUT_DIRECTION_RTL; } - public static float dpiFromPx(int size, DisplayMetrics metrics){ + public static float dpiFromPx(float size, DisplayMetrics metrics) { float densityRatio = (float) metrics.densityDpi / DisplayMetrics.DENSITY_DEFAULT; return (size / densityRatio); } diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java index 327896013f..c73a5e1efb 100644 --- a/src/com/android/launcher3/Workspace.java +++ b/src/com/android/launcher3/Workspace.java @@ -3256,7 +3256,8 @@ public class Workspace extends PagedView return mOverlayShown; } - void moveToDefaultScreen() { + /** Calls {@link #snapToPage(int)} on the {@link #DEFAULT_PAGE}, then requests focus on it. */ + public void moveToDefaultScreen() { int page = DEFAULT_PAGE; if (!workspaceInModalState() && getNextPage() != page) { snapToPage(page); diff --git a/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java b/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java index 7a7e1fee6c..c33392d53d 100644 --- a/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java +++ b/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java @@ -36,6 +36,7 @@ import android.view.animation.Interpolator; import com.android.launcher3.LauncherState.PageAlphaProvider; import com.android.launcher3.LauncherState.ScaleAndTranslation; import com.android.launcher3.LauncherStateManager.AnimationConfig; +import com.android.launcher3.allapps.AllAppsContainerView; import com.android.launcher3.anim.AnimatorSetBuilder; import com.android.launcher3.anim.PropertySetter; import com.android.launcher3.graphics.WorkspaceAndHotseatScrim; @@ -77,6 +78,7 @@ public class WorkspaceStateTransitionAnimation { ScaleAndTranslation scaleAndTranslation = state.getWorkspaceScaleAndTranslation(mLauncher); ScaleAndTranslation hotseatScaleAndTranslation = state.getHotseatScaleAndTranslation( mLauncher); + ScaleAndTranslation qsbScaleAndTranslation = state.getQsbScaleAndTranslation(mLauncher); mNewScale = scaleAndTranslation.scale; PageAlphaProvider pageAlphaProvider = state.getWorkspacePageAlphaProvider(mLauncher); final int childCount = mWorkspace.getChildCount(); @@ -90,24 +92,24 @@ public class WorkspaceStateTransitionAnimation { pageAlphaProvider.interpolator); boolean playAtomicComponent = config.playAtomicOverviewScaleComponent(); Hotseat hotseat = mWorkspace.getHotseat(); + // Since we set the pivot relative to mWorkspace, we need to scale a sibling of Workspace. + AllAppsContainerView qsbScaleView = mLauncher.getAppsView(); + View qsbView = qsbScaleView.getSearchView(); if (playAtomicComponent) { Interpolator scaleInterpolator = builder.getInterpolator(ANIM_WORKSPACE_SCALE, ZOOM_OUT); propertySetter.setFloat(mWorkspace, SCALE_PROPERTY, mNewScale, scaleInterpolator); if (!hotseat.getRotationMode().isTransposed) { - // Set the hotseat's pivot point to match the workspace's, so that it scales - // together. Since both hotseat and workspace can move, transform the point - // manually instead of using dragLayer.getDescendantCoordRelativeToSelf and - // related methods. - hotseat.setPivotY(mWorkspace.getPivotY() + mWorkspace.getTop() - hotseat.getTop()); - hotseat.setPivotX(mWorkspace.getPivotX() - + mWorkspace.getLeft() - hotseat.getLeft()); + setPivotToScaleWithWorkspace(hotseat); + setPivotToScaleWithWorkspace(qsbScaleView); } float hotseatScale = hotseatScaleAndTranslation.scale; Interpolator hotseatScaleInterpolator = builder.getInterpolator(ANIM_HOTSEAT_SCALE, scaleInterpolator); propertySetter.setFloat(hotseat, SCALE_PROPERTY, hotseatScale, hotseatScaleInterpolator); + propertySetter.setFloat(qsbScaleView, SCALE_PROPERTY, qsbScaleAndTranslation.scale, + hotseatScaleInterpolator); float hotseatIconsAlpha = (elements & HOTSEAT_ICONS) != 0 ? 1 : 0; propertySetter.setViewAlpha(hotseat, hotseatIconsAlpha, fadeInterpolator); @@ -134,10 +136,24 @@ public class WorkspaceStateTransitionAnimation { hotseatScaleAndTranslation.translationY, hotseatTranslationInterpolator); propertySetter.setFloat(mWorkspace.getPageIndicator(), View.TRANSLATION_Y, hotseatScaleAndTranslation.translationY, hotseatTranslationInterpolator); + propertySetter.setFloat(qsbView, View.TRANSLATION_Y, + qsbScaleAndTranslation.translationY, hotseatTranslationInterpolator); setScrim(propertySetter, state); } + /** + * Set the given view's pivot point to match the workspace's, so that it scales together. Since + * both this view and workspace can move, transform the point manually instead of using + * dragLayer.getDescendantCoordRelativeToSelf and related methods. + */ + private void setPivotToScaleWithWorkspace(View sibling) { + sibling.setPivotY(mWorkspace.getPivotY() + mWorkspace.getTop() + - sibling.getTop() - sibling.getTranslationY()); + sibling.setPivotX(mWorkspace.getPivotX() + mWorkspace.getLeft() + - sibling.getLeft() - sibling.getTranslationX()); + } + public void setScrim(PropertySetter propertySetter, LauncherState state) { WorkspaceAndHotseatScrim scrim = mLauncher.getDragLayer().getScrim(); propertySetter.setFloat(scrim, SCRIM_PROGRESS, state.getWorkspaceScrimAlpha(mLauncher), diff --git a/src/com/android/launcher3/anim/AnimatorPlaybackController.java b/src/com/android/launcher3/anim/AnimatorPlaybackController.java index 4a52795f89..1c277ab8c0 100644 --- a/src/com/android/launcher3/anim/AnimatorPlaybackController.java +++ b/src/com/android/launcher3/anim/AnimatorPlaybackController.java @@ -26,6 +26,7 @@ import android.animation.TimeInterpolator; import android.animation.ValueAnimator; import android.util.Log; +import androidx.annotation.Nullable; import androidx.dynamicanimation.animation.DynamicAnimation; import androidx.dynamicanimation.animation.SpringAnimation; @@ -250,14 +251,24 @@ public abstract class AnimatorPlaybackController implements ValueAnimator.Animat } } + /** @see #dispatchOnCancelWithoutCancelRunnable(Runnable) */ + public void dispatchOnCancelWithoutCancelRunnable() { + dispatchOnCancelWithoutCancelRunnable(null); + } + /** * Sets mOnCancelRunnable = null before dispatching the cancel and restoring the runnable. This * is intended to be used only if you need to cancel but want to defer cleaning up yourself. + * @param callback An optional callback to run after dispatching the cancel but before resetting + * the onCancelRunnable. */ - public void dispatchOnCancelWithoutCancelRunnable() { + public void dispatchOnCancelWithoutCancelRunnable(@Nullable Runnable callback) { Runnable onCancel = mOnCancelRunnable; setOnCancelRunnable(null); dispatchOnCancel(); + if (callback != null) { + callback.run(); + } setOnCancelRunnable(onCancel); } diff --git a/src/com/android/launcher3/config/FeatureFlags.java b/src/com/android/launcher3/config/FeatureFlags.java index 8ccb369138..b1a2c3368d 100644 --- a/src/com/android/launcher3/config/FeatureFlags.java +++ b/src/com/android/launcher3/config/FeatureFlags.java @@ -117,7 +117,8 @@ public final class FeatureFlags { "Show launcher preview in grid picker"); public static final BooleanFlag ENABLE_OVERVIEW_ACTIONS = getDebugFlag( - "ENABLE_OVERVIEW_ACTIONS", false, "Show app actions in Overview"); + "ENABLE_OVERVIEW_ACTIONS", false, "Show app actions instead of the shelf in Overview." + + " As part of this decoupling, also distinguish swipe up from nav bar vs above it."); public static final BooleanFlag ENABLE_DATABASE_RESTORE = getDebugFlag( "ENABLE_DATABASE_RESTORE", true, diff --git a/src/com/android/launcher3/dragndrop/DragLayer.java b/src/com/android/launcher3/dragndrop/DragLayer.java index 8823bde624..92f35e2b19 100644 --- a/src/com/android/launcher3/dragndrop/DragLayer.java +++ b/src/com/android/launcher3/dragndrop/DragLayer.java @@ -536,6 +536,9 @@ public class DragLayer extends BaseDragLayer { mOverviewScrim.updateCurrentScrimmedView(this); mFocusIndicatorHelper.draw(canvas); super.dispatchDraw(canvas); + if (mOverviewScrim.getScrimmedView() == null) { + mOverviewScrim.draw(canvas); + } } @Override diff --git a/src/com/android/launcher3/graphics/OverviewScrim.java b/src/com/android/launcher3/graphics/OverviewScrim.java index d707403ed2..94acbfdcca 100644 --- a/src/com/android/launcher3/graphics/OverviewScrim.java +++ b/src/com/android/launcher3/graphics/OverviewScrim.java @@ -55,16 +55,17 @@ public class OverviewScrim extends Scrim { mCurrentScrimmedView = mStableScrimmedView; int currentIndex = root.indexOfChild(mCurrentScrimmedView); final int childCount = root.getChildCount(); - while (mCurrentScrimmedView.getVisibility() != VISIBLE && currentIndex < childCount) { + while (mCurrentScrimmedView != null && mCurrentScrimmedView.getVisibility() != VISIBLE + && currentIndex < childCount) { currentIndex++; mCurrentScrimmedView = root.getChildAt(currentIndex); } } /** - * @return The view to draw the scrim behind. + * @return The view to draw the scrim behind, or null if all visible views should be scrimmed. */ - public View getScrimmedView() { + public @Nullable View getScrimmedView() { return mCurrentScrimmedView; } } diff --git a/src/com/android/launcher3/states/HintState.java b/src/com/android/launcher3/states/HintState.java new file mode 100644 index 0000000000..cb56097f05 --- /dev/null +++ b/src/com/android/launcher3/states/HintState.java @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2020 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.states; + +import static com.android.launcher3.LauncherAnimUtils.HINT_TRANSITION_MS; + +import com.android.launcher3.Launcher; +import com.android.launcher3.LauncherState; +import com.android.launcher3.Workspace; +import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType; + +/** + * Scale down workspace/hotseat to hint at going to either overview (on pause) or first home screen. + */ +public class HintState extends LauncherState { + + private static final int STATE_FLAGS = FLAG_DISABLE_ACCESSIBILITY | FLAG_DISABLE_RESTORE + | FLAG_HAS_SYS_UI_SCRIM; + + public HintState(int id) { + super(id, ContainerType.DEFAULT_CONTAINERTYPE, HINT_TRANSITION_MS, STATE_FLAGS); + } + + @Override + public ScaleAndTranslation getWorkspaceScaleAndTranslation(Launcher launcher) { + return new ScaleAndTranslation(0.9f, 0, 0); + } + + @Override + public ScaleAndTranslation getQsbScaleAndTranslation(Launcher launcher) { + // Treat the QSB as part of the hotseat so they move together. + return getHotseatScaleAndTranslation(launcher); + } + + @Override + public void onStateTransitionEnd(Launcher launcher) { + launcher.getStateManager().goToState(NORMAL); + Workspace workspace = launcher.getWorkspace(); + workspace.post(workspace::moveToDefaultScreen); + } +} diff --git a/src/com/android/launcher3/testing/TestProtocol.java b/src/com/android/launcher3/testing/TestProtocol.java index 01c207f907..5c200509d9 100644 --- a/src/com/android/launcher3/testing/TestProtocol.java +++ b/src/com/android/launcher3/testing/TestProtocol.java @@ -31,6 +31,7 @@ public final class TestProtocol { public static final int QUICK_SWITCH_STATE_ORDINAL = 4; public static final int ALL_APPS_STATE_ORDINAL = 5; public static final int BACKGROUND_APP_STATE_ORDINAL = 6; + public static final int HINT_STATE_ORDINAL = 7; public static final String TAPL_EVENTS_TAG = "TaplEvents"; public static String stateOrdinalToString(int ordinal) { @@ -49,6 +50,8 @@ public final class TestProtocol { return "AllApps"; case BACKGROUND_APP_STATE_ORDINAL: return "Background"; + case HINT_STATE_ORDINAL: + return "Hint"; default: return null; } diff --git a/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java b/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java index d193bef40d..3ec480d43e 100644 --- a/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java +++ b/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java @@ -525,7 +525,11 @@ public abstract class AbstractStateChangeTouchController if (targetState != mStartState) { logReachedState(logAction, targetState); } - mLauncher.getStateManager().goToState(targetState, false /* animated */); + if (!mLauncher.isInState(targetState)) { + // If we're already in the target state, don't jump to it at the end of the animation in + // case the user started interacting with it before the animation finished. + mLauncher.getStateManager().goToState(targetState, false /* animated */); + } mLauncher.getDragLayer().getScrim().animateToSysuiMultiplier(1, 0, 0); } From fd239cafbed7b74e1abb8e56a41be5f9d17fc96a Mon Sep 17 00:00:00 2001 From: Tony Wickham Date: Wed, 6 Nov 2019 14:27:23 -0800 Subject: [PATCH 3/3] Translate recents slightly while dragging after pausing There's a lot of resistance, but feels better than nothing responding to your movement. Bug: 143361609 Change-Id: I9d7e06279ebdbaa0317909ce96d6f001dbe9699a --- ...ButtonNavbarToOverviewTouchController.java | 40 +++++++++++++++++-- .../touch/SingleAxisSwipeDetector.java | 24 ++++++----- .../touch/SingleAxisSwipeDetectorTest.java | 5 ++- 3 files changed, 55 insertions(+), 14 deletions(-) diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonNavbarToOverviewTouchController.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonNavbarToOverviewTouchController.java index a993f3f53b..2ac2d2de8e 100644 --- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonNavbarToOverviewTouchController.java +++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonNavbarToOverviewTouchController.java @@ -21,11 +21,13 @@ import static com.android.launcher3.LauncherState.NORMAL; import static com.android.launcher3.LauncherState.OVERVIEW; import static com.android.launcher3.LauncherStateManager.ATOMIC_OVERVIEW_PEEK_COMPONENT; import static com.android.launcher3.Utilities.EDGE_NAV_BAR; +import static com.android.launcher3.anim.Interpolators.ACCEL_DEACCEL; import static com.android.launcher3.util.VibratorWrapper.OVERVIEW_HAPTIC; import android.animation.Animator; import android.animation.AnimatorSet; import android.animation.ValueAnimator; +import android.graphics.PointF; import android.view.MotionEvent; import com.android.launcher3.Launcher; @@ -37,6 +39,7 @@ import com.android.launcher3.anim.AnimatorSetBuilder; import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch; import com.android.launcher3.util.VibratorWrapper; import com.android.quickstep.util.StaggeredWorkspaceAnim; +import com.android.quickstep.views.RecentsView; /** * Touch controller which handles swipe and hold from the nav bar to go to Overview. Swiping above @@ -45,11 +48,22 @@ import com.android.quickstep.util.StaggeredWorkspaceAnim; */ public class NoButtonNavbarToOverviewTouchController extends FlingAndHoldTouchController { + + // How much of the movement to use for translating overview after swipe and hold. + private static final float OVERVIEW_MOVEMENT_FACTOR = 0.25f; + private static final long TRANSLATION_ANIM_MIN_DURATION_MS = 80; + private static final float TRANSLATION_ANIM_VELOCITY_DP_PER_MS = 0.8f; + + private final RecentsView mRecentsView; + private boolean mDidTouchStartInNavBar; private boolean mReachedOverview; + // The last recorded displacement before we reached overview. + private PointF mStartDisplacement = new PointF(); public NoButtonNavbarToOverviewTouchController(Launcher l) { super(l); + mRecentsView = l.getOverviewPanel(); } @Override @@ -125,12 +139,20 @@ public class NoButtonNavbarToOverviewTouchController extends FlingAndHoldTouchCo } @Override - public boolean onDrag(float displacement, MotionEvent event) { + public boolean onDrag(float yDisplacement, float xDisplacement, MotionEvent event) { if (mMotionPauseDetector.isPaused()) { + if (!mReachedOverview) { + mStartDisplacement.set(xDisplacement, yDisplacement); + } else { + mRecentsView.setTranslationX((xDisplacement - mStartDisplacement.x) + * OVERVIEW_MOVEMENT_FACTOR); + mRecentsView.setTranslationY((yDisplacement - mStartDisplacement.y) + * OVERVIEW_MOVEMENT_FACTOR); + } // Stay in Overview. return true; } - return super.onDrag(displacement, event); + return super.onDrag(yDisplacement, xDisplacement, event); } @Override @@ -164,7 +186,19 @@ public class NoButtonNavbarToOverviewTouchController extends FlingAndHoldTouchCo anim.start(); } } else { - maybeSwipeInteractionToOverviewComplete(); + if (mReachedOverview) { + float distanceDp = dpiFromPx(Math.max( + Math.abs(mRecentsView.getTranslationX()), + Math.abs(mRecentsView.getTranslationY()))); + long duration = (long) Math.max(TRANSLATION_ANIM_MIN_DURATION_MS, + distanceDp / TRANSLATION_ANIM_VELOCITY_DP_PER_MS); + mRecentsView.animate() + .translationX(0) + .translationY(0) + .setInterpolator(ACCEL_DEACCEL) + .setDuration(duration) + .withEndAction(this::maybeSwipeInteractionToOverviewComplete); + } } } diff --git a/src/com/android/launcher3/touch/SingleAxisSwipeDetector.java b/src/com/android/launcher3/touch/SingleAxisSwipeDetector.java index f2ebc45193..9d406f3bef 100644 --- a/src/com/android/launcher3/touch/SingleAxisSwipeDetector.java +++ b/src/com/android/launcher3/touch/SingleAxisSwipeDetector.java @@ -54,8 +54,8 @@ public class SingleAxisSwipeDetector extends BaseSwipeDetector { } @Override - boolean canScrollStart(PointF displacement, float touchSlop) { - return Math.abs(displacement.y) >= Math.max(Math.abs(displacement.x), touchSlop); + float extractOrthogonalDirection(PointF direction) { + return direction.x; } }; @@ -80,9 +80,10 @@ public class SingleAxisSwipeDetector extends BaseSwipeDetector { } @Override - boolean canScrollStart(PointF displacement, float touchSlop) { - return Math.abs(displacement.x) >= Math.max(Math.abs(displacement.y), touchSlop); + float extractOrthogonalDirection(PointF direction) { + return direction.y; } + }; private final Direction mDir; @@ -126,7 +127,9 @@ public class SingleAxisSwipeDetector extends BaseSwipeDetector { @Override protected boolean shouldScrollStart(PointF displacement) { // Reject cases where the angle or slop condition is not met. - if (!mDir.canScrollStart(displacement, mTouchSlop)) { + float minDisplacement = Math.max(mTouchSlop, + Math.abs(mDir.extractOrthogonalDirection(displacement))); + if (Math.abs(mDir.extractDirection(displacement)) < minDisplacement) { return false; } @@ -150,7 +153,8 @@ public class SingleAxisSwipeDetector extends BaseSwipeDetector { @Override protected void reportDraggingInternal(PointF displacement, MotionEvent event) { - mListener.onDrag(mDir.extractDirection(displacement), event); + mListener.onDrag(mDir.extractDirection(displacement), + mDir.extractOrthogonalDirection(displacement), event); } @Override @@ -164,13 +168,16 @@ public class SingleAxisSwipeDetector extends BaseSwipeDetector { /** @param start whether this was the original drag start, as opposed to a recatch. */ void onDragStart(boolean start); - // TODO remove boolean onDrag(float displacement); default boolean onDrag(float displacement, MotionEvent event) { return onDrag(displacement); } + default boolean onDrag(float displacement, float orthogonalDisplacement, MotionEvent ev) { + return onDrag(displacement, ev); + } + void onDragEnd(float velocity); } @@ -183,8 +190,7 @@ public class SingleAxisSwipeDetector extends BaseSwipeDetector { /** Returns the part of the given {@link PointF} that is relevant to this direction. */ abstract float extractDirection(PointF point); - /** Reject cases where the angle or slop condition is not met. */ - abstract boolean canScrollStart(PointF displacement, float touchSlop); + abstract float extractOrthogonalDirection(PointF point); } } diff --git a/tests/src/com/android/launcher3/touch/SingleAxisSwipeDetectorTest.java b/tests/src/com/android/launcher3/touch/SingleAxisSwipeDetectorTest.java index 6d463b5684..b0ece77c9b 100644 --- a/tests/src/com/android/launcher3/touch/SingleAxisSwipeDetectorTest.java +++ b/tests/src/com/android/launcher3/touch/SingleAxisSwipeDetectorTest.java @@ -22,15 +22,16 @@ import static com.android.launcher3.touch.SingleAxisSwipeDetector.HORIZONTAL; import static com.android.launcher3.touch.SingleAxisSwipeDetector.VERTICAL; import static org.junit.Assert.assertTrue; +import static org.mockito.Matchers.any; import static org.mockito.Matchers.anyBoolean; import static org.mockito.Matchers.anyFloat; -import static org.mockito.Matchers.anyObject; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import android.util.Log; +import android.view.MotionEvent; import android.view.ViewConfiguration; import androidx.test.InstrumentationRegistry; @@ -158,7 +159,7 @@ public class SingleAxisSwipeDetectorTest { mGenerator.put(0, 100, 100); mGenerator.move(0, 100, 100 + mTouchSlop); // TODO: actually calculate the following parameters and do exact value checks. - verify(mMockListener).onDrag(anyFloat(), anyObject()); + verify(mMockListener).onDrag(anyFloat(), anyFloat(), any(MotionEvent.class)); } @Test