From dbfe998db9d0e270f5ff722e77bf4b874fd427ff Mon Sep 17 00:00:00 2001 From: Jon Miranda Date: Wed, 12 Jun 2019 09:40:11 -0700 Subject: [PATCH 01/41] Fix bug where recents animation is out of sync. This is caused by recent change where we animate RecentsView instead of the individual tasks when we are launching the center task. The fix is to not apply scale/translation to the individual tasks when we are launching the center task. Bug: 133450867 Change-Id: I463b5819c6c253001752c3dafdbd61009deba8b4 --- .../src/com/android/quickstep/TaskViewUtils.java | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskViewUtils.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskViewUtils.java index d0ea73a6fe..6897c1e7d7 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskViewUtils.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskViewUtils.java @@ -29,8 +29,6 @@ import android.view.View; import com.android.launcher3.BaseActivity; import com.android.launcher3.BaseDraggingActivity; import com.android.launcher3.ItemInfo; -import com.android.launcher3.Launcher; -import com.android.launcher3.LauncherAppState; import com.android.launcher3.Utilities; import com.android.quickstep.util.ClipAnimationHelper; import com.android.quickstep.util.MultiValueUpdateListener; @@ -123,6 +121,7 @@ public final class TaskViewUtils { new RemoteAnimationTargetSet(targets, MODE_OPENING); targetSet.addDependentTransactionApplier(applier); + final RecentsView recentsView = v.getRecentsView(); final ValueAnimator appAnimator = ValueAnimator.ofFloat(0, 1); appAnimator.setInterpolator(TOUCH_RESPONSE_INTERPOLATOR); appAnimator.addUpdateListener(new MultiValueUpdateListener() { @@ -153,7 +152,10 @@ public final class TaskViewUtils { // TODO: Take into account the current fullscreen progress for animating the insets params.setProgress(1 - percent); RectF taskBounds = inOutHelper.applyTransform(targetSet, params); - if (!skipViewChanges) { + int taskIndex = recentsView.indexOfChild(v); + int centerTaskIndex = recentsView.getCurrentPage(); + boolean parallaxCenterAndAdjacentTask = taskIndex != centerTaskIndex; + if (!skipViewChanges && parallaxCenterAndAdjacentTask) { float scale = taskBounds.width() / mThumbnailRect.width(); v.setScaleX(scale); v.setScaleY(scale); From 921163a3e8b4ad6764d41953d594f71281e4a69a Mon Sep 17 00:00:00 2001 From: vadimt Date: Mon, 17 Jun 2019 13:24:40 -0700 Subject: [PATCH 02/41] Restoring wait time 1 min Apparently, some tests fail with 10 sec Bug: 133765434 Change-Id: I13fc39c42c665675d15a66da99454e05387c11f4 --- .../com/android/launcher3/tapl/LauncherInstrumentation.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java index 2db9d08269..f4e64c19d6 100644 --- a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java +++ b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java @@ -122,7 +122,7 @@ public final class LauncherInstrumentation { private static final String APPS_RES_ID = "apps_view"; private static final String OVERVIEW_RES_ID = "overview_panel"; private static final String WIDGETS_RES_ID = "widgets_list_view"; - public static final int WAIT_TIME_MS = 10000; + public static final int WAIT_TIME_MS = 60000; private static final String SYSTEMUI_PACKAGE = "com.android.systemui"; private static WeakReference sActiveContainer = new WeakReference<>(null); From 06d30e54d597f5ec0c914966055a1e2b2aedbed8 Mon Sep 17 00:00:00 2001 From: vadimt Date: Tue, 11 Jun 2019 19:15:14 -0700 Subject: [PATCH 03/41] Enabling tests in landscape mode Bug: 121280703 Change-Id: I3245ca8d0b0496035658ce2a602882a62942f3f1 --- .../android/quickstep/TaplTestsQuickstep.java | 2 +- .../launcher3/ui/AbstractLauncherUiTest.java | 44 +------------ .../launcher3/ui/PortraitLandscapeRunner.java | 63 +++++++++++++++++++ .../launcher3/ui/TaplTestsLauncher3.java | 6 +- .../ui/widget/RequestPinItemTest.java | 2 +- .../util/rule/LauncherActivityRule.java | 5 -- .../android/launcher3/tapl/Background.java | 30 ++++++--- .../tapl/LauncherInstrumentation.java | 6 +- .../com/android/launcher3/tapl/Workspace.java | 4 +- 9 files changed, 98 insertions(+), 64 deletions(-) create mode 100644 tests/src/com/android/launcher3/ui/PortraitLandscapeRunner.java diff --git a/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java b/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java index f02859f462..9e3bf2f56e 100644 --- a/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java +++ b/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java @@ -208,7 +208,7 @@ public class TaplTestsQuickstep extends AbstractQuickStepTest { @Test @NavigationModeSwitch - @PortraitLandscape +// @PortraitLandscape public void testBackground() throws Exception { startAppFast(resolveSystemApp(Intent.CATEGORY_APP_CALCULATOR)); final Background background = mLauncher.getBackground(); diff --git a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java index abc93cd4b7..e663e70305 100644 --- a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java +++ b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java @@ -35,7 +35,6 @@ import android.content.pm.PackageManager; import android.os.Process; import android.os.RemoteException; import android.util.Log; -import android.view.Surface; import androidx.test.InstrumentationRegistry; import androidx.test.uiautomator.By; @@ -67,7 +66,6 @@ import org.junit.Before; import org.junit.Rule; import org.junit.rules.RuleChain; import org.junit.rules.TestRule; -import org.junit.runners.model.Statement; import java.io.IOException; import java.lang.annotation.ElementType; @@ -124,46 +122,10 @@ public abstract class AbstractLauncherUiTest { protected @interface PortraitLandscape { } - @Rule - public TestRule mPortraitLandscapeExecutor = - (base, description) -> false && description.getAnnotation(PortraitLandscape.class) - != null ? new Statement() { - @Override - public void evaluate() throws Throwable { - try { - // Create launcher activity if necessary and bring it to the front. - mLauncher.pressHome(); - waitForLauncherCondition("Launcher activity wasn't created", - launcher -> launcher != null); - - executeOnLauncher(launcher -> - launcher.getRotationHelper().forceAllowRotationForTesting(true)); - - evaluateInPortrait(); - evaluateInLandscape(); - } finally { - mDevice.setOrientationNatural(); - executeOnLauncher(launcher -> - launcher.getRotationHelper().forceAllowRotationForTesting(false)); - mLauncher.setExpectedRotation(Surface.ROTATION_0); - } - } - - private void evaluateInPortrait() throws Throwable { - mDevice.setOrientationNatural(); - mLauncher.setExpectedRotation(Surface.ROTATION_0); - base.evaluate(); - } - - private void evaluateInLandscape() throws Throwable { - mDevice.setOrientationLeft(); - mLauncher.setExpectedRotation(Surface.ROTATION_90); - base.evaluate(); - } - } : base; - protected TestRule getRulesInsideActivityMonitor() { - return new FailureWatcher(this); + return RuleChain. + outerRule(new PortraitLandscapeRunner(this)). + around(new FailureWatcher(this)); } @Rule diff --git a/tests/src/com/android/launcher3/ui/PortraitLandscapeRunner.java b/tests/src/com/android/launcher3/ui/PortraitLandscapeRunner.java new file mode 100644 index 0000000000..20cd1e7d2e --- /dev/null +++ b/tests/src/com/android/launcher3/ui/PortraitLandscapeRunner.java @@ -0,0 +1,63 @@ +package com.android.launcher3.ui; + +import android.view.Surface; + +import com.android.launcher3.tapl.TestHelpers; + +import org.junit.rules.TestRule; +import org.junit.runner.Description; +import org.junit.runners.model.Statement; + +class PortraitLandscapeRunner implements TestRule { + private AbstractLauncherUiTest mTest; + + public PortraitLandscapeRunner(AbstractLauncherUiTest test) { + mTest = test; + } + + @Override + public Statement apply(Statement base, Description description) { + if (!TestHelpers.isInLauncherProcess() || + description.getAnnotation(AbstractLauncherUiTest.PortraitLandscape.class) == null) { + return base; + } + + return new Statement() { + @Override + public void evaluate() throws Throwable { + try { + mTest.mDevice.pressHome(); + mTest.waitForLauncherCondition("Launcher activity wasn't created", + launcher -> launcher != null); + + mTest.executeOnLauncher(launcher -> + launcher.getRotationHelper().forceAllowRotationForTesting( + true)); + + evaluateInPortrait(); + evaluateInLandscape(); + } finally { + mTest.mDevice.setOrientationNatural(); + mTest.executeOnLauncher(launcher -> + launcher.getRotationHelper().forceAllowRotationForTesting( + false)); + mTest.mLauncher.setExpectedRotation(Surface.ROTATION_0); + } + } + + private void evaluateInPortrait() throws Throwable { + mTest.mDevice.setOrientationNatural(); + mTest.mLauncher.setExpectedRotation(Surface.ROTATION_0); + base.evaluate(); + mTest.mLauncher.pressHome(); + } + + private void evaluateInLandscape() throws Throwable { + mTest.mDevice.setOrientationLeft(); + mTest.mLauncher.setExpectedRotation(Surface.ROTATION_90); + base.evaluate(); + mTest.mLauncher.pressHome(); + } + }; + } +} diff --git a/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java b/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java index d171004fc1..06c56f2c44 100644 --- a/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java +++ b/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java @@ -59,11 +59,7 @@ public class TaplTestsLauncher3 extends AbstractLauncherUiTest { public static void initialize(AbstractLauncherUiTest test) throws Exception { test.clearLauncherData(); - if (TestHelpers.isInLauncherProcess()) { - test.mActivityMonitor.returnToHome(); - } else { - test.mDevice.pressHome(); - } + 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.waitForResumed("Launcher internal state is still Background"); diff --git a/tests/src/com/android/launcher3/ui/widget/RequestPinItemTest.java b/tests/src/com/android/launcher3/ui/widget/RequestPinItemTest.java index a57d7bab8e..be27a66a4c 100644 --- a/tests/src/com/android/launcher3/ui/widget/RequestPinItemTest.java +++ b/tests/src/com/android/launcher3/ui/widget/RequestPinItemTest.java @@ -190,7 +190,7 @@ public class RequestPinItemTest extends AbstractLauncherUiTest { } // Go back to home - mActivityMonitor.returnToHome(); + mLauncher.pressHome(); Wait.atMost(null, new ItemSearchCondition(itemMatcher), DEFAULT_ACTIVITY_TIMEOUT); } diff --git a/tests/src/com/android/launcher3/util/rule/LauncherActivityRule.java b/tests/src/com/android/launcher3/util/rule/LauncherActivityRule.java index 145c3c89e3..2aba7a56de 100644 --- a/tests/src/com/android/launcher3/util/rule/LauncherActivityRule.java +++ b/tests/src/com/android/launcher3/util/rule/LauncherActivityRule.java @@ -72,11 +72,6 @@ public class LauncherActivityRule implements TestRule { getInstrumentation().startActivitySync(getHomeIntentInPackage(getTargetContext())); } - public void returnToHome() { - getTargetContext().startActivity(getHomeIntentInPackage(getTargetContext())); - getInstrumentation().waitForIdleSync(); - } - private class MyStatement extends Statement implements ActivityLifecycleCallbacks { private final Statement mBase; diff --git a/tests/tapl/com/android/launcher3/tapl/Background.java b/tests/tapl/com/android/launcher3/tapl/Background.java index 6c46192356..c9eaf276d7 100644 --- a/tests/tapl/com/android/launcher3/tapl/Background.java +++ b/tests/tapl/com/android/launcher3/tapl/Background.java @@ -87,16 +87,24 @@ public class Background extends LauncherInstrumentation.VisibleContainer { } case TWO_BUTTON: { - final int centerX = mLauncher.getDevice().getDisplayWidth() / 2; - final int startY = getSwipeStartY(); - final int swipeHeight = mLauncher.getTestInfo(getSwipeHeightRequestName()). - getInt(TestProtocol.TEST_INFO_RESPONSE_FIELD); + final int startX; + final int startY; + final int endX; + final int endY; + final int swipeLength = mLauncher.getTestInfo(getSwipeHeightRequestName()). + getInt(TestProtocol.TEST_INFO_RESPONSE_FIELD) + mLauncher.getTouchSlop(); - mLauncher.swipeToState( - centerX, startY, centerX, - startY - swipeHeight - mLauncher.getTouchSlop(), - 10, - expectedState); + if (mLauncher.getDevice().isNaturalOrientation()) { + startX = endX = mLauncher.getDevice().getDisplayWidth() / 2; + startY = getSwipeStartY(); + endY = startY - swipeLength; + } else { + startX = getSwipeStartX(); + endX = startX - swipeLength; + startY = endY = mLauncher.getDevice().getDisplayHeight() / 2; + } + + mLauncher.swipeToState(startX, startY, endX, endY, 10, expectedState); break; } @@ -111,6 +119,10 @@ public class Background extends LauncherInstrumentation.VisibleContainer { return TestProtocol.REQUEST_BACKGROUND_TO_OVERVIEW_SWIPE_HEIGHT; } + protected int getSwipeStartX() { + return mLauncher.getRealDisplaySize().x - 1; + } + protected int getSwipeStartY() { return mLauncher.getRealDisplaySize().y - 1; } diff --git a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java index 2db9d08269..5be4d8195b 100644 --- a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java +++ b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java @@ -333,7 +333,11 @@ public final class LauncherInstrumentation { "but the current state is not " + containerType.name())) { switch (containerType) { case WORKSPACE: { - waitForLauncherObject(APPS_RES_ID); + if (mDevice.isNaturalOrientation()) { + waitForLauncherObject(APPS_RES_ID); + } else { + waitUntilGone(APPS_RES_ID); + } waitUntilGone(OVERVIEW_RES_ID); waitUntilGone(WIDGETS_RES_ID); return waitForLauncherObject(WORKSPACE_RES_ID); diff --git a/tests/tapl/com/android/launcher3/tapl/Workspace.java b/tests/tapl/com/android/launcher3/tapl/Workspace.java index 33754c125b..26c7b8f307 100644 --- a/tests/tapl/com/android/launcher3/tapl/Workspace.java +++ b/tests/tapl/com/android/launcher3/tapl/Workspace.java @@ -213,6 +213,8 @@ public final class Workspace extends Home { @Override protected int getSwipeStartY() { - return mLauncher.waitForLauncherObject("hotseat").getVisibleBounds().top; + return mLauncher.getDevice().isNaturalOrientation() ? + mLauncher.waitForLauncherObject("hotseat").getVisibleBounds().top + : mLauncher.getRealDisplaySize().y - 1; } } \ No newline at end of file From 5095684e222b8130e7aa6363f1418d0de54e1110 Mon Sep 17 00:00:00 2001 From: Zak Cohen Date: Mon, 17 Jun 2019 15:27:28 -0700 Subject: [PATCH 04/41] TaskThumbnailView - post updates to overlays. Post the update of the overlay to handler. No way of knowing if an overlay will add / remove views during the update so assume its not safe to do in layout. Bug: 135287203 Test: manual Change-Id: I7bd0c86da096e7108b71b2d0530e99ee950d6f2e --- .../src/com/android/quickstep/views/TaskThumbnailView.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskThumbnailView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskThumbnailView.java index 6f10b42fba..d55a520443 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskThumbnailView.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskThumbnailView.java @@ -367,8 +367,11 @@ public class TaskThumbnailView extends View { } mRotated = isRotated; - updateOverlay(); invalidate(); + + // Update can be called from {@link #onSizeChanged} during layout, post handling of overlay + // as overlay could modify the views in the overlay as a side effect of its update. + post(this::updateOverlay); } @Override From 71f06807a458ec285841198e2363ba9d75f26ebf Mon Sep 17 00:00:00 2001 From: Kevin Date: Mon, 17 Jun 2019 17:22:56 -0700 Subject: [PATCH 05/41] Add QuickstepProcessInitializer back to common src While removing unused files in Go version of quickstep, I removed QuickstepProcessInitializer which seemed unused but is actually initialized as a resource based ovverride. This CL puts it back as common src so that both Launcher3GoIconRecents and the rest of the variants all use this class. Bug: 135472635 Test: Run Launcher3GoIconRecents => no exception Test: Run Launcher3 => no exception Change-Id: I56f319bac536ec4da96285fab932fe574afa4520 --- .../src/com/android/quickstep/QuickstepProcessInitializer.java | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename quickstep/{recents_ui_overrides => }/src/com/android/quickstep/QuickstepProcessInitializer.java (100%) diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/QuickstepProcessInitializer.java b/quickstep/src/com/android/quickstep/QuickstepProcessInitializer.java similarity index 100% rename from quickstep/recents_ui_overrides/src/com/android/quickstep/QuickstepProcessInitializer.java rename to quickstep/src/com/android/quickstep/QuickstepProcessInitializer.java From 398184c10432c59762a6de49207ac4c4ace30d9c Mon Sep 17 00:00:00 2001 From: Winson Chung Date: Mon, 17 Jun 2019 17:07:44 -0700 Subject: [PATCH 06/41] Adding check for no default home before registering new receiver Bug: 135473571 Test: Manual, try restarting with no default launcher set? Change-Id: Ibb68070ee95ca856b38a74823bcbfbc7890f329e --- .../com/android/quickstep/OverviewComponentObserver.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/quickstep/src/com/android/quickstep/OverviewComponentObserver.java b/quickstep/src/com/android/quickstep/OverviewComponentObserver.java index b5da836e65..0a73b8b195 100644 --- a/quickstep/src/com/android/quickstep/OverviewComponentObserver.java +++ b/quickstep/src/com/android/quickstep/OverviewComponentObserver.java @@ -115,7 +115,11 @@ public final class OverviewComponentObserver { // as uninstalling the app or removing the "Launcher" feature in an update). // Listen for package updates of this app (and remove any previously attached // package listener). - if (!defaultHome.getPackageName().equals(mUpdateRegisteredPackage)) { + if (defaultHome == null) { + if (mUpdateRegisteredPackage != null) { + mContext.unregisterReceiver(mOtherHomeAppUpdateReceiver); + } + } else if (!defaultHome.getPackageName().equals(mUpdateRegisteredPackage)) { if (mUpdateRegisteredPackage != null) { mContext.unregisterReceiver(mOtherHomeAppUpdateReceiver); } From 6aabe679c5ae11845dfe3d6698dabaf357bb17fd Mon Sep 17 00:00:00 2001 From: Greg Kaiser Date: Fri, 14 Jun 2019 14:22:30 -0700 Subject: [PATCH 07/41] Android.mk: Strictly order QuickStepGo and GoIconRecents We had Launcher3QuickStepGo adding Launcher3GoIconRecents to LOCAL_OVERRIDES_PACKAGES, and also had Launcher3GoIconRecents adding Launcher3QuickStepGo to LOCAL_OVERRIDES_PACKAGES. As a result, if both of these got added to PROJECT_PACKAGES, we would end up with neither launcher, since they both overrode each other. To fix this, we declare a strict hierarchy. Since Launcher3GoIconRecents is the default, we put Launcher3QuickStepGo to be higher in the hierarchy. While that might seem a counter-intuitive choice, it makes it much easier for folks to override the default, without needing to modify the file containing the default. Test: Various clean builds which revealed this root issue, and this fix. Bug: 135161289 Change-Id: If718a7030aa6ee4b6b952406006abda77b1eb8e5 --- Android.mk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Android.mk b/Android.mk index 7956d2810a..9d113d9547 100644 --- a/Android.mk +++ b/Android.mk @@ -298,7 +298,7 @@ LOCAL_PROGUARD_ENABLED := full LOCAL_PACKAGE_NAME := Launcher3GoIconRecents LOCAL_PRIVILEGED_MODULE := true LOCAL_PRODUCT_MODULE := true -LOCAL_OVERRIDES_PACKAGES := Home Launcher2 Launcher3 Launcher3Go Launcher3QuickStep Launcher3QuickStepGo +LOCAL_OVERRIDES_PACKAGES := Home Launcher2 Launcher3 Launcher3Go Launcher3QuickStep LOCAL_REQUIRED_MODULES := privapp_whitelist_com.android.launcher3 LOCAL_FULL_LIBS_MANIFEST_FILES := \ From 8be504000a250879dbb6c1a80545c02bc5d51daf Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Tue, 18 Jun 2019 12:02:42 -0700 Subject: [PATCH 08/41] Fixing onStateTransitionEnd is not called at the end of reset state Bug: 135299165 Bug: 133867119 Change-Id: Ifac762a44fa45dfef72a9357f09fc96c34162565 --- src/com/android/launcher3/LauncherStateManager.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/com/android/launcher3/LauncherStateManager.java b/src/com/android/launcher3/LauncherStateManager.java index 8b03691c81..4425e2af61 100644 --- a/src/com/android/launcher3/LauncherStateManager.java +++ b/src/com/android/launcher3/LauncherStateManager.java @@ -223,6 +223,7 @@ public class LauncherStateManager { } public void reapplyState(boolean cancelCurrentAnimation) { + boolean wasInAnimation = mConfig.mCurrentAnimation != null; if (cancelCurrentAnimation) { cancelAnimation(); } @@ -230,6 +231,9 @@ public class LauncherStateManager { for (StateHandler handler : getStateHandlers()) { handler.setState(mState); } + if (wasInAnimation) { + onStateTransitionEnd(mState); + } } } From 32cb616fab1ad495968d1eadd503a61b8bf3db35 Mon Sep 17 00:00:00 2001 From: Tony Date: Fri, 14 Jun 2019 20:26:12 -0700 Subject: [PATCH 09/41] Fix bugs with hotseat in overview - Allow touches to go through recents to the hotseat. - Translate the hotseat with the all apps shelf when swiping up in background app state. Bug: 135222111 Change-Id: Ib887fc25ccfeb406a44074198c11f7b1d245443c --- .../uioverrides/states/BackgroundAppState.java | 12 ++++++++++++ .../uioverrides/states/OverviewState.java | 4 ++++ .../LauncherActivityControllerHelper.java | 7 +++++-- .../quickstep/views/LauncherRecentsView.java | 14 ++++++++++++++ .../com/android/quickstep/views/RecentsView.java | 4 ++++ .../allapps/AllAppsTransitionController.java | 16 ++++++++++++---- .../android/launcher3/views/BaseDragLayer.java | 14 ++++++++++++++ 7 files changed, 65 insertions(+), 6 deletions(-) diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java index 1c66968582..d14de70c50 100644 --- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java +++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java @@ -83,4 +83,16 @@ public class BackgroundAppState extends OverviewState { public int getVisibleElements(Launcher launcher) { return super.getVisibleElements(launcher) & ~RECENTS_CLEAR_ALL_BUTTON; } + + @Override + public ScaleAndTranslation getHotseatScaleAndTranslation(Launcher launcher) { + if ((getVisibleElements(launcher) & HOTSEAT_ICONS) != 0) { + // Translate hotseat offscreen if we show it in overview. + ScaleAndTranslation scaleAndTranslation = super.getHotseatScaleAndTranslation(launcher); + scaleAndTranslation.translationY = LayoutUtils.getShelfTrackingDistance(launcher, + launcher.getDeviceProfile()); + return scaleAndTranslation; + } + return super.getHotseatScaleAndTranslation(launcher); + } } 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 9a99c15522..5c9b200961 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 @@ -127,6 +127,10 @@ public class OverviewState extends LauncherState { // We have no all apps content, so we're still at the fully down progress. return super.getVerticalProgress(launcher); } + return getDefaultVerticalProgress(launcher); + } + + public static float getDefaultVerticalProgress(Launcher launcher) { return 1 - (getDefaultSwipeHeight(launcher) / launcher.getAllAppsController().getShiftRange()); } diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityControllerHelper.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityControllerHelper.java index 0d06c19ab5..e7d085ca14 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityControllerHelper.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityControllerHelper.java @@ -16,7 +16,6 @@ package com.android.quickstep; import static android.view.View.TRANSLATION_Y; - import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY; import static com.android.launcher3.LauncherState.BACKGROUND_APP; import static com.android.launcher3.LauncherState.NORMAL; @@ -62,6 +61,7 @@ import com.android.launcher3.anim.AnimatorPlaybackController; import com.android.launcher3.anim.AnimatorSetBuilder; import com.android.launcher3.anim.SpringObjectAnimator; import com.android.launcher3.testing.TestProtocol; +import com.android.launcher3.uioverrides.states.OverviewState; import com.android.launcher3.userevent.nano.LauncherLogProto; import com.android.launcher3.views.FloatingIconView; import com.android.quickstep.SysUINavigationMode.Mode; @@ -260,8 +260,11 @@ public final class LauncherActivityControllerHelper implements ActivityControlHe } float shelfHiddenProgress = BACKGROUND_APP.getVerticalProgress(activity); float shelfOverviewProgress = OVERVIEW.getVerticalProgress(activity); + // Peek based on default overview progress so we can see hotseat if we're showing + // that instead of predictions in overview. + float defaultOverviewProgress = OverviewState.getDefaultVerticalProgress(activity); float shelfPeekingProgress = shelfHiddenProgress - - (shelfHiddenProgress - shelfOverviewProgress) * 0.25f; + - (shelfHiddenProgress - defaultOverviewProgress) * 0.25f; float toProgress = mShelfState == ShelfAnimState.HIDE ? shelfHiddenProgress : mShelfState == ShelfAnimState.PEEK diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/LauncherRecentsView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/LauncherRecentsView.java index 5b2e27e539..03441c87ec 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/LauncherRecentsView.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/LauncherRecentsView.java @@ -33,9 +33,11 @@ import android.graphics.Canvas; import android.graphics.Rect; import android.os.Build; import android.util.AttributeSet; +import android.view.MotionEvent; import android.view.View; import com.android.launcher3.DeviceProfile; +import com.android.launcher3.Hotseat; import com.android.launcher3.Launcher; import com.android.launcher3.LauncherState; import com.android.launcher3.LauncherStateManager.StateListener; @@ -250,4 +252,16 @@ public class LauncherRecentsView extends RecentsView implements StateL setDisallowScrollToClearAll(!hasClearAllButton); } } + + @Override + protected boolean shouldStealTouchFromSiblingsBelow(MotionEvent ev) { + if (ev.getAction() == MotionEvent.ACTION_DOWN) { + // Allow touches to go through to the hotseat. + Hotseat hotseat = mActivity.getHotseat(); + boolean touchingHotseat = hotseat.isShown() + && mActivity.getDragLayer().isEventOverView(hotseat, ev, this); + return !touchingHotseat; + } + return super.shouldStealTouchFromSiblingsBelow(ev); + } } diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java index 9058e7eda7..a8987a3d42 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java @@ -521,6 +521,10 @@ public abstract class RecentsView extends PagedView impl // Do not let touch escape to siblings below this view. + return isHandlingTouch() || shouldStealTouchFromSiblingsBelow(ev); + } + + protected boolean shouldStealTouchFromSiblingsBelow(MotionEvent ev) { return true; } diff --git a/src/com/android/launcher3/allapps/AllAppsTransitionController.java b/src/com/android/launcher3/allapps/AllAppsTransitionController.java index c62fc3d8f9..a351b9ae1d 100644 --- a/src/com/android/launcher3/allapps/AllAppsTransitionController.java +++ b/src/com/android/launcher3/allapps/AllAppsTransitionController.java @@ -2,6 +2,8 @@ package com.android.launcher3.allapps; import static com.android.launcher3.LauncherState.ALL_APPS_CONTENT; import static com.android.launcher3.LauncherState.ALL_APPS_HEADER_EXTRA; +import static com.android.launcher3.LauncherState.BACKGROUND_APP; +import static com.android.launcher3.LauncherState.HOTSEAT_ICONS; import static com.android.launcher3.LauncherState.OVERVIEW; import static com.android.launcher3.LauncherState.VERTICAL_SWIPE_INDICATOR; import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_ALL_APPS_FADE; @@ -28,15 +30,12 @@ import com.android.launcher3.ProgressInterface; import com.android.launcher3.R; import com.android.launcher3.anim.AnimationSuccessListener; import com.android.launcher3.anim.AnimatorSetBuilder; -import com.android.launcher3.anim.SpringObjectAnimator; import com.android.launcher3.anim.PropertySetter; +import com.android.launcher3.anim.SpringObjectAnimator; import com.android.launcher3.testing.TestProtocol; -import com.android.launcher3.util.MultiValueAlpha; import com.android.launcher3.util.Themes; import com.android.launcher3.views.ScrimView; -import androidx.dynamicanimation.animation.FloatPropertyCompat; - /** * Handles AllApps view transition. * 1) Slides all apps view using direct manipulation @@ -139,6 +138,15 @@ public class AllAppsTransitionController implements StateHandler, OnDeviceProfil } else { mLauncher.getSystemUiController().updateUiState(UI_STATE_ALL_APPS, 0); } + + if ((OVERVIEW.getVisibleElements(mLauncher) & HOTSEAT_ICONS) != 0) { + // Translate hotseat with the shelf until reaching overview. + float overviewProgress = OVERVIEW.getVerticalProgress(mLauncher); + if (progress >= overviewProgress || mLauncher.isInState(BACKGROUND_APP)) { + float hotseatShift = (progress - overviewProgress) * mShiftRange; + mLauncher.getHotseat().setTranslationY(hotseatShift); + } + } } @Override diff --git a/src/com/android/launcher3/views/BaseDragLayer.java b/src/com/android/launcher3/views/BaseDragLayer.java index ac152dbffa..51c7022bde 100644 --- a/src/com/android/launcher3/views/BaseDragLayer.java +++ b/src/com/android/launcher3/views/BaseDragLayer.java @@ -116,11 +116,25 @@ public abstract class BaseDragLayer mMultiValueAlpha = new MultiValueAlpha(this, alphaChannelCount); } + /** + * Same as {@link #isEventOverView(View, MotionEvent, View)} where evView == this drag layer. + */ public boolean isEventOverView(View view, MotionEvent ev) { getDescendantRectRelativeToSelf(view, mHitRect); return mHitRect.contains((int) ev.getX(), (int) ev.getY()); } + /** + * Given a motion event in evView's coordinates, return whether the event is within another + * view's bounds. + */ + public boolean isEventOverView(View view, MotionEvent ev, View evView) { + int[] xy = new int[] {(int) ev.getX(), (int) ev.getY()}; + getDescendantCoordRelativeToSelf(evView, xy); + getDescendantRectRelativeToSelf(view, mHitRect); + return mHitRect.contains(xy[0], xy[1]); + } + @Override public boolean onInterceptTouchEvent(MotionEvent ev) { int action = ev.getAction(); From 312209b01ae088a5e747687fbff2b519d78c2714 Mon Sep 17 00:00:00 2001 From: Tony Wickham Date: Tue, 18 Jun 2019 15:54:29 -0700 Subject: [PATCH 10/41] Set mNextPage after springBack when overscrolling Bug: 134609899 Change-Id: I74294655689c4ccd66896550b74e205d1c3df543 --- src/com/android/launcher3/PagedView.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/com/android/launcher3/PagedView.java b/src/com/android/launcher3/PagedView.java index 80e17c9ff1..dfa3e1beed 100644 --- a/src/com/android/launcher3/PagedView.java +++ b/src/com/android/launcher3/PagedView.java @@ -1204,6 +1204,7 @@ public abstract class PagedView extends ViewGrou if (((initialScrollX >= mMaxScrollX) && (isVelocityXLeft || !isFling)) || ((initialScrollX <= mMinScrollX) && (!isVelocityXLeft || !isFling))) { mScroller.springBack(getScrollX(), mMinScrollX, mMaxScrollX); + mNextPage = getPageNearestToCenterOfScreen(); } else { mScroller.setInterpolator(mDefaultInterpolator); mScroller.fling(initialScrollX, -velocityX, From e069291ecdbcaa1fdd4fa7201f97992cfe3bc6e8 Mon Sep 17 00:00:00 2001 From: Winson Chung Date: Tue, 18 Jun 2019 11:00:29 -0700 Subject: [PATCH 11/41] Trigger heap dump when heap exceeds a limit Bug: 135150619 Change-Id: I2b207bed1f66407f573abeae879aae34e98741bd --- .../quickstep/QuickstepProcessInitializer.java | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/quickstep/src/com/android/quickstep/QuickstepProcessInitializer.java b/quickstep/src/com/android/quickstep/QuickstepProcessInitializer.java index befeee0db9..7bfa9a0f99 100644 --- a/quickstep/src/com/android/quickstep/QuickstepProcessInitializer.java +++ b/quickstep/src/com/android/quickstep/QuickstepProcessInitializer.java @@ -15,6 +15,7 @@ */ package com.android.quickstep; +import android.app.ActivityManager; import android.content.Context; import android.content.pm.PackageManager; import android.os.UserManager; @@ -22,17 +23,29 @@ import android.util.Log; import com.android.launcher3.BuildConfig; import com.android.launcher3.MainProcessInitializer; +import com.android.launcher3.Utilities; import com.android.systemui.shared.system.ThreadedRendererCompat; @SuppressWarnings("unused") public class QuickstepProcessInitializer extends MainProcessInitializer { private static final String TAG = "QuickstepProcessInitializer"; + private static final int HEAP_LIMIT_MB = 250; public QuickstepProcessInitializer(Context context) { } @Override protected void init(Context context) { + if (Utilities.IS_DEBUG_DEVICE) { + try { + // Trigger a heap dump if the PSS reaches beyond the target heap limit + final ActivityManager am = context.getSystemService(ActivityManager.class); + am.setWatchHeapLimit(HEAP_LIMIT_MB * 1024 * 1024); + } catch (SecurityException e) { + // Do nothing + } + } + // Workaround for b/120550382, an external app can cause the launcher process to start for // a work profile user which we do not support. Disable the application immediately when we // detect this to be the case. From 7d2aed0cb502873f2d910994a8eddeae74ec7ceb Mon Sep 17 00:00:00 2001 From: Miranda Kephart Date: Tue, 18 Jun 2019 15:41:32 -0400 Subject: [PATCH 12/41] Allow shallow-angle flings in AssistantTouchConsumer While any angle above 22 degrees from horizontal is permitted for drag invocations, the SwipeDetector used to detect flings only allowed angles within 45 degrees of vertical. This changes to using GestureListener and permits flings in the 22-45 degree region (as well as up to vertical). Test: manual Bug: 135011207 Change-Id: I8e08650395917341869b27ff381f32268fadcb3c --- .../AssistantTouchConsumer.java | 81 ++++++++++--------- 1 file changed, 43 insertions(+), 38 deletions(-) diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/AssistantTouchConsumer.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/AssistantTouchConsumer.java index 837423aceb..8f92772e83 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/AssistantTouchConsumer.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/AssistantTouchConsumer.java @@ -34,11 +34,14 @@ import static com.android.launcher3.userevent.nano.LauncherLogProto.ContainerTyp import android.animation.ValueAnimator; import android.content.Context; import android.content.res.Resources; +import android.gesture.Gesture; import android.graphics.PointF; import android.os.Bundle; import android.os.RemoteException; import android.os.SystemClock; import android.util.Log; +import android.view.GestureDetector; +import android.view.GestureDetector.SimpleOnGestureListener; import android.view.HapticFeedbackConstants; import android.view.MotionEvent; import android.view.ViewConfiguration; @@ -56,8 +59,7 @@ import com.android.systemui.shared.system.QuickStepContract; /** * Touch consumer for handling events to launch assistant from launcher */ -public class AssistantTouchConsumer extends DelegateInputConsumer - implements SwipeDetector.Listener { +public class AssistantTouchConsumer extends DelegateInputConsumer { private static final String TAG = "AssistantTouchConsumer"; private static final long RETRACT_ANIMATION_DURATION_MS = 300; @@ -68,7 +70,6 @@ public class AssistantTouchConsumer extends DelegateInputConsumer private static final int OPA_BUNDLE_TRIGGER_DIAG_SWIPE_GESTURE = 83; private static final String INVOCATION_TYPE_KEY = "invocation_type"; private static final int INVOCATION_TYPE_GESTURE = 1; - private static final int INVOCATION_TYPE_FLING = 6; private final PointF mDownPos = new PointF(); private final PointF mLastPos = new PointF(); @@ -90,7 +91,7 @@ public class AssistantTouchConsumer extends DelegateInputConsumer private final float mSquaredSlop; private final ISystemUiProxy mSysUiProxy; private final Context mContext; - private final SwipeDetector mSwipeDetector; + private final GestureDetector mGestureDetector; public AssistantTouchConsumer(Context context, ISystemUiProxy systemUiProxy, ActivityControlHelper activityControlHelper, InputConsumer delegate, @@ -107,8 +108,8 @@ public class AssistantTouchConsumer extends DelegateInputConsumer mSquaredSlop = slop * slop; mActivityControlHelper = activityControlHelper; - mSwipeDetector = new SwipeDetector(mContext, this, SwipeDetector.VERTICAL); - mSwipeDetector.setDetectableScrollConditions(SwipeDetector.DIRECTION_POSITIVE, false); + + mGestureDetector = new GestureDetector(context, new AssistantGestureListener()); } @Override @@ -119,7 +120,7 @@ public class AssistantTouchConsumer extends DelegateInputConsumer @Override public void onMotionEvent(MotionEvent ev) { // TODO add logging - mSwipeDetector.onTouchEvent(ev); + mGestureDetector.onTouchEvent(ev); switch (ev.getActionMasked()) { case ACTION_DOWN: { @@ -171,13 +172,8 @@ public class AssistantTouchConsumer extends DelegateInputConsumer mStartDragPos.set(mLastPos.x, mLastPos.y); mDragTime = SystemClock.uptimeMillis(); - // Determine if angle is larger than threshold for assistant detection - float angle = (float) Math.toDegrees( - Math.atan2(mDownPos.y - mLastPos.y, mDownPos.x - mLastPos.x)); - mDirection = angle > 90 ? UPLEFT : UPRIGHT; - angle = angle > 90 ? 180 - angle : angle; - - if (angle > mAngleThreshold && angle < 90) { + if (isValidAssistantGestureAngle( + mDownPos.x - mLastPos.x, mDownPos.y - mLastPos.y)) { setActive(ev); } else { mState = STATE_DELEGATE_ACTIVE; @@ -261,6 +257,19 @@ public class AssistantTouchConsumer extends DelegateInputConsumer } } + /** + * Determine if angle is larger than threshold for assistant detection + */ + private boolean isValidAssistantGestureAngle(float deltaX, float deltaY) { + float angle = (float) Math.toDegrees(Math.atan2(deltaY, deltaX)); + mDirection = angle > 90 ? UPLEFT : UPRIGHT; + + // normalize so that angle is measured clockwise from horizontal in the bottom right corner + // and counterclockwise from horizontal in the bottom left corner + angle = angle > 90 ? 180 - angle : angle; + return (angle > mAngleThreshold && angle < 90); + } + public static boolean withinTouchRegion(Context context, MotionEvent ev) { final Resources res = context.getResources(); final int width = res.getDisplayMetrics().widthPixels; @@ -269,32 +278,28 @@ public class AssistantTouchConsumer extends DelegateInputConsumer return (ev.getX() > width - size || ev.getX() < size) && ev.getY() > height - size; } - @Override - public void onDragStart(boolean start) { - // do nothing - } + private class AssistantGestureListener extends SimpleOnGestureListener { + @Override + public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { + if (isValidAssistantGestureAngle(velocityX, -velocityY) + && !mLaunchedAssistant && mState != STATE_DELEGATE_ACTIVE) { + mLastProgress = 1; + try { + mSysUiProxy.onAssistantGestureCompletion( + (float) Math.sqrt(velocityX * velocityX + velocityY * velocityY)); + startAssistantInternal(FLING); - @Override - public boolean onDrag(float displacement) { - return false; - } - - @Override - public void onDragEnd(float velocity, boolean fling) { - if (fling && !mLaunchedAssistant && mState != STATE_DELEGATE_ACTIVE) { - mLastProgress = 1; - try { - mSysUiProxy.onAssistantGestureCompletion(velocity); - startAssistantInternal(FLING); - - Bundle args = new Bundle(); - args.putInt(INVOCATION_TYPE_KEY, INVOCATION_TYPE_GESTURE); - mSysUiProxy.startAssistant(args); - mLaunchedAssistant = true; - } catch (RemoteException e) { - Log.w(TAG, "Failed to send SysUI start/send assistant progress: " + mLastProgress, - e); + Bundle args = new Bundle(); + args.putInt(INVOCATION_TYPE_KEY, INVOCATION_TYPE_GESTURE); + mSysUiProxy.startAssistant(args); + mLaunchedAssistant = true; + } catch (RemoteException e) { + Log.w(TAG, + "Failed to send SysUI start/send assistant progress: " + mLastProgress, + e); + } } + return true; } } } From 3e195d7429436e04fcb716b8687872290509fbcb Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Wed, 19 Jun 2019 14:24:38 -0700 Subject: [PATCH 13/41] Disable fake landscape UI Bug: 131360075 Change-Id: I21e0a6cdf332e2d16821c589f9630c254b595770 --- src/com/android/launcher3/Launcher.java | 4 ++++ src/com/android/launcher3/LauncherModel.java | 3 ++- src/com/android/launcher3/config/BaseFlags.java | 2 +- 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index 80ea78f194..ad2783e0c6 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -430,6 +430,10 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns, super.onConfigurationChanged(newConfig); } + public void reload() { + onIdpChanged(mDeviceProfile.inv); + } + private boolean supportsFakeLandscapeUI() { return FeatureFlags.FAKE_LANDSCAPE_UI.get() && !mRotationHelper.homeScreenCanRotate(); } diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java index ac392a6e65..d79f5d5a94 100644 --- a/src/com/android/launcher3/LauncherModel.java +++ b/src/com/android/launcher3/LauncherModel.java @@ -301,7 +301,8 @@ public class LauncherModel extends BroadcastReceiver } } } else if (IS_DOGFOOD_BUILD && ACTION_FORCE_ROLOAD.equals(action)) { - forceReload(); + Launcher l = (Launcher) getCallback(); + l.reload(); } } diff --git a/src/com/android/launcher3/config/BaseFlags.java b/src/com/android/launcher3/config/BaseFlags.java index 54d0db1006..54efcb7868 100644 --- a/src/com/android/launcher3/config/BaseFlags.java +++ b/src/com/android/launcher3/config/BaseFlags.java @@ -109,7 +109,7 @@ abstract class BaseFlags { "Show chip hints and gleams on the overview screen"); public static final TogglableFlag FAKE_LANDSCAPE_UI = new TogglableFlag( - "FAKE_LANDSCAPE_UI", true, + "FAKE_LANDSCAPE_UI", false, "Rotate launcher UI instead of using transposed layout"); public static void initialize(Context context) { From 59c6901823180bdeb6c39187df45fd1adddeae2d Mon Sep 17 00:00:00 2001 From: Tony Wickham Date: Tue, 18 Jun 2019 16:34:37 -0700 Subject: [PATCH 14/41] Some improvements to home to overview transition for 0 button mode - Peek in overview further, and with more overshoot - Fade out and scale down workspace faster while swiping up - Scale and translate workspace slower when letting go to enter overview (so it doesn't zoom out at warp speed) - Fade in all apps shelf sooner Bug: 132455160 Change-Id: Ieafad0ccf9bb587889bc35d536627661db10e358 --- .../uioverrides/RecentsUiFactory.java | 19 ------ .../uioverrides/states/OverviewState.java | 40 ++++++++++++ .../uioverrides/RecentsUiFactory.java | 21 ------- .../uioverrides/states/OverviewPeekState.java | 16 +++++ .../uioverrides/states/OverviewState.java | 41 ++++++++++++ .../FlingAndHoldTouchController.java | 39 ++++++++++-- quickstep/res/values/dimens.xml | 2 +- .../uioverrides/states/AllAppsState.java | 9 ++- .../quickstep/views/ShelfScrimView.java | 18 +++++- src/com/android/launcher3/LauncherState.java | 53 +++++++++++++++- .../launcher3/LauncherStateManager.java | 62 +------------------ .../WorkspaceStateTransitionAnimation.java | 13 +++- .../launcher3/anim/AnimatorSetBuilder.java | 12 ++-- .../launcher3/uioverrides/UiFactory.java | 2 - 14 files changed, 227 insertions(+), 120 deletions(-) diff --git a/go/quickstep/src/com/android/launcher3/uioverrides/RecentsUiFactory.java b/go/quickstep/src/com/android/launcher3/uioverrides/RecentsUiFactory.java index b5fefb46d6..45822432f5 100644 --- a/go/quickstep/src/com/android/launcher3/uioverrides/RecentsUiFactory.java +++ b/go/quickstep/src/com/android/launcher3/uioverrides/RecentsUiFactory.java @@ -16,11 +16,6 @@ package com.android.launcher3.uioverrides; -import static android.view.View.VISIBLE; -import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY; - -import android.view.View; - import com.android.launcher3.DeviceProfile; import com.android.launcher3.Launcher; import com.android.launcher3.LauncherStateManager.StateHandler; @@ -43,8 +38,6 @@ import java.util.ArrayList; public abstract class RecentsUiFactory { public static final boolean GO_LOW_RAM_RECENTS_ENABLED = true; - // Scale recents takes before animating in - private static final float RECENTS_PREPARE_SCALE = 1.33f; public static TouchController[] createTouchControllers(Launcher launcher) { ArrayList list = new ArrayList<>(); @@ -76,18 +69,6 @@ public abstract class RecentsUiFactory { return new RecentsViewStateController(launcher); } - /** - * Prepare the recents view to animate in. - * - * @param launcher the launcher activity - */ - public static void prepareToShowOverview(Launcher launcher) { - View overview = launcher.getOverviewPanel(); - if (overview.getVisibility() != VISIBLE) { - SCALE_PROPERTY.set(overview, RECENTS_PREPARE_SCALE); - } - } - /** * Clean-up logic that occurs when recents is no longer in use/visible. * diff --git a/go/quickstep/src/com/android/launcher3/uioverrides/states/OverviewState.java b/go/quickstep/src/com/android/launcher3/uioverrides/states/OverviewState.java index 1b24fc856e..d0cfcf97a5 100644 --- a/go/quickstep/src/com/android/launcher3/uioverrides/states/OverviewState.java +++ b/go/quickstep/src/com/android/launcher3/uioverrides/states/OverviewState.java @@ -16,15 +16,31 @@ package com.android.launcher3.uioverrides.states; +import static android.view.View.VISIBLE; + import static com.android.launcher3.LauncherAnimUtils.OVERVIEW_TRANSITION_MS; +import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY; +import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_OVERVIEW_FADE; +import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_OVERVIEW_SCALE; +import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_OVERVIEW_TRANSLATE_X; +import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_WORKSPACE_FADE; +import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_WORKSPACE_SCALE; +import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_WORKSPACE_TRANSLATE; +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.states.RotationHelper.REQUEST_ROTATE; +import android.view.View; + import com.android.launcher3.DeviceProfile; import com.android.launcher3.Launcher; import com.android.launcher3.LauncherState; import com.android.launcher3.R; +import com.android.launcher3.anim.AnimatorSetBuilder; import com.android.launcher3.userevent.nano.LauncherLogProto; +import com.android.quickstep.SysUINavigationMode; import com.android.quickstep.views.IconRecentsView; /** @@ -32,6 +48,9 @@ import com.android.quickstep.views.IconRecentsView; */ public class OverviewState extends LauncherState { + // Scale recents takes before animating in + private static final float RECENTS_PREPARE_SCALE = 1.33f; + private static final int STATE_FLAGS = FLAG_WORKSPACE_ICONS_CAN_BE_DRAGGED | FLAG_DISABLE_RESTORE | FLAG_OVERVIEW_UI | FLAG_DISABLE_ACCESSIBILITY; @@ -103,6 +122,27 @@ public class OverviewState extends LauncherState { return dp.allAppsCellHeightPx - dp.allAppsIconTextSizePx; } + @Override + public void prepareForAtomicAnimation(Launcher launcher, LauncherState fromState, + AnimatorSetBuilder builder) { + if (fromState == NORMAL && this == OVERVIEW) { + if (SysUINavigationMode.getMode(launcher) == SysUINavigationMode.Mode.NO_BUTTON) { + builder.setInterpolator(ANIM_WORKSPACE_SCALE, ACCEL); + builder.setInterpolator(ANIM_WORKSPACE_TRANSLATE, ACCEL); + } else { + builder.setInterpolator(ANIM_WORKSPACE_SCALE, OVERSHOOT_1_2); + } + 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_FADE, OVERSHOOT_1_2); + + View overview = launcher.getOverviewPanel(); + if (overview.getVisibility() != VISIBLE) { + SCALE_PROPERTY.set(overview, RECENTS_PREPARE_SCALE); + } + } + } public static OverviewState newBackgroundState(int id) { return new OverviewState(id); diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/RecentsUiFactory.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/RecentsUiFactory.java index d84362c9f2..8d5ac50942 100644 --- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/RecentsUiFactory.java +++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/RecentsUiFactory.java @@ -16,8 +16,6 @@ package com.android.launcher3.uioverrides; -import static android.view.View.VISIBLE; -import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY; import static com.android.launcher3.LauncherState.NORMAL; import static com.android.launcher3.LauncherState.OVERVIEW; import static com.android.quickstep.SysUINavigationMode.Mode.NO_BUTTON; @@ -62,9 +60,6 @@ public abstract class RecentsUiFactory { private static final AsyncCommand SET_SHELF_HEIGHT_CMD = (visible, height) -> WindowManagerWrapper.getInstance().setShelfHeight(visible != 0, height); - // Scale recents takes before animating in - private static final float RECENTS_PREPARE_SCALE = 1.33f; - public static RotationMode ROTATION_LANDSCAPE = new RotationMode(-90) { @Override public void mapRect(int left, int top, int right, int bottom, Rect out) { @@ -188,22 +183,6 @@ public abstract class RecentsUiFactory { return new RecentsViewStateController(launcher); } - /** - * Prepare the recents view to animate in. - * - * @param launcher the launcher activity - */ - public static void prepareToShowOverview(Launcher launcher) { - if (SysUINavigationMode.getMode(launcher) == NO_BUTTON) { - // Overview lives on the side, so doesn't scale in from above. - return; - } - RecentsView overview = launcher.getOverviewPanel(); - if (overview.getVisibility() != VISIBLE || overview.getContentAlpha() == 0) { - SCALE_PROPERTY.set(overview, RECENTS_PREPARE_SCALE); - } - } - /** * Recents logic that triggers when launcher state changes or launcher activity stops/resumes. * diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/OverviewPeekState.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/OverviewPeekState.java index bc1d4ba0c7..c954762837 100644 --- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/OverviewPeekState.java +++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/OverviewPeekState.java @@ -14,8 +14,15 @@ * limitations under the License. */ package com.android.launcher3.uioverrides.states; +import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_OVERVIEW_FADE; +import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_OVERVIEW_TRANSLATE_X; +import static com.android.launcher3.anim.Interpolators.INSTANT; +import static com.android.launcher3.anim.Interpolators.OVERSHOOT_1_7; + import com.android.launcher3.Launcher; +import com.android.launcher3.LauncherState; import com.android.launcher3.R; +import com.android.launcher3.anim.AnimatorSetBuilder; public class OverviewPeekState extends OverviewState { public OverviewPeekState(int id) { @@ -29,4 +36,13 @@ public class OverviewPeekState extends OverviewState { - launcher.getResources().getDimension(R.dimen.overview_peek_distance); return result; } + + @Override + public void prepareForAtomicAnimation(Launcher launcher, LauncherState fromState, + AnimatorSetBuilder builder) { + if (this == OVERVIEW_PEEK && fromState == NORMAL) { + builder.setInterpolator(ANIM_OVERVIEW_FADE, INSTANT); + builder.setInterpolator(ANIM_OVERVIEW_TRANSLATE_X, OVERSHOOT_1_7); + } + } } 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 5c9b200961..5543860eec 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 @@ -15,8 +15,20 @@ */ package com.android.launcher3.uioverrides.states; +import static android.view.View.VISIBLE; + import static com.android.launcher3.LauncherAnimUtils.OVERVIEW_TRANSITION_MS; +import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY; +import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_OVERVIEW_FADE; +import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_OVERVIEW_SCALE; +import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_OVERVIEW_TRANSLATE_X; +import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_WORKSPACE_FADE; +import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_WORKSPACE_SCALE; +import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_WORKSPACE_TRANSLATE; +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.logging.LoggerUtils.newContainerTarget; import static com.android.launcher3.states.RotationHelper.REQUEST_ROTATE; @@ -30,8 +42,11 @@ import com.android.launcher3.LauncherState; 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.uioverrides.UiFactory; import com.android.launcher3.userevent.nano.LauncherLogProto.Action; import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType; +import com.android.quickstep.SysUINavigationMode; import com.android.quickstep.views.RecentsView; import com.android.quickstep.views.TaskView; @@ -40,6 +55,9 @@ import com.android.quickstep.views.TaskView; */ public class OverviewState extends LauncherState { + // Scale recents takes before animating in + private static final float RECENTS_PREPARE_SCALE = 1.33f; + protected static final Rect sTempRect = new Rect(); private static final int STATE_FLAGS = FLAG_WORKSPACE_ICONS_CAN_BE_DRAGGED @@ -160,6 +178,29 @@ public class OverviewState extends LauncherState { } } + @Override + public void prepareForAtomicAnimation(Launcher launcher, LauncherState fromState, + AnimatorSetBuilder builder) { + if (fromState == NORMAL && this == OVERVIEW) { + if (SysUINavigationMode.getMode(launcher) == SysUINavigationMode.Mode.NO_BUTTON) { + builder.setInterpolator(ANIM_WORKSPACE_SCALE, ACCEL); + builder.setInterpolator(ANIM_WORKSPACE_TRANSLATE, ACCEL); + } else { + builder.setInterpolator(ANIM_WORKSPACE_SCALE, OVERSHOOT_1_2); + + // Scale up the recents, if it is not coming from the side + RecentsView overview = launcher.getOverviewPanel(); + if (overview.getVisibility() != VISIBLE || overview.getContentAlpha() == 0) { + SCALE_PROPERTY.set(overview, RECENTS_PREPARE_SCALE); + } + } + 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_FADE, OVERSHOOT_1_2); + } + } + public static OverviewState newBackgroundState(int id) { return new BackgroundAppState(id); } 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 e3e339add1..3fe4bcfd9c 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 @@ -16,11 +16,20 @@ package com.android.launcher3.uioverrides.touchcontrollers; +import static com.android.launcher3.LauncherState.ALL_APPS; +import static com.android.launcher3.LauncherState.HOTSEAT_ICONS; import static com.android.launcher3.LauncherState.NORMAL; import static com.android.launcher3.LauncherState.OVERVIEW; import static com.android.launcher3.LauncherState.OVERVIEW_PEEK; import static com.android.launcher3.LauncherStateManager.ANIM_ALL; import static com.android.launcher3.LauncherStateManager.ATOMIC_OVERVIEW_PEEK_COMPONENT; +import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_HOTSEAT_SCALE; +import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_HOTSEAT_TRANSLATE; +import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_VERTICAL_PROGRESS; +import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_WORKSPACE_FADE; +import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_WORKSPACE_SCALE; +import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_WORKSPACE_TRANSLATE; +import static com.android.launcher3.anim.Interpolators.DEACCEL_3; import static com.android.launcher3.anim.Interpolators.OVERSHOOT_1_2; import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_OVERVIEW_DISABLED; @@ -38,14 +47,14 @@ import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch; import com.android.quickstep.OverviewInteractionState; import com.android.quickstep.util.MotionPauseDetector; import com.android.quickstep.views.RecentsView; -import com.android.systemui.shared.system.QuickStepContract; /** * Touch controller which handles swipe and hold to go to Overview */ public class FlingAndHoldTouchController extends PortraitStatesTouchController { - private static final long PEEK_ANIM_DURATION = 100; + private static final long PEEK_IN_ANIM_DURATION = 240; + private static final long PEEK_OUT_ANIM_DURATION = 100; private static final float MAX_DISPLACEMENT_PERCENT = 0.75f; private final MotionPauseDetector mMotionPauseDetector; @@ -81,9 +90,9 @@ public class FlingAndHoldTouchController extends PortraitStatesTouchController { } 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, - PEEK_ANIM_DURATION); + new AnimatorSetBuilder(), ATOMIC_OVERVIEW_PEEK_COMPONENT, peekDuration); mPeekAnim.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { @@ -106,6 +115,21 @@ public class FlingAndHoldTouchController extends PortraitStatesTouchController { return mStartState == NORMAL && (stateFlags & SYSUI_STATE_OVERVIEW_DISABLED) == 0; } + @Override + protected AnimatorSetBuilder getAnimatorSetBuilderForStates(LauncherState fromState, + LauncherState toState) { + if (fromState == NORMAL && toState == ALL_APPS) { + AnimatorSetBuilder builder = new AnimatorSetBuilder(); + + // Get workspace out of the way quickly, to prepare for potential pause. + builder.setInterpolator(ANIM_WORKSPACE_SCALE, DEACCEL_3); + builder.setInterpolator(ANIM_WORKSPACE_TRANSLATE, DEACCEL_3); + builder.setInterpolator(ANIM_WORKSPACE_FADE, DEACCEL_3); + return builder; + } + return super.getAnimatorSetBuilderForStates(fromState, toState); + } + @Override public boolean onDrag(float displacement, MotionEvent event) { float upDisplacement = -displacement; @@ -123,8 +147,11 @@ public class FlingAndHoldTouchController extends PortraitStatesTouchController { } AnimatorSetBuilder builder = new AnimatorSetBuilder(); - builder.setInterpolator(AnimatorSetBuilder.ANIM_VERTICAL_PROGRESS, OVERSHOOT_1_2); - builder.setInterpolator(AnimatorSetBuilder.ANIM_WORKSPACE_TRANSLATE, OVERSHOOT_1_2); + builder.setInterpolator(ANIM_VERTICAL_PROGRESS, OVERSHOOT_1_2); + if ((OVERVIEW.getVisibleElements(mLauncher) & HOTSEAT_ICONS) != 0) { + builder.setInterpolator(ANIM_HOTSEAT_SCALE, OVERSHOOT_1_2); + builder.setInterpolator(ANIM_HOTSEAT_TRANSLATE, OVERSHOOT_1_2); + } AnimatorSet overviewAnim = mLauncher.getStateManager().createAtomicAnimation( NORMAL, OVERVIEW, builder, ANIM_ALL, ATOMIC_DURATION); overviewAnim.addListener(new AnimatorListenerAdapter() { diff --git a/quickstep/res/values/dimens.xml b/quickstep/res/values/dimens.xml index 82d1aa6728..c7801a952c 100644 --- a/quickstep/res/values/dimens.xml +++ b/quickstep/res/values/dimens.xml @@ -24,7 +24,7 @@ 10dp 70dp - 32dp + 96dp diff --git a/quickstep/src/com/android/launcher3/uioverrides/states/AllAppsState.java b/quickstep/src/com/android/launcher3/uioverrides/states/AllAppsState.java index ab24f5f5a1..85a9545073 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/states/AllAppsState.java +++ b/quickstep/src/com/android/launcher3/uioverrides/states/AllAppsState.java @@ -23,6 +23,7 @@ import com.android.launcher3.Launcher; import com.android.launcher3.LauncherState; import com.android.launcher3.allapps.AllAppsContainerView; import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType; +import com.android.quickstep.SysUINavigationMode; /** * Definition for AllApps state @@ -63,7 +64,13 @@ public class AllAppsState extends LauncherState { public ScaleAndTranslation getWorkspaceScaleAndTranslation(Launcher launcher) { ScaleAndTranslation scaleAndTranslation = LauncherState.OVERVIEW .getWorkspaceScaleAndTranslation(launcher); - scaleAndTranslation.scale = 1; + if (SysUINavigationMode.getMode(launcher) == SysUINavigationMode.Mode.NO_BUTTON) { + float normalScale = 1; + // Scale down halfway to where we'd be in overview, to prepare for a potential pause. + scaleAndTranslation.scale = (scaleAndTranslation.scale + normalScale) / 2; + } else { + scaleAndTranslation.scale = 1; + } return scaleAndTranslation; } diff --git a/quickstep/src/com/android/quickstep/views/ShelfScrimView.java b/quickstep/src/com/android/quickstep/views/ShelfScrimView.java index 36521e5cc9..b6ddb5fd1d 100644 --- a/quickstep/src/com/android/quickstep/views/ShelfScrimView.java +++ b/quickstep/src/com/android/quickstep/views/ShelfScrimView.java @@ -18,6 +18,7 @@ package com.android.quickstep.views; import static com.android.launcher3.LauncherState.BACKGROUND_APP; import static com.android.launcher3.LauncherState.OVERVIEW; import static com.android.launcher3.anim.Interpolators.ACCEL; +import static com.android.launcher3.anim.Interpolators.ACCEL_2; import static com.android.launcher3.anim.Interpolators.LINEAR; import static com.android.launcher3.icons.GraphicsUtils.setColorAlphaBound; @@ -29,6 +30,7 @@ import android.graphics.Path; import android.graphics.Path.Direction; import android.graphics.Path.Op; import android.util.AttributeSet; +import android.view.animation.Interpolator; import com.android.launcher3.DeviceProfile; import com.android.launcher3.R; @@ -69,6 +71,9 @@ public class ShelfScrimView extends ScrimView implements NavigationModeChangeLis private int mMidAlpha; private float mMidProgress; + private Interpolator mBeforeMidProgressColorInterpolator = ACCEL; + private Interpolator mAfterMidProgressColorInterpolator = ACCEL; + private float mShiftRange; private final float mShelfOffset; @@ -120,6 +125,15 @@ public class ShelfScrimView extends ScrimView implements NavigationModeChangeLis @Override public void onNavigationModeChanged(Mode newMode) { mSysUINavigationMode = newMode; + // Note that these interpolators are inverted because progress goes 1 to 0. + if (mSysUINavigationMode == Mode.NO_BUTTON) { + // Show the shelf more quickly before reaching overview progress. + mBeforeMidProgressColorInterpolator = ACCEL_2; + mAfterMidProgressColorInterpolator = ACCEL; + } else { + mBeforeMidProgressColorInterpolator = ACCEL; + mAfterMidProgressColorInterpolator = Interpolators.clampToProgress(ACCEL, 0.5f, 1f); + } } @Override @@ -171,7 +185,7 @@ public class ShelfScrimView extends ScrimView implements NavigationModeChangeLis mRemainingScreenColor = 0; int alpha = Math.round(Utilities.mapToRange( - mProgress, mMidProgress, 1, mMidAlpha, 0, ACCEL)); + mProgress, mMidProgress, 1, mMidAlpha, 0, mBeforeMidProgressColorInterpolator)); mShelfColor = setColorAlphaBound(mEndScrim, alpha); } else { mDragHandleOffset += mShiftRange * (mMidProgress - mProgress); @@ -179,7 +193,7 @@ public class ShelfScrimView extends ScrimView implements NavigationModeChangeLis // Note that these ranges and interpolators are inverted because progress goes 1 to 0. int alpha = Math.round( Utilities.mapToRange(mProgress, (float) 0, mMidProgress, (float) mEndAlpha, - (float) mMidAlpha, Interpolators.clampToProgress(ACCEL, 0.5f, 1f))); + (float) mMidAlpha, mAfterMidProgressColorInterpolator)); mShelfColor = setColorAlphaBound(mEndScrim, alpha); int remainingScrimAlpha = Math.round( diff --git a/src/com/android/launcher3/LauncherState.java b/src/com/android/launcher3/LauncherState.java index 1480648895..dcfd272b47 100644 --- a/src/com/android/launcher3/LauncherState.java +++ b/src/com/android/launcher3/LauncherState.java @@ -17,7 +17,18 @@ package com.android.launcher3; import static android.view.View.IMPORTANT_FOR_ACCESSIBILITY_AUTO; import static android.view.View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS; +import static android.view.View.VISIBLE; import static android.view.accessibility.AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED; + +import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_OVERVIEW_FADE; +import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_OVERVIEW_SCALE; +import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_OVERVIEW_TRANSLATE_X; +import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_WORKSPACE_FADE; +import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_WORKSPACE_SCALE; +import static com.android.launcher3.anim.Interpolators.ACCEL; +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.testing.TestProtocol.ALL_APPS_STATE_ORDINAL; import static com.android.launcher3.testing.TestProtocol.BACKGROUND_APP_STATE_ORDINAL; import static com.android.launcher3.testing.TestProtocol.NORMAL_STATE_ORDINAL; @@ -30,11 +41,11 @@ import static com.android.launcher3.states.RotationHelper.REQUEST_NONE; import android.view.animation.Interpolator; +import com.android.launcher3.anim.AnimatorSetBuilder; import com.android.launcher3.states.SpringLoadedState; import com.android.launcher3.uioverrides.UiFactory; import com.android.launcher3.uioverrides.states.AllAppsState; import com.android.launcher3.uioverrides.states.OverviewState; -import com.android.launcher3.userevent.nano.LauncherLogProto.Action; import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType; import java.util.Arrays; @@ -272,6 +283,46 @@ public class LauncherState { } } + /** + * Prepares for a non-user controlled animation from fromState to this state. Preparations + * include: + * - Setting interpolators for various animations included in the state transition. + * - Setting some start values (e.g. scale) for views that are hidden but about to be shown. + */ + public void prepareForAtomicAnimation(Launcher launcher, LauncherState fromState, + AnimatorSetBuilder builder) { + if (this == NORMAL && fromState == OVERVIEW) { + builder.setInterpolator(ANIM_WORKSPACE_SCALE, DEACCEL); + builder.setInterpolator(ANIM_WORKSPACE_FADE, ACCEL); + builder.setInterpolator(ANIM_OVERVIEW_SCALE, clampToProgress(ACCEL, 0, 0.9f)); + builder.setInterpolator(ANIM_OVERVIEW_TRANSLATE_X, ACCEL); + builder.setInterpolator(ANIM_OVERVIEW_FADE, DEACCEL_1_7); + Workspace workspace = launcher.getWorkspace(); + + // Start from a higher workspace scale, but only if we're invisible so we don't jump. + boolean isWorkspaceVisible = workspace.getVisibility() == VISIBLE; + if (isWorkspaceVisible) { + CellLayout currentChild = (CellLayout) workspace.getChildAt( + workspace.getCurrentPage()); + isWorkspaceVisible = currentChild.getVisibility() == VISIBLE + && currentChild.getShortcutsAndWidgets().getAlpha() > 0; + } + if (!isWorkspaceVisible) { + workspace.setScaleX(0.92f); + workspace.setScaleY(0.92f); + } + Hotseat hotseat = launcher.getHotseat(); + boolean isHotseatVisible = hotseat.getVisibility() == VISIBLE && hotseat.getAlpha() > 0; + if (!isHotseatVisible) { + hotseat.setScaleX(0.92f); + hotseat.setScaleY(0.92f); + } + } else if (this == NORMAL && fromState == OVERVIEW_PEEK) { + // Keep fully visible until the very end (when overview is offscreen) to make invisible. + builder.setInterpolator(ANIM_OVERVIEW_FADE, t -> t < 1 ? 0 : 1); + } + } + protected static void dispatchWindowStateChanged(Launcher launcher) { launcher.getWindow().getDecorView().sendAccessibilityEvent(TYPE_WINDOW_STATE_CHANGED); } diff --git a/src/com/android/launcher3/LauncherStateManager.java b/src/com/android/launcher3/LauncherStateManager.java index 4425e2af61..505918e09e 100644 --- a/src/com/android/launcher3/LauncherStateManager.java +++ b/src/com/android/launcher3/LauncherStateManager.java @@ -16,23 +16,7 @@ package com.android.launcher3; -import static android.view.View.VISIBLE; - import static com.android.launcher3.LauncherState.NORMAL; -import static com.android.launcher3.LauncherState.OVERVIEW; -import static com.android.launcher3.LauncherState.OVERVIEW_PEEK; -import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_OVERVIEW_FADE; -import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_OVERVIEW_SCALE; -import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_OVERVIEW_TRANSLATE_X; -import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_WORKSPACE_FADE; -import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_WORKSPACE_SCALE; -import static com.android.launcher3.anim.Interpolators.ACCEL; -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.INSTANT; -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.anim.Interpolators.clampToProgress; import static com.android.launcher3.anim.PropertySetter.NO_ANIM_PROPERTY_SETTER; import android.animation.Animator; @@ -42,8 +26,6 @@ import android.os.Handler; import android.os.Looper; import android.util.Log; -import androidx.annotation.IntDef; - import com.android.launcher3.anim.AnimationSuccessListener; import com.android.launcher3.anim.AnimatorPlaybackController; import com.android.launcher3.anim.AnimatorSetBuilder; @@ -58,6 +40,8 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; +import androidx.annotation.IntDef; + /** * TODO: figure out what kind of tests we can write for this * @@ -314,47 +298,7 @@ public class LauncherStateManager { */ public void prepareForAtomicAnimation(LauncherState fromState, LauncherState toState, AnimatorSetBuilder builder) { - if (fromState == NORMAL && toState == OVERVIEW) { - builder.setInterpolator(ANIM_WORKSPACE_SCALE, OVERSHOOT_1_2); - 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_FADE, OVERSHOOT_1_2); - - // Start from a higher overview scale, but only if we're invisible so we don't jump. - UiFactory.prepareToShowOverview(mLauncher); - } else if (fromState == OVERVIEW && toState == NORMAL) { - builder.setInterpolator(ANIM_WORKSPACE_SCALE, DEACCEL); - builder.setInterpolator(ANIM_WORKSPACE_FADE, ACCEL); - builder.setInterpolator(ANIM_OVERVIEW_SCALE, clampToProgress(ACCEL, 0, 0.9f)); - builder.setInterpolator(ANIM_OVERVIEW_TRANSLATE_X, ACCEL); - builder.setInterpolator(ANIM_OVERVIEW_FADE, DEACCEL_1_7); - Workspace workspace = mLauncher.getWorkspace(); - - // Start from a higher workspace scale, but only if we're invisible so we don't jump. - boolean isWorkspaceVisible = workspace.getVisibility() == VISIBLE; - if (isWorkspaceVisible) { - CellLayout currentChild = (CellLayout) workspace.getChildAt( - workspace.getCurrentPage()); - isWorkspaceVisible = currentChild.getVisibility() == VISIBLE - && currentChild.getShortcutsAndWidgets().getAlpha() > 0; - } - if (!isWorkspaceVisible) { - workspace.setScaleX(0.92f); - workspace.setScaleY(0.92f); - } - Hotseat hotseat = workspace.getHotseat(); - boolean isHotseatVisible = hotseat.getVisibility() == VISIBLE && hotseat.getAlpha() > 0; - if (!isHotseatVisible) { - hotseat.setScaleX(0.92f); - hotseat.setScaleY(0.92f); - } - } else if (fromState == NORMAL && toState == OVERVIEW_PEEK) { - builder.setInterpolator(ANIM_OVERVIEW_FADE, INSTANT); - } else if (fromState == OVERVIEW_PEEK && toState == NORMAL) { - // Keep fully visible until the very end (when overview is offscreen) to make invisible. - builder.setInterpolator(ANIM_OVERVIEW_FADE, t -> t < 1 ? 0 : 1); - } + toState.prepareForAtomicAnimation(mLauncher, fromState, builder); } public AnimatorSet createAtomicAnimation(LauncherState fromState, LauncherState toState, diff --git a/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java b/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java index 065d065682..40c6b5f1b4 100644 --- a/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java +++ b/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java @@ -19,6 +19,8 @@ package com.android.launcher3; import static com.android.launcher3.LauncherAnimUtils.DRAWABLE_ALPHA; import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY; import static com.android.launcher3.LauncherState.HOTSEAT_ICONS; +import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_HOTSEAT_SCALE; +import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_HOTSEAT_TRANSLATE; import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_WORKSPACE_FADE; import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_WORKSPACE_SCALE; import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_WORKSPACE_TRANSLATE; @@ -104,7 +106,10 @@ public class WorkspaceStateTransitionAnimation { hotseat.setPivotY(workspacePivot[1]); } float hotseatScale = hotseatScaleAndTranslation.scale; - propertySetter.setFloat(hotseat, SCALE_PROPERTY, hotseatScale, scaleInterpolator); + Interpolator hotseatScaleInterpolator = builder.getInterpolator(ANIM_HOTSEAT_SCALE, + scaleInterpolator); + propertySetter.setFloat(hotseat, SCALE_PROPERTY, hotseatScale, + hotseatScaleInterpolator); float hotseatIconsAlpha = (elements & HOTSEAT_ICONS) != 0 ? 1 : 0; propertySetter.setViewAlpha(hotseat, hotseatIconsAlpha, fadeInterpolator); @@ -125,10 +130,12 @@ public class WorkspaceStateTransitionAnimation { propertySetter.setFloat(mWorkspace, View.TRANSLATION_Y, scaleAndTranslation.translationY, translationInterpolator); + Interpolator hotseatTranslationInterpolator = builder.getInterpolator( + ANIM_HOTSEAT_TRANSLATE, translationInterpolator); propertySetter.setFloat(hotseat, View.TRANSLATION_Y, - hotseatScaleAndTranslation.translationY, translationInterpolator); + hotseatScaleAndTranslation.translationY, hotseatTranslationInterpolator); propertySetter.setFloat(mWorkspace.getPageIndicator(), View.TRANSLATION_Y, - hotseatScaleAndTranslation.translationY, translationInterpolator); + hotseatScaleAndTranslation.translationY, hotseatTranslationInterpolator); setScrim(propertySetter, state); } diff --git a/src/com/android/launcher3/anim/AnimatorSetBuilder.java b/src/com/android/launcher3/anim/AnimatorSetBuilder.java index 5c498f8bb0..52a896eda1 100644 --- a/src/com/android/launcher3/anim/AnimatorSetBuilder.java +++ b/src/com/android/launcher3/anim/AnimatorSetBuilder.java @@ -32,11 +32,13 @@ public class AnimatorSetBuilder { public static final int ANIM_WORKSPACE_SCALE = 1; public static final int ANIM_WORKSPACE_TRANSLATE = 2; public static final int ANIM_WORKSPACE_FADE = 3; - public static final int ANIM_OVERVIEW_SCALE = 4; - public static final int ANIM_OVERVIEW_TRANSLATE_X = 5; - public static final int ANIM_OVERVIEW_TRANSLATE_Y = 6; - public static final int ANIM_OVERVIEW_FADE = 7; - public static final int ANIM_ALL_APPS_FADE = 8; + public static final int ANIM_HOTSEAT_SCALE = 4; + public static final int ANIM_HOTSEAT_TRANSLATE = 5; + public static final int ANIM_OVERVIEW_SCALE = 6; + public static final int ANIM_OVERVIEW_TRANSLATE_X = 7; + public static final int ANIM_OVERVIEW_TRANSLATE_Y = 8; + public static final int ANIM_OVERVIEW_FADE = 9; + public static final int ANIM_ALL_APPS_FADE = 10; public static final int FLAG_DONT_ANIMATE_OVERVIEW = 1 << 0; diff --git a/src_ui_overrides/com/android/launcher3/uioverrides/UiFactory.java b/src_ui_overrides/com/android/launcher3/uioverrides/UiFactory.java index e41916cf47..17ff66e92c 100644 --- a/src_ui_overrides/com/android/launcher3/uioverrides/UiFactory.java +++ b/src_ui_overrides/com/android/launcher3/uioverrides/UiFactory.java @@ -69,8 +69,6 @@ public class UiFactory { return false; } - public static void prepareToShowOverview(Launcher launcher) { } - public static void setBackButtonAlpha(Launcher launcher, float alpha, boolean animate) { } From b80941bb24dbaa1b392d6d37bfff39cf3542aa6b Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Wed, 19 Jun 2019 21:30:40 -0700 Subject: [PATCH 15/41] Using FloatProperty for spring animation, instead of a interface to allow easier generalization of animation definitions Change-Id: I37b1a604003ec007aa390eabdfe8c1ab733b7471 --- .../LauncherActivityControllerHelper.java | 3 +- .../util/StaggeredWorkspaceAnim.java | 10 ++-- .../android/quickstep/views/RecentsView.java | 16 +++---- .../android/launcher3/LauncherAnimUtils.java | 47 +++++++++++-------- .../android/launcher3/ProgressInterface.java | 26 ---------- .../allapps/AllAppsTransitionController.java | 16 +++---- .../launcher3/anim/SpringObjectAnimator.java | 46 +++++++----------- 7 files changed, 63 insertions(+), 101 deletions(-) delete mode 100644 src/com/android/launcher3/ProgressInterface.java diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityControllerHelper.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityControllerHelper.java index e7d085ca14..755d978fc1 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityControllerHelper.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityControllerHelper.java @@ -21,6 +21,7 @@ import static com.android.launcher3.LauncherState.BACKGROUND_APP; import static com.android.launcher3.LauncherState.NORMAL; import static com.android.launcher3.LauncherState.OVERVIEW; import static com.android.launcher3.LauncherStateManager.ANIM_ALL; +import static com.android.launcher3.allapps.AllAppsTransitionController.ALL_APPS_PROGRESS; import static com.android.launcher3.allapps.AllAppsTransitionController.SPRING_DAMPING_RATIO; import static com.android.launcher3.allapps.AllAppsTransitionController.SPRING_STIFFNESS; import static com.android.launcher3.anim.Interpolators.ACCEL_2; @@ -380,7 +381,7 @@ public final class LauncherActivityControllerHelper implements ActivityControlHe private Animator createShelfAnim(Launcher activity, float ... progressValues) { Animator shiftAnim = new SpringObjectAnimator<>(activity.getAllAppsController(), - "allAppsSpringFromACH", activity.getAllAppsController().getShiftRange(), + ALL_APPS_PROGRESS, activity.getAllAppsController().getShiftRange(), SPRING_DAMPING_RATIO, SPRING_STIFFNESS, progressValues); return shiftAnim; } diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/StaggeredWorkspaceAnim.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/StaggeredWorkspaceAnim.java index 9eda2f9d4d..07e96869ed 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/StaggeredWorkspaceAnim.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/StaggeredWorkspaceAnim.java @@ -17,19 +17,15 @@ package com.android.quickstep.util; import android.animation.Animator; import android.animation.ObjectAnimator; -import android.animation.ValueAnimator; import android.view.View; import android.view.ViewGroup; import androidx.annotation.Nullable; -import androidx.dynamicanimation.animation.SpringForce; import com.android.launcher3.CellLayout; import com.android.launcher3.DeviceProfile; import com.android.launcher3.Launcher; -import com.android.launcher3.LauncherAnimUtils.ViewProgressProperty; import com.android.launcher3.LauncherState; -import com.android.launcher3.LauncherStateManager; import com.android.launcher3.LauncherStateManager.AnimationConfig; import com.android.launcher3.R; import com.android.launcher3.ShortcutAndWidgetContainer; @@ -40,6 +36,7 @@ import com.android.launcher3.anim.SpringObjectAnimator; import java.util.ArrayList; import java.util.List; +import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_Y; import static com.android.launcher3.LauncherState.BACKGROUND_APP; import static com.android.launcher3.LauncherState.NORMAL; import static com.android.launcher3.anim.Interpolators.LINEAR; @@ -147,9 +144,8 @@ public class StaggeredWorkspaceAnim { long startDelay = (long) ((invertedRow + 1) * APP_CLOSE_ROW_START_DELAY_MS); v.setTranslationY(mSpringTransY); - SpringObjectAnimator springTransY = new SpringObjectAnimator<>( - new ViewProgressProperty(v, View.TRANSLATION_Y), "staggeredSpringTransY", 1f, - DAMPING_RATIO, STIFFNESS, mSpringTransY, 0); + SpringObjectAnimator springTransY = new SpringObjectAnimator<>(v, VIEW_TRANSLATE_Y, + 1f, DAMPING_RATIO, STIFFNESS, mSpringTransY, 0); springTransY.setStartDelay(startDelay); mAnimators.add(springTransY); diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java index a8987a3d42..06a8e2eaf8 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java @@ -19,6 +19,8 @@ package com.android.quickstep.views; import static com.android.launcher3.BaseActivity.STATE_HANDLER_INVISIBILITY_FLAGS; import static com.android.launcher3.InvariantDeviceProfile.CHANGE_FLAG_ICON_PARAMS; import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY; +import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_X; +import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_Y; import static com.android.launcher3.Utilities.EDGE_NAV_BAR; import static com.android.launcher3.Utilities.squaredHypot; import static com.android.launcher3.Utilities.squaredTouchSlop; @@ -79,7 +81,6 @@ import com.android.launcher3.BaseActivity; import com.android.launcher3.DeviceProfile; import com.android.launcher3.Insettable; import com.android.launcher3.InvariantDeviceProfile; -import com.android.launcher3.LauncherAnimUtils.ViewProgressProperty; import com.android.launcher3.LauncherState; import com.android.launcher3.PagedView; import com.android.launcher3.R; @@ -1031,9 +1032,9 @@ public abstract class RecentsView extends PagedView impl private void addDismissedTaskAnimations(View taskView, AnimatorSet anim, long duration) { addAnim(ObjectAnimator.ofFloat(taskView, ALPHA, 0), duration, ACCEL_2, anim); if (QUICKSTEP_SPRINGS.get() && taskView instanceof TaskView) - addAnim(new SpringObjectAnimator<>(new ViewProgressProperty(taskView, - View.TRANSLATION_Y), "taskViewTransY", SPRING_MIN_VISIBLE_CHANGE, - SPRING_DAMPING_RATIO, SPRING_STIFFNESS, 0, -taskView.getHeight()), + addAnim(new SpringObjectAnimator<>(taskView, VIEW_TRANSLATE_Y, + SPRING_MIN_VISIBLE_CHANGE, SPRING_DAMPING_RATIO, SPRING_STIFFNESS, + 0, -taskView.getHeight()), duration, LINEAR, anim); else { addAnim(ObjectAnimator.ofFloat(taskView, TRANSLATION_Y, -taskView.getHeight()), @@ -1109,10 +1110,9 @@ public abstract class RecentsView extends PagedView impl int scrollDiff = newScroll[i] - oldScroll[i] + offset; if (scrollDiff != 0) { if (QUICKSTEP_SPRINGS.get() && child instanceof TaskView) { - addAnim(new SpringObjectAnimator<>( - new ViewProgressProperty(child, View.TRANSLATION_X), - "taskViewTransX", SPRING_MIN_VISIBLE_CHANGE, SPRING_DAMPING_RATIO, - SPRING_STIFFNESS, 0, scrollDiff), duration, ACCEL, anim); + addAnim(new SpringObjectAnimator<>(child, VIEW_TRANSLATE_X, + SPRING_MIN_VISIBLE_CHANGE, SPRING_DAMPING_RATIO, SPRING_STIFFNESS, + 0, scrollDiff), duration, ACCEL, anim); } else { addAnim(ObjectAnimator.ofFloat(child, TRANSLATION_X, scrollDiff), duration, ACCEL, anim); diff --git a/src/com/android/launcher3/LauncherAnimUtils.java b/src/com/android/launcher3/LauncherAnimUtils.java index 04f2b52766..74362ed5a3 100644 --- a/src/com/android/launcher3/LauncherAnimUtils.java +++ b/src/com/android/launcher3/LauncherAnimUtils.java @@ -17,6 +17,7 @@ package com.android.launcher3; import android.graphics.drawable.Drawable; +import android.util.FloatProperty; import android.util.Property; import android.view.View; import android.view.ViewGroup.LayoutParams; @@ -47,15 +48,15 @@ public class LauncherAnimUtils { } }; - public static final Property SCALE_PROPERTY = - new Property(Float.class, "scale") { + public static final FloatProperty SCALE_PROPERTY = + new FloatProperty("scale") { @Override public Float get(View view) { return view.getScaleX(); } @Override - public void set(View view, Float scale) { + public void setValue(View view, float scale) { view.setScaleX(scale); view.setScaleY(scale); } @@ -92,23 +93,31 @@ public class LauncherAnimUtils { } }; - public static class ViewProgressProperty implements ProgressInterface { - View mView; - Property mProperty; + public static final FloatProperty VIEW_TRANSLATE_X = + View.TRANSLATION_X instanceof FloatProperty ? (FloatProperty) View.TRANSLATION_X + : new FloatProperty("translateX") { + @Override + public void setValue(View view, float v) { + view.setTranslationX(v); + } - public ViewProgressProperty(View view, Property property) { - mView = view; - mProperty = property; - } + @Override + public Float get(View view) { + return view.getTranslationX(); + } + }; - @Override - public void setProgress(float progress) { - mProperty.set(mView, progress); - } + public static final FloatProperty VIEW_TRANSLATE_Y = + View.TRANSLATION_Y instanceof FloatProperty ? (FloatProperty) View.TRANSLATION_Y + : new FloatProperty("translateY") { + @Override + public void setValue(View view, float v) { + view.setTranslationY(v); + } - @Override - public float getProgress() { - return mProperty.get(mView); - } - } + @Override + public Float get(View view) { + return view.getTranslationY(); + } + }; } diff --git a/src/com/android/launcher3/ProgressInterface.java b/src/com/android/launcher3/ProgressInterface.java deleted file mode 100644 index 663d8ba5e5..0000000000 --- a/src/com/android/launcher3/ProgressInterface.java +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.launcher3; - -/** - * Progress is defined as a value with range [0, 1], and is specific to each implementor. - * It is used when there is a transition from one state of the UI to another. - */ -public interface ProgressInterface { - void setProgress(float progress); - float getProgress(); -} \ No newline at end of file diff --git a/src/com/android/launcher3/allapps/AllAppsTransitionController.java b/src/com/android/launcher3/allapps/AllAppsTransitionController.java index a351b9ae1d..3289d7d1d1 100644 --- a/src/com/android/launcher3/allapps/AllAppsTransitionController.java +++ b/src/com/android/launcher3/allapps/AllAppsTransitionController.java @@ -16,8 +16,8 @@ import static com.android.launcher3.util.SystemUiController.UI_STATE_ALL_APPS; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; +import android.util.FloatProperty; import android.util.Log; -import android.util.Property; import android.view.animation.Interpolator; import com.android.launcher3.DeviceProfile; @@ -26,7 +26,6 @@ import com.android.launcher3.Launcher; import com.android.launcher3.LauncherState; import com.android.launcher3.LauncherStateManager.AnimationConfig; import com.android.launcher3.LauncherStateManager.StateHandler; -import com.android.launcher3.ProgressInterface; import com.android.launcher3.R; import com.android.launcher3.anim.AnimationSuccessListener; import com.android.launcher3.anim.AnimatorSetBuilder; @@ -46,14 +45,13 @@ import com.android.launcher3.views.ScrimView; * If release velocity < THRES1, snap according to either top or bottom depending on whether it's * closer to top or closer to the page indicator. */ -public class AllAppsTransitionController implements StateHandler, OnDeviceProfileChangeListener, - ProgressInterface { +public class AllAppsTransitionController implements StateHandler, OnDeviceProfileChangeListener { public static final float SPRING_DAMPING_RATIO = 0.9f; public static final float SPRING_STIFFNESS = 600f; - public static final Property ALL_APPS_PROGRESS = - new Property(Float.class, "allAppsProgress") { + public static final FloatProperty ALL_APPS_PROGRESS = + new FloatProperty("allAppsProgress") { @Override public Float get(AllAppsTransitionController controller) { @@ -61,7 +59,7 @@ public class AllAppsTransitionController implements StateHandler, OnDeviceProfil } @Override - public void set(AllAppsTransitionController controller, Float progress) { + public void setValue(AllAppsTransitionController controller, float progress) { controller.setProgress(progress); } }; @@ -121,7 +119,6 @@ public class AllAppsTransitionController implements StateHandler, OnDeviceProfil * @see #setState(LauncherState) * @see #setStateWithAnimation(LauncherState, AnimatorSetBuilder, AnimationConfig) */ - @Override public void setProgress(float progress) { mProgress = progress; mScrimView.setProgress(progress); @@ -149,7 +146,6 @@ public class AllAppsTransitionController implements StateHandler, OnDeviceProfil } } - @Override public float getProgress() { return mProgress; } @@ -192,7 +188,7 @@ public class AllAppsTransitionController implements StateHandler, OnDeviceProfil Interpolator interpolator = config.userControlled ? LINEAR : toState == OVERVIEW ? builder.getInterpolator(ANIM_OVERVIEW_SCALE, FAST_OUT_SLOW_IN) : FAST_OUT_SLOW_IN; - Animator anim = new SpringObjectAnimator<>(this, "allAppsSpringFromAATC", 1f / mShiftRange, + Animator anim = new SpringObjectAnimator<>(this, ALL_APPS_PROGRESS, 1f / mShiftRange, SPRING_DAMPING_RATIO, SPRING_STIFFNESS, mProgress, targetProgress); anim.setDuration(config.duration); anim.setInterpolator(builder.getInterpolator(ANIM_VERTICAL_PROGRESS, interpolator)); diff --git a/src/com/android/launcher3/anim/SpringObjectAnimator.java b/src/com/android/launcher3/anim/SpringObjectAnimator.java index b1395af89f..395fed259a 100644 --- a/src/com/android/launcher3/anim/SpringObjectAnimator.java +++ b/src/com/android/launcher3/anim/SpringObjectAnimator.java @@ -15,6 +15,8 @@ */ package com.android.launcher3.anim; +import static androidx.dynamicanimation.animation.FloatPropertyCompat.createFloatPropertyCompat; + import static com.android.launcher3.config.FeatureFlags.QUICKSTEP_SPRINGS; import android.animation.Animator; @@ -24,15 +26,12 @@ import android.animation.TimeInterpolator; import android.animation.ValueAnimator; import android.os.Handler; import android.os.Looper; +import android.util.FloatProperty; import android.util.Log; -import android.util.Property; - -import com.android.launcher3.ProgressInterface; import java.util.ArrayList; import androidx.dynamicanimation.animation.DynamicAnimation.OnAnimationEndListener; -import androidx.dynamicanimation.animation.FloatPropertyCompat; import androidx.dynamicanimation.animation.SpringAnimation; import androidx.dynamicanimation.animation.SpringForce; @@ -40,12 +39,11 @@ import androidx.dynamicanimation.animation.SpringForce; * This animator allows for an object's property to be be controlled by an {@link ObjectAnimator} or * a {@link SpringAnimation}. It extends ValueAnimator so it can be used in an AnimatorSet. */ -public class SpringObjectAnimator extends ValueAnimator { +public class SpringObjectAnimator extends ValueAnimator { private static final String TAG = "SpringObjectAnimator"; private static boolean DEBUG = false; - private T mObject; private ObjectAnimator mObjectAnimator; private float[] mValues; @@ -57,29 +55,15 @@ public class SpringObjectAnimator extends ValueAnim private boolean mAnimatorEnded = true; private boolean mEnded = true; - private static final FloatPropertyCompat sFloatProperty = - new FloatPropertyCompat("springObjectAnimator") { - @Override - public float getValue(ProgressInterface object) { - return object.getProgress(); - } - - @Override - public void setValue(ProgressInterface object, float progress) { - object.setProgress(progress); - } - }; - - public SpringObjectAnimator(T object, String name, float minimumVisibleChange, float damping, - float stiffness, float... values) { - mObject = object; - mSpring = new SpringAnimation(object, sFloatProperty); + public SpringObjectAnimator(T object, FloatProperty property, float minimumVisibleChange, + float damping, float stiffness, float... values) { + mSpring = new SpringAnimation(object, createFloatPropertyCompat(property)); mSpring.setMinimumVisibleChange(minimumVisibleChange); mSpring.setSpring(new SpringForce(0) .setDampingRatio(damping) .setStiffness(stiffness)); mSpring.setStartVelocity(0.01f); - mProperty = new SpringProperty(name, mSpring); + mProperty = new SpringProperty<>(property, mSpring); mObjectAnimator = ObjectAnimator.ofFloat(object, mProperty, values); mValues = values; mListeners = new ArrayList<>(); @@ -285,13 +269,15 @@ public class SpringObjectAnimator extends ValueAnim mObjectAnimator.setCurrentPlayTime(playTime); } - public static class SpringProperty extends Property { + public static class SpringProperty extends FloatProperty { boolean useSpring = false; + final FloatProperty mProperty; final SpringAnimation mSpring; - public SpringProperty(String name, SpringAnimation spring) { - super(Float.class, name); + public SpringProperty(FloatProperty property, SpringAnimation spring) { + super(property.getName()); + mProperty = property; mSpring = spring; } @@ -301,15 +287,15 @@ public class SpringObjectAnimator extends ValueAnim @Override public Float get(T object) { - return object.getProgress(); + return mProperty.get(object); } @Override - public void set(T object, Float progress) { + public void setValue(T object, float progress) { if (useSpring) { mSpring.animateToFinalPosition(progress); } else { - object.setProgress(progress); + mProperty.setValue(object, progress); } } } From f806125f991671230aefc1acc87e4a4e4c410c2c Mon Sep 17 00:00:00 2001 From: vadimt Date: Thu, 20 Jun 2019 12:31:01 -0700 Subject: [PATCH 16/41] TAPL: switching dismissing tasks to a model-time gesture Change-Id: I4068c4102cd881b89b680c61699ba78832499a3d --- .../android/launcher3/tapl/LauncherInstrumentation.java | 2 +- tests/tapl/com/android/launcher3/tapl/OverviewTask.java | 9 ++++++--- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java index 22b04fb041..ffb42fbf58 100644 --- a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java +++ b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java @@ -692,7 +692,7 @@ public final class LauncherInstrumentation { // Inject a swipe gesture. Inject exactly 'steps' motion points, incrementing event time by a // fixed interval each time. - private void linearGesture(int startX, int startY, int endX, int endY, int steps) { + void linearGesture(int startX, int startY, int endX, int endY, int steps) { final long downTime = SystemClock.uptimeMillis(); final Point start = new Point(startX, startY); final Point end = new Point(endX, endY); diff --git a/tests/tapl/com/android/launcher3/tapl/OverviewTask.java b/tests/tapl/com/android/launcher3/tapl/OverviewTask.java index 8b124641f6..641c413538 100644 --- a/tests/tapl/com/android/launcher3/tapl/OverviewTask.java +++ b/tests/tapl/com/android/launcher3/tapl/OverviewTask.java @@ -16,7 +16,8 @@ package com.android.launcher3.tapl; -import androidx.test.uiautomator.Direction; +import android.graphics.Rect; + import androidx.test.uiautomator.UiObject2; import androidx.test.uiautomator.Until; @@ -26,7 +27,6 @@ import com.android.launcher3.testing.TestProtocol; * A recent task in the overview panel carousel. */ public final class OverviewTask { - static final int FLING_SPEED = 3000; private static final long WAIT_TIME_MS = 60000; private final LauncherInstrumentation mLauncher; private final UiObject2 mTask; @@ -51,7 +51,10 @@ public final class OverviewTask { "want to dismiss a task")) { verifyActiveContainer(); // Dismiss the task via flinging it up. - mTask.fling(Direction.DOWN, (int) (FLING_SPEED * mLauncher.getDisplayDensity())); + final Rect taskBounds = mTask.getVisibleBounds(); + final int centerX = taskBounds.centerX(); + final int centerY = taskBounds.centerY(); + mLauncher.linearGesture(centerX, centerY, centerX, 0, 10); mLauncher.waitForIdle(); } } From 73769f0a88c44ffa4f3212a612e5018085591ceb Mon Sep 17 00:00:00 2001 From: Winson Chung Date: Thu, 20 Jun 2019 13:02:10 -0700 Subject: [PATCH 17/41] Fixing rounding error when computing bottom clip - In TaskThumbnailView.updateThumbnailMatrix() we compare the rounded scaled bitmap height with the height of the task/thumbnail view. Since the position is also non-integer, the rounding of the sum is not equivalent to the rounding of the individual components, which causes the check to fail. Bug: 135150767 Test: Quickswitch on sargo Change-Id: I849b4732569e5b8699f00f5ff210f41fb505f1e7 --- quickstep/src/com/android/quickstep/util/LayoutUtils.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/quickstep/src/com/android/quickstep/util/LayoutUtils.java b/quickstep/src/com/android/quickstep/util/LayoutUtils.java index c8aed81918..050bdff09b 100644 --- a/quickstep/src/com/android/quickstep/util/LayoutUtils.java +++ b/quickstep/src/com/android/quickstep/util/LayoutUtils.java @@ -110,7 +110,7 @@ public class LayoutUtils { float y = insets.top + Math.max(topIconMargin, (launcherVisibleHeight - extraVerticalSpace - outHeight) / 2); outRect.set(Math.round(x), Math.round(y), - Math.round(x + outWidth), Math.round(y + outHeight)); + Math.round(x) + Math.round(outWidth), Math.round(y) + Math.round(outHeight)); } public static int getShelfTrackingDistance(Context context, DeviceProfile dp) { From e244a5802d48c1d57279e5a95eacc97156f96a04 Mon Sep 17 00:00:00 2001 From: vadimt Date: Thu, 20 Jun 2019 14:26:19 -0700 Subject: [PATCH 18/41] Tests: waiting for nav bar to match nav mode Change-Id: Ib17fce4577e1e2c585108f0e95a98fad710e5eb2 --- .../quickstep/NavigationModeSwitchRule.java | 17 ++++++++---- .../tapl/LauncherInstrumentation.java | 26 ++++++++++++------- 2 files changed, 29 insertions(+), 14 deletions(-) diff --git a/quickstep/tests/src/com/android/quickstep/NavigationModeSwitchRule.java b/quickstep/tests/src/com/android/quickstep/NavigationModeSwitchRule.java index 90763b871b..3b35c86af8 100644 --- a/quickstep/tests/src/com/android/quickstep/NavigationModeSwitchRule.java +++ b/quickstep/tests/src/com/android/quickstep/NavigationModeSwitchRule.java @@ -159,13 +159,20 @@ public class NavigationModeSwitchRule implements TestRule { } for (int i = 0; i != 100; ++i) { - if (mLauncher.getNavigationModel() == expectedMode) { - Thread.sleep(5000); - return; - } + if (mLauncher.getNavigationModel() == expectedMode) break; Thread.sleep(100); } - Assert.fail("Couldn't switch to " + overlayPackage); + Assert.assertTrue("Couldn't switch to " + overlayPackage, + mLauncher.getNavigationModel() == expectedMode); + + for (int i = 0; i != 100; ++i) { + if (mLauncher.getNavigationModeMismatchError() == null) break; + Thread.sleep(100); + } + final String error = mLauncher.getNavigationModeMismatchError(); + Assert.assertTrue("Switching nav mode: " + error, error == null); + + Thread.sleep(5000); } private void setOverlayPackageEnabled(String overlayPackage, boolean enable) diff --git a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java index 22b04fb041..9336806ae4 100644 --- a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java +++ b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java @@ -315,18 +315,26 @@ public final class LauncherInstrumentation { mExpectedRotation = expectedRotation; } - private UiObject2 verifyContainerType(ContainerType containerType) { - assertEquals("Unexpected display rotation", - mExpectedRotation, mDevice.getDisplayRotation()); + public String getNavigationModeMismatchError() { final NavigationModel navigationModel = getNavigationModel(); final boolean hasRecentsButton = hasSystemUiObject("recent_apps"); final boolean hasHomeButton = hasSystemUiObject("home"); - assertTrue("Presence of recents button doesn't match the interaction mode, mode=" - + navigationModel.name() + ", hasRecents=" + hasRecentsButton, - (navigationModel == NavigationModel.THREE_BUTTON) == hasRecentsButton); - assertTrue("Presence of home button doesn't match the interaction mode, mode=" - + navigationModel.name() + ", hasHome=" + hasHomeButton, - (navigationModel != NavigationModel.ZERO_BUTTON) == hasHomeButton); + if ((navigationModel == NavigationModel.THREE_BUTTON) != hasRecentsButton) { + return "Presence of recents button doesn't match the interaction mode, mode=" + + navigationModel.name() + ", hasRecents=" + hasRecentsButton; + } + if ((navigationModel != NavigationModel.ZERO_BUTTON) != hasHomeButton) { + return "Presence of home button doesn't match the interaction mode, mode=" + + navigationModel.name() + ", hasHome=" + hasHomeButton; + } + return null; + } + + private UiObject2 verifyContainerType(ContainerType containerType) { + assertEquals("Unexpected display rotation", + mExpectedRotation, mDevice.getDisplayRotation()); + final String error = getNavigationModeMismatchError(); + assertTrue(error, error == null); log("verifyContainerType: " + containerType); try (Closable c = addContextLayer( From 1c1b86bf5127f9092d04b09681d92e1e1c61d8af Mon Sep 17 00:00:00 2001 From: Lyn Han Date: Wed, 19 Jun 2019 14:02:38 -0700 Subject: [PATCH 19/41] Dot location getters For use by SystemUI-Bubbles flyout-to-dot animation Bug: 129158983 Test: manual (Bubbles dot looks good) Change-Id: I53287efcfe3e02b4ba0572d62f138cc52f7f66a6 --- .../src/com/android/launcher3/icons/DotRenderer.java | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/iconloaderlib/src/com/android/launcher3/icons/DotRenderer.java b/iconloaderlib/src/com/android/launcher3/icons/DotRenderer.java index af07aa3a99..97a0fd3ffc 100644 --- a/iconloaderlib/src/com/android/launcher3/icons/DotRenderer.java +++ b/iconloaderlib/src/com/android/launcher3/icons/DotRenderer.java @@ -85,6 +85,14 @@ public class DotRenderer { return pos; } + public float[] getLeftDotPosition() { + return mLeftDotPosition; + } + + public float[] getRightDotPosition() { + return mRightDotPosition; + } + /** * Draw a circle on top of the canvas according to the given params. */ From 715de78e94cf811562a55f7bc341d51f953bd05f Mon Sep 17 00:00:00 2001 From: vadimt Date: Thu, 20 Jun 2019 16:20:36 -0700 Subject: [PATCH 20/41] Tests: removing workaround for Overview crashing on rotation Bug: 135287203 Change-Id: I9766f385be5331f56638438822f321ccd1efc920 --- tests/src/com/android/launcher3/ui/PortraitLandscapeRunner.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/tests/src/com/android/launcher3/ui/PortraitLandscapeRunner.java b/tests/src/com/android/launcher3/ui/PortraitLandscapeRunner.java index 20cd1e7d2e..d7c14118d0 100644 --- a/tests/src/com/android/launcher3/ui/PortraitLandscapeRunner.java +++ b/tests/src/com/android/launcher3/ui/PortraitLandscapeRunner.java @@ -49,14 +49,12 @@ class PortraitLandscapeRunner implements TestRule { mTest.mDevice.setOrientationNatural(); mTest.mLauncher.setExpectedRotation(Surface.ROTATION_0); base.evaluate(); - mTest.mLauncher.pressHome(); } private void evaluateInLandscape() throws Throwable { mTest.mDevice.setOrientationLeft(); mTest.mLauncher.setExpectedRotation(Surface.ROTATION_90); base.evaluate(); - mTest.mLauncher.pressHome(); } }; } From d11b604e194489dca0f2e54defab64bff5d47132 Mon Sep 17 00:00:00 2001 From: vadimt Date: Thu, 20 Jun 2019 17:52:16 -0700 Subject: [PATCH 21/41] Converting some tests to TAPL Change-Id: I1b67f80d9d61792d5795f9638bf9d19919f36d39 --- .../com/android/launcher3/ui/TestViewHelpers.java | 15 --------------- .../launcher3/ui/widget/RequestPinItemTest.java | 11 +++++------ 2 files changed, 5 insertions(+), 21 deletions(-) diff --git a/tests/src/com/android/launcher3/ui/TestViewHelpers.java b/tests/src/com/android/launcher3/ui/TestViewHelpers.java index a73bde0116..d13d319525 100644 --- a/tests/src/com/android/launcher3/ui/TestViewHelpers.java +++ b/tests/src/com/android/launcher3/ui/TestViewHelpers.java @@ -54,21 +54,6 @@ public class TestViewHelpers { return UiDevice.getInstance(getInstrumentation()); } - /** - * Opens all apps and returns the recycler view - */ - public static UiObject2 openAllApps() { - final UiDevice device = getDevice(); - device.waitForIdle(); - UiObject2 hotseat = device.wait( - Until.findObject(getSelectorForId(R.id.hotseat)), 2500); - Point start = hotseat.getVisibleCenter(); - int endY = (int) (device.getDisplayHeight() * 0.1f); - // 100 px/step - device.swipe(start.x, start.y, start.x, endY, (start.y - endY) / 100); - return findViewById(R.id.apps_list_view); - } - public static UiObject2 findViewById(int id) { return getDevice().wait(Until.findObject(getSelectorForId(id)), AbstractLauncherUiTest.DEFAULT_UI_TIMEOUT); diff --git a/tests/src/com/android/launcher3/ui/widget/RequestPinItemTest.java b/tests/src/com/android/launcher3/ui/widget/RequestPinItemTest.java index be27a66a4c..e4f9d7889f 100644 --- a/tests/src/com/android/launcher3/ui/widget/RequestPinItemTest.java +++ b/tests/src/com/android/launcher3/ui/widget/RequestPinItemTest.java @@ -41,7 +41,6 @@ import com.android.launcher3.testcomponent.AppWidgetNoConfig; import com.android.launcher3.testcomponent.AppWidgetWithConfig; import com.android.launcher3.testcomponent.RequestPinItemActivity; import com.android.launcher3.ui.AbstractLauncherUiTest; -import com.android.launcher3.ui.TestViewHelpers; import com.android.launcher3.util.Condition; import com.android.launcher3.util.Wait; import com.android.launcher3.util.rule.ShellCommandRule; @@ -149,14 +148,14 @@ public class RequestPinItemTest extends AbstractLauncherUiTest { clearHomescreen(); mActivityMonitor.startLauncher(); - // Open all apps and wait for load complete - final UiObject2 appsContainer = TestViewHelpers.openAllApps(); - Wait.atMost(null, Condition.minChildCount(appsContainer, 2), DEFAULT_UI_TIMEOUT); - // Open Pin item activity BlockingBroadcastReceiver openMonitor = new BlockingBroadcastReceiver( RequestPinItemActivity.class.getName()); - scrollAndFind(appsContainer, By.text("Test Pin Item")).click(); + mLauncher. + getWorkspace(). + switchToAllApps(). + getAppIcon("Test Pin Item"). + launch("com.google.android.apps.nexuslauncher.tests"); assertNotNull(openMonitor.blockingGetExtraIntent()); // Set callback From 6346451bd6fa1e419b2fc31fba8fcfb008ed024d Mon Sep 17 00:00:00 2001 From: vadimt Date: Fri, 21 Jun 2019 10:58:03 -0700 Subject: [PATCH 22/41] Tests: fixing expected package name for AOSP tests Change-Id: Id7a57d4bfcbed23ba20ca11846680fa1341e8a99 --- .../com/android/launcher3/ui/widget/RequestPinItemTest.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/src/com/android/launcher3/ui/widget/RequestPinItemTest.java b/tests/src/com/android/launcher3/ui/widget/RequestPinItemTest.java index e4f9d7889f..2766a3e3bd 100644 --- a/tests/src/com/android/launcher3/ui/widget/RequestPinItemTest.java +++ b/tests/src/com/android/launcher3/ui/widget/RequestPinItemTest.java @@ -15,6 +15,8 @@ */ package com.android.launcher3.ui.widget; +import static com.android.launcher3.ui.TaplTestsLauncher3.getAppPackageName; + import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNotSame; @@ -155,7 +157,7 @@ public class RequestPinItemTest extends AbstractLauncherUiTest { getWorkspace(). switchToAllApps(). getAppIcon("Test Pin Item"). - launch("com.google.android.apps.nexuslauncher.tests"); + launch(getAppPackageName()); assertNotNull(openMonitor.blockingGetExtraIntent()); // Set callback From 70266689b776736b4236c35ee3ab68e08cdff1e1 Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Fri, 21 Jun 2019 12:30:48 -0700 Subject: [PATCH 23/41] Using a common gradle dependency path Change-Id: If01b0d7ebd6719935ac467a5f085b0517b133bbc --- iconloaderlib/build.gradle | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/iconloaderlib/build.gradle b/iconloaderlib/build.gradle index 49d427ede6..8a4d2b76e5 100644 --- a/iconloaderlib/build.gradle +++ b/iconloaderlib/build.gradle @@ -1,13 +1,3 @@ -buildscript { - repositories { - mavenCentral() - google() - } - dependencies { - classpath GRADLE_CLASS_PATH - } -} - apply plugin: 'com.android.library' android { @@ -44,12 +34,6 @@ android { } } - -repositories { - mavenCentral() - google() -} - dependencies { implementation "androidx.core:core:${ANDROID_X_VERSION}" } From c4bb3739b1ff3b3e34daa1fc53550b2ef2e8398a Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Thu, 20 Jun 2019 11:34:14 -0700 Subject: [PATCH 24/41] Cleaning up some animation states: > When running one-off animations during quickstep, cancelling prevoisly running animations. > Cancelling such one-off animations when state is reset > Preventing touch proxied from recent transition to affect quickswitch (by affecting pagedView) Bug: 135686388 Bug: 135571566 Change-Id: Id647015a583761d8fd46a02e3e2d88027e282a79 --- .../LauncherAppTransitionManagerImpl.java | 35 ++++++- .../LauncherActivityControllerHelper.java | 95 +++++++------------ .../quickstep/RecentsAnimationWrapper.java | 5 + .../android/quickstep/views/RecentsView.java | 12 +-- .../LauncherAppTransitionManager.java | 12 +++ .../launcher3/LauncherStateManager.java | 46 +++++++++ src/com/android/launcher3/PagedView.java | 8 +- src/com/android/launcher3/Utilities.java | 10 ++ .../allapps/AllAppsTransitionController.java | 12 ++- .../launcher3/views/BaseDragLayer.java | 3 + 10 files changed, 161 insertions(+), 77 deletions(-) diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java index eac4dce180..371161ebdc 100644 --- a/quickstep/recents_ui_overrides/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java +++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java @@ -16,25 +16,32 @@ package com.android.launcher3; +import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_X; import static com.android.launcher3.LauncherState.NORMAL; +import static com.android.launcher3.allapps.AllAppsTransitionController.ALL_APPS_PROGRESS; import static com.android.launcher3.anim.Interpolators.AGGRESSIVE_EASE; import static com.android.launcher3.anim.Interpolators.LINEAR; import static com.android.quickstep.TaskViewUtils.findTaskViewToLaunch; import static com.android.quickstep.TaskViewUtils.getRecentsWindowAnimator; +import static androidx.dynamicanimation.animation.DynamicAnimation.MIN_VISIBLE_CHANGE_PIXELS; +import static androidx.dynamicanimation.animation.SpringForce.DAMPING_RATIO_MEDIUM_BOUNCY; +import static androidx.dynamicanimation.animation.SpringForce.STIFFNESS_MEDIUM; + import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.AnimatorSet; import android.animation.ObjectAnimator; -import android.app.ActivityOptions; import android.content.Context; import android.view.View; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import com.android.launcher3.allapps.AllAppsTransitionController; import com.android.launcher3.anim.AnimatorPlaybackController; import com.android.launcher3.anim.Interpolators; +import com.android.launcher3.anim.SpringObjectAnimator; import com.android.quickstep.util.ClipAnimationHelper; import com.android.quickstep.views.RecentsView; import com.android.quickstep.views.TaskView; @@ -46,11 +53,12 @@ import com.android.systemui.shared.system.RemoteAnimationTargetCompat; */ public final class LauncherAppTransitionManagerImpl extends QuickstepAppTransitionManagerImpl { - private RecentsView mRecentsView; + public static final int INDEX_SHELF_ANIM = 0; + public static final int INDEX_RECENTS_FADE_ANIM = 1; + public static final int INDEX_RECENTS_TRANSLATE_X_ANIM = 2; public LauncherAppTransitionManagerImpl(Context context) { super(context); - mRecentsView = mLauncher.getOverviewPanel(); } @Override @@ -133,4 +141,25 @@ public final class LauncherAppTransitionManagerImpl extends QuickstepAppTransiti mLauncher.getStateManager().reapplyState(); }; } + + @Override + public int getStateElementAnimationsCount() { + return 3; + } + + @Override + public Animator createStateElementAnimation(int index, float... values) { + switch (index) { + case INDEX_SHELF_ANIM: + return mLauncher.getAllAppsController().createSpringAnimation(values); + case INDEX_RECENTS_FADE_ANIM: + return ObjectAnimator.ofFloat(mLauncher.getOverviewPanel(), + RecentsView.CONTENT_ALPHA, values); + case INDEX_RECENTS_TRANSLATE_X_ANIM: + return new SpringObjectAnimator<>(mLauncher.getOverviewPanel(), + VIEW_TRANSLATE_X, MIN_VISIBLE_CHANGE_PIXELS, 0.8f, 250, values); + default: + return super.createStateElementAnimation(index, values); + } + } } diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityControllerHelper.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityControllerHelper.java index 755d978fc1..07c0496423 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityControllerHelper.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityControllerHelper.java @@ -16,14 +16,15 @@ package com.android.quickstep; import static android.view.View.TRANSLATION_Y; + import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY; +import static com.android.launcher3.LauncherAppTransitionManagerImpl.INDEX_RECENTS_FADE_ANIM; +import static com.android.launcher3.LauncherAppTransitionManagerImpl.INDEX_RECENTS_TRANSLATE_X_ANIM; +import static com.android.launcher3.LauncherAppTransitionManagerImpl.INDEX_SHELF_ANIM; import static com.android.launcher3.LauncherState.BACKGROUND_APP; import static com.android.launcher3.LauncherState.NORMAL; import static com.android.launcher3.LauncherState.OVERVIEW; import static com.android.launcher3.LauncherStateManager.ANIM_ALL; -import static com.android.launcher3.allapps.AllAppsTransitionController.ALL_APPS_PROGRESS; -import static com.android.launcher3.allapps.AllAppsTransitionController.SPRING_DAMPING_RATIO; -import static com.android.launcher3.allapps.AllAppsTransitionController.SPRING_STIFFNESS; import static com.android.launcher3.anim.Interpolators.ACCEL_2; import static com.android.launcher3.anim.Interpolators.ACCEL_DEACCEL; import static com.android.launcher3.anim.Interpolators.INSTANT; @@ -31,7 +32,6 @@ import static com.android.launcher3.anim.Interpolators.LINEAR; import static com.android.quickstep.WindowTransformSwipeHandler.RECENTS_ATTACH_DURATION; import android.animation.Animator; -import android.animation.AnimatorListenerAdapter; import android.animation.AnimatorSet; import android.animation.ObjectAnimator; import android.animation.TimeInterpolator; @@ -48,8 +48,6 @@ import android.view.animation.Interpolator; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.annotation.UiThread; -import androidx.dynamicanimation.animation.SpringAnimation; -import androidx.dynamicanimation.animation.SpringForce; import com.android.launcher3.DeviceProfile; import com.android.launcher3.Launcher; @@ -60,7 +58,6 @@ import com.android.launcher3.LauncherStateManager; import com.android.launcher3.allapps.DiscoveryBounce; import com.android.launcher3.anim.AnimatorPlaybackController; import com.android.launcher3.anim.AnimatorSetBuilder; -import com.android.launcher3.anim.SpringObjectAnimator; import com.android.launcher3.testing.TestProtocol; import com.android.launcher3.uioverrides.states.OverviewState; import com.android.launcher3.userevent.nano.LauncherLogProto; @@ -68,6 +65,7 @@ import com.android.launcher3.views.FloatingIconView; import com.android.quickstep.SysUINavigationMode.Mode; import com.android.quickstep.util.LayoutUtils; import com.android.quickstep.util.StaggeredWorkspaceAnim; +import com.android.quickstep.views.LauncherRecentsView; import com.android.quickstep.views.RecentsView; import com.android.quickstep.views.TaskView; import com.android.systemui.shared.system.RemoteAnimationTargetCompat; @@ -217,10 +215,7 @@ public final class LauncherActivityControllerHelper implements ActivityControlHe activity.getAppsView().getContentView().setVisibility(View.GONE); return new AnimationFactory() { - private Animator mShelfAnim; private ShelfAnimState mShelfState; - private Animator mAttachToWindowFadeAnim; - private SpringAnimation mAttachToWindowTranslationXAnim; private boolean mIsAttachedToWindow; @Override @@ -253,9 +248,7 @@ public final class LauncherActivityControllerHelper implements ActivityControlHe return; } mShelfState = shelfState; - if (mShelfAnim != null) { - mShelfAnim.cancel(); - } + activity.getStateManager().cancelStateElementAnimation(INDEX_SHELF_ANIM); if (mShelfState == ShelfAnimState.CANCEL) { return; } @@ -271,16 +264,10 @@ public final class LauncherActivityControllerHelper implements ActivityControlHe : mShelfState == ShelfAnimState.PEEK ? shelfPeekingProgress : shelfOverviewProgress; - mShelfAnim = createShelfAnim(activity, toProgress); - mShelfAnim.addListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - mShelfAnim = null; - } - }); - mShelfAnim.setInterpolator(interpolator); - mShelfAnim.setDuration(duration); - mShelfAnim.start(); + Animator shelfAnim = activity.getStateManager() + .createStateElementAnimation(INDEX_SHELF_ANIM, toProgress); + shelfAnim.setInterpolator(interpolator); + shelfAnim.setDuration(duration).start(); } @Override @@ -289,12 +276,10 @@ public final class LauncherActivityControllerHelper implements ActivityControlHe return; } mIsAttachedToWindow = attached; - if (mAttachToWindowFadeAnim != null) { - mAttachToWindowFadeAnim.cancel(); - } - RecentsView recentsView = activity.getOverviewPanel(); - mAttachToWindowFadeAnim = ObjectAnimator.ofFloat(recentsView, - RecentsView.CONTENT_ALPHA, attached ? 1 : 0); + LauncherRecentsView recentsView = activity.getOverviewPanel(); + Animator fadeAnim = activity.getStateManager() + .createStateElementAnimation( + INDEX_RECENTS_FADE_ANIM, attached ? 1 : 0); int runningTaskIndex = recentsView.getRunningTaskIndex(); if (runningTaskIndex == 0) { @@ -316,33 +301,28 @@ public final class LauncherActivityControllerHelper implements ActivityControlHe float fromTranslationX = attached ? offscreenX - scrollOffsetX : 0; float toTranslationX = attached ? 0 : offscreenX - scrollOffsetX; - if (mAttachToWindowTranslationXAnim == null) { - mAttachToWindowTranslationXAnim = new SpringAnimation(recentsView, - SpringAnimation.TRANSLATION_X).setSpring(new SpringForce() - .setDampingRatio(0.8f) - .setStiffness(250)); - } + activity.getStateManager() + .cancelStateElementAnimation(INDEX_RECENTS_TRANSLATE_X_ANIM); + if (!recentsView.isShown() && animate) { recentsView.setTranslationX(fromTranslationX); - mAttachToWindowTranslationXAnim.setStartValue(fromTranslationX); - } - mAttachToWindowTranslationXAnim.animateToFinalPosition(toTranslationX); - if (!animate && mAttachToWindowTranslationXAnim.canSkipToEnd()) { - mAttachToWindowTranslationXAnim.skipToEnd(); + } else { + fromTranslationX = recentsView.getTranslationX(); } - mAttachToWindowFadeAnim.setInterpolator(attached ? INSTANT : ACCEL_2); - } else { - mAttachToWindowFadeAnim.setInterpolator(ACCEL_DEACCEL); - } - mAttachToWindowFadeAnim.addListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - mAttachToWindowFadeAnim = null; + if (!animate) { + recentsView.setTranslationX(toTranslationX); + } else { + activity.getStateManager().createStateElementAnimation( + INDEX_RECENTS_TRANSLATE_X_ANIM, + fromTranslationX, toTranslationX).start(); } - }); - mAttachToWindowFadeAnim.setDuration(animate ? RECENTS_ATTACH_DURATION : 0); - mAttachToWindowFadeAnim.start(); + + fadeAnim.setInterpolator(attached ? INSTANT : ACCEL_2); + } else { + fadeAnim.setInterpolator(ACCEL_DEACCEL); + } + fadeAnim.setDuration(animate ? RECENTS_ATTACH_DURATION : 0).start(); } }; } @@ -358,10 +338,10 @@ public final class LauncherActivityControllerHelper implements ActivityControlHe if (!activity.getDeviceProfile().isVerticalBarLayout() && SysUINavigationMode.getMode(activity) != Mode.NO_BUTTON) { // Don't animate the shelf when the mode is NO_BUTTON, because we update it atomically. - Animator shiftAnim = createShelfAnim(activity, + anim.play(activity.getStateManager().createStateElementAnimation( + INDEX_SHELF_ANIM, fromState.getVerticalProgress(activity), - endState.getVerticalProgress(activity)); - anim.play(shiftAnim); + endState.getVerticalProgress(activity))); } playScaleDownAnim(anim, activity, fromState, endState); @@ -379,13 +359,6 @@ public final class LauncherActivityControllerHelper implements ActivityControlHe callback.accept(controller); } - private Animator createShelfAnim(Launcher activity, float ... progressValues) { - Animator shiftAnim = new SpringObjectAnimator<>(activity.getAllAppsController(), - ALL_APPS_PROGRESS, activity.getAllAppsController().getShiftRange(), - SPRING_DAMPING_RATIO, SPRING_STIFFNESS, progressValues); - return shiftAnim; - } - /** * Scale down recents from the center task being full screen to being in overview. */ diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/RecentsAnimationWrapper.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/RecentsAnimationWrapper.java index 5eecf1713d..ddd28a3500 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/RecentsAnimationWrapper.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/RecentsAnimationWrapper.java @@ -19,6 +19,8 @@ import static android.view.MotionEvent.ACTION_CANCEL; import static android.view.MotionEvent.ACTION_DOWN; import static android.view.MotionEvent.ACTION_UP; +import static com.android.launcher3.Utilities.FLAG_NO_GESTURES; + import android.view.InputEvent; import android.view.KeyEvent; import android.view.MotionEvent; @@ -182,7 +184,10 @@ public class RecentsAnimationWrapper { } } if (mInputConsumer != null) { + int flags = ev.getEdgeFlags(); + ev.setEdgeFlags(flags | FLAG_NO_GESTURES); mInputConsumer.onMotionEvent(ev); + ev.setEdgeFlags(flags); } return true; diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java index 06a8e2eaf8..a98df0fa1f 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java @@ -16,6 +16,8 @@ package com.android.quickstep.views; +import static androidx.dynamicanimation.animation.DynamicAnimation.MIN_VISIBLE_CHANGE_PIXELS; + import static com.android.launcher3.BaseActivity.STATE_HANDLER_INVISIBILITY_FLAGS; import static com.android.launcher3.InvariantDeviceProfile.CHANGE_FLAG_ICON_PARAMS; import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY; @@ -125,10 +127,6 @@ public abstract class RecentsView extends PagedView impl private static final String TAG = RecentsView.class.getSimpleName(); - public static final float SPRING_MIN_VISIBLE_CHANGE = 0.001f; - public static final float SPRING_DAMPING_RATIO = SpringForce.DAMPING_RATIO_MEDIUM_BOUNCY; - public static final float SPRING_STIFFNESS = SpringForce.STIFFNESS_MEDIUM; - public static final FloatProperty CONTENT_ALPHA = new FloatProperty("contentAlpha") { @Override @@ -1033,7 +1031,8 @@ public abstract class RecentsView extends PagedView impl addAnim(ObjectAnimator.ofFloat(taskView, ALPHA, 0), duration, ACCEL_2, anim); if (QUICKSTEP_SPRINGS.get() && taskView instanceof TaskView) addAnim(new SpringObjectAnimator<>(taskView, VIEW_TRANSLATE_Y, - SPRING_MIN_VISIBLE_CHANGE, SPRING_DAMPING_RATIO, SPRING_STIFFNESS, + MIN_VISIBLE_CHANGE_PIXELS, SpringForce.DAMPING_RATIO_MEDIUM_BOUNCY, + SpringForce.STIFFNESS_MEDIUM, 0, -taskView.getHeight()), duration, LINEAR, anim); else { @@ -1111,7 +1110,8 @@ public abstract class RecentsView extends PagedView impl if (scrollDiff != 0) { if (QUICKSTEP_SPRINGS.get() && child instanceof TaskView) { addAnim(new SpringObjectAnimator<>(child, VIEW_TRANSLATE_X, - SPRING_MIN_VISIBLE_CHANGE, SPRING_DAMPING_RATIO, SPRING_STIFFNESS, + MIN_VISIBLE_CHANGE_PIXELS, SpringForce.DAMPING_RATIO_MEDIUM_BOUNCY, + SpringForce.STIFFNESS_MEDIUM, 0, scrollDiff), duration, ACCEL, anim); } else { addAnim(ObjectAnimator.ofFloat(child, TRANSLATION_X, scrollDiff), duration, diff --git a/src/com/android/launcher3/LauncherAppTransitionManager.java b/src/com/android/launcher3/LauncherAppTransitionManager.java index 4bddc6a4ea..c55c120a62 100644 --- a/src/com/android/launcher3/LauncherAppTransitionManager.java +++ b/src/com/android/launcher3/LauncherAppTransitionManager.java @@ -17,6 +17,7 @@ package com.android.launcher3; +import android.animation.Animator; import android.app.ActivityOptions; import android.content.Context; import android.graphics.Rect; @@ -55,4 +56,15 @@ public class LauncherAppTransitionManager implements ResourceBasedOverride { public boolean supportsAdaptiveIconAnimation() { return false; } + + /** + * Number of animations which run on state properties. + */ + public int getStateElementAnimationsCount() { + return 0; + } + + public Animator createStateElementAnimation(int index, float... values) { + throw new RuntimeException("Unknown gesture animation " + index); + } } diff --git a/src/com/android/launcher3/LauncherStateManager.java b/src/com/android/launcher3/LauncherStateManager.java index 505918e09e..2c8c208456 100644 --- a/src/com/android/launcher3/LauncherStateManager.java +++ b/src/com/android/launcher3/LauncherStateManager.java @@ -111,6 +111,9 @@ public class LauncherStateManager { private final Launcher mLauncher; private final ArrayList mListeners = new ArrayList<>(); + // Animators which are run on properties also controlled by state animations. + private Animator[] mStateElementAnimators; + private StateHandler[] mStateHandlers; private LauncherState mState = NORMAL; @@ -209,6 +212,7 @@ public class LauncherStateManager { public void reapplyState(boolean cancelCurrentAnimation) { boolean wasInAnimation = mConfig.mCurrentAnimation != null; if (cancelCurrentAnimation) { + cancelAllStateElementAnimation(); cancelAnimation(); } if (mConfig.mCurrentAnimation == null) { @@ -250,6 +254,7 @@ public class LauncherStateManager { mConfig.reset(); if (!animated) { + cancelAllStateElementAnimation(); onStateTransitionStart(state); for (StateHandler handler : getStateHandlers()) { handler.setState(state); @@ -521,6 +526,47 @@ public class LauncherStateManager { mConfig.setAnimation(anim, null); } + private void cancelAllStateElementAnimation() { + if (mStateElementAnimators == null) { + return; + } + + for (Animator animator : mStateElementAnimators) { + if (animator != null) { + animator.cancel(); + } + } + } + + /** + * Cancels a currently running gesture animation + */ + public void cancelStateElementAnimation(int index) { + if (mStateElementAnimators == null) { + return; + } + if (mStateElementAnimators[index] != null) { + mStateElementAnimators[index].cancel(); + } + } + + public Animator createStateElementAnimation(int index, float... values) { + cancelStateElementAnimation(index); + LauncherAppTransitionManager latm = mLauncher.getAppTransitionManager(); + if (mStateElementAnimators == null) { + mStateElementAnimators = new Animator[latm.getStateElementAnimationsCount()]; + } + Animator anim = latm.createStateElementAnimation(index, values); + mStateElementAnimators[index] = anim; + anim.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + mStateElementAnimators[index] = null; + } + }); + return anim; + } + private void clearCurrentAnimation() { if (mConfig.mCurrentAnimation != null) { mConfig.mCurrentAnimation.removeListener(mConfig); diff --git a/src/com/android/launcher3/PagedView.java b/src/com/android/launcher3/PagedView.java index dfa3e1beed..2eeb132bbf 100644 --- a/src/com/android/launcher3/PagedView.java +++ b/src/com/android/launcher3/PagedView.java @@ -16,6 +16,7 @@ package com.android.launcher3; +import static com.android.launcher3.Utilities.shouldDisableGestures; import static com.android.launcher3.compat.AccessibilityManagerCompat.isAccessibilityEnabled; import static com.android.launcher3.compat.AccessibilityManagerCompat.isObservedEventType; import static com.android.launcher3.config.FeatureFlags.QUICKSTEP_SPRINGS; @@ -844,10 +845,11 @@ public abstract class PagedView extends ViewGrou * If we return true, onTouchEvent will be called and we do the actual * scrolling there. */ - acquireVelocityTrackerAndAddMovement(ev); // Skip touch handling if there are no pages to swipe - if (getChildCount() <= 0) return super.onInterceptTouchEvent(ev); + if (getChildCount() <= 0 || shouldDisableGestures(ev)) return false; + + acquireVelocityTrackerAndAddMovement(ev); /* * Shortcut the most recurring case: the user is in the dragging @@ -1093,7 +1095,7 @@ public abstract class PagedView extends ViewGrou @Override public boolean onTouchEvent(MotionEvent ev) { // Skip touch handling if there are no pages to swipe - if (getChildCount() <= 0) return false; + if (getChildCount() <= 0 || shouldDisableGestures(ev)) return false; acquireVelocityTrackerAndAddMovement(ev); diff --git a/src/com/android/launcher3/Utilities.java b/src/com/android/launcher3/Utilities.java index 7bdbb95f79..65aa3a775d 100644 --- a/src/com/android/launcher3/Utilities.java +++ b/src/com/android/launcher3/Utilities.java @@ -127,6 +127,16 @@ public final class Utilities { */ public static final int EDGE_NAV_BAR = 1 << 8; + /** + * Set on a motion event do disallow any gestures and only handle touch. + * See {@link MotionEvent#setEdgeFlags(int)}. + */ + public static final int FLAG_NO_GESTURES = 1 << 9; + + public static boolean shouldDisableGestures(MotionEvent ev) { + return (ev.getEdgeFlags() & FLAG_NO_GESTURES) == FLAG_NO_GESTURES; + } + /** * Indicates if the device has a debug build. Should only be used to store additional info or * add extra logging and not for changing the app behavior. diff --git a/src/com/android/launcher3/allapps/AllAppsTransitionController.java b/src/com/android/launcher3/allapps/AllAppsTransitionController.java index 3289d7d1d1..4683893ea0 100644 --- a/src/com/android/launcher3/allapps/AllAppsTransitionController.java +++ b/src/com/android/launcher3/allapps/AllAppsTransitionController.java @@ -47,8 +47,8 @@ import com.android.launcher3.views.ScrimView; */ public class AllAppsTransitionController implements StateHandler, OnDeviceProfileChangeListener { - public static final float SPRING_DAMPING_RATIO = 0.9f; - public static final float SPRING_STIFFNESS = 600f; + private static final float SPRING_DAMPING_RATIO = 0.9f; + private static final float SPRING_STIFFNESS = 600f; public static final FloatProperty ALL_APPS_PROGRESS = new FloatProperty("allAppsProgress") { @@ -188,8 +188,7 @@ public class AllAppsTransitionController implements StateHandler, OnDeviceProfil Interpolator interpolator = config.userControlled ? LINEAR : toState == OVERVIEW ? builder.getInterpolator(ANIM_OVERVIEW_SCALE, FAST_OUT_SLOW_IN) : FAST_OUT_SLOW_IN; - Animator anim = new SpringObjectAnimator<>(this, ALL_APPS_PROGRESS, 1f / mShiftRange, - SPRING_DAMPING_RATIO, SPRING_STIFFNESS, mProgress, targetProgress); + Animator anim = createSpringAnimation(mProgress, targetProgress); anim.setDuration(config.duration); anim.setInterpolator(builder.getInterpolator(ANIM_VERTICAL_PROGRESS, interpolator)); anim.addListener(getProgressAnimatorListener()); @@ -199,6 +198,11 @@ public class AllAppsTransitionController implements StateHandler, OnDeviceProfil setAlphas(toState, config, builder); } + public Animator createSpringAnimation(float... progressValues) { + return new SpringObjectAnimator<>(this, ALL_APPS_PROGRESS, 1f / mShiftRange, + SPRING_DAMPING_RATIO, SPRING_STIFFNESS, progressValues); + } + private void setAlphas(LauncherState toState, AnimationConfig config, AnimatorSetBuilder builder) { setAlphas(toState.getVisibleElements(mLauncher), config, builder); diff --git a/src/com/android/launcher3/views/BaseDragLayer.java b/src/com/android/launcher3/views/BaseDragLayer.java index 51c7022bde..c1ba780503 100644 --- a/src/com/android/launcher3/views/BaseDragLayer.java +++ b/src/com/android/launcher3/views/BaseDragLayer.java @@ -21,6 +21,7 @@ import static android.view.MotionEvent.ACTION_DOWN; import static android.view.MotionEvent.ACTION_UP; import static com.android.launcher3.Utilities.SINGLE_FRAME_MS; +import static com.android.launcher3.Utilities.shouldDisableGestures; import android.annotation.TargetApi; import android.content.Context; @@ -151,6 +152,8 @@ public abstract class BaseDragLayer } private TouchController findControllerToHandleTouch(MotionEvent ev) { + if (shouldDisableGestures(ev)) return null; + AbstractFloatingView topView = AbstractFloatingView.getTopOpenView(mActivity); if (topView != null && topView.onControllerInterceptTouchEvent(ev)) { return topView; From 29794c56a4ffbae6b1f1b420a619877d1da0c4bb Mon Sep 17 00:00:00 2001 From: Adam Cohen Date: Fri, 21 Jun 2019 12:50:30 -0700 Subject: [PATCH 25/41] Workaround issue with latest version of RecyclerView which is overriding our custom accessibility delegate -> Not the prettiest fix, but we only ever use the LauncherAccessibilityDelegate for BubbleTextViews,so anything else getting set here is incorrect. Stop-gap until we root cause the RV issue and decide whether there's a fix there, or a better way to ensure Launcher behaves correctly. issue 129745295 Change-Id: I27cf79fda9edc2bebeda4728b776ab292caa141d --- src/com/android/launcher3/BubbleTextView.java | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java index 7085c60521..1619e3645f 100644 --- a/src/com/android/launcher3/BubbleTextView.java +++ b/src/com/android/launcher3/BubbleTextView.java @@ -43,6 +43,7 @@ import android.view.ViewDebug; import android.widget.TextView; import com.android.launcher3.Launcher.OnResumeCallback; +import com.android.launcher3.accessibility.LauncherAccessibilityDelegate; import com.android.launcher3.dot.DotInfo; import com.android.launcher3.folder.FolderIcon; import com.android.launcher3.graphics.DrawableFactory; @@ -227,6 +228,18 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver, applyFromWorkspaceItem(info, false); } + @Override + public void setAccessibilityDelegate(AccessibilityDelegate delegate) { + if (delegate instanceof LauncherAccessibilityDelegate) { + super.setAccessibilityDelegate(delegate); + } else { + // NO-OP + // Workaround for b/129745295 where RecyclerView is setting our Accessibility + // delegate incorrectly. There are no cases when we shouldn't be using the + // LauncherAccessibilityDelegate for BubbleTextView. + } + } + public void applyFromWorkspaceItem(WorkspaceItemInfo info, boolean promiseStateChanged) { applyIconAndLabel(info); setTag(info); From 1ce213888e48be12031881b685f66dc55816db64 Mon Sep 17 00:00:00 2001 From: vadimt Date: Fri, 21 Jun 2019 14:23:15 -0700 Subject: [PATCH 26/41] Workaround for b/135766310 Bug: 135766310 Change-Id: I8ef40158d5b7f832be46ee2c2fe98a91a095ee82 --- tests/src/com/android/launcher3/ui/PortraitLandscapeRunner.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/src/com/android/launcher3/ui/PortraitLandscapeRunner.java b/tests/src/com/android/launcher3/ui/PortraitLandscapeRunner.java index d7c14118d0..0f36292f90 100644 --- a/tests/src/com/android/launcher3/ui/PortraitLandscapeRunner.java +++ b/tests/src/com/android/launcher3/ui/PortraitLandscapeRunner.java @@ -49,12 +49,14 @@ class PortraitLandscapeRunner implements TestRule { mTest.mDevice.setOrientationNatural(); mTest.mLauncher.setExpectedRotation(Surface.ROTATION_0); base.evaluate(); + mTest.getDevice().pressHome(); } private void evaluateInLandscape() throws Throwable { mTest.mDevice.setOrientationLeft(); mTest.mLauncher.setExpectedRotation(Surface.ROTATION_90); base.evaluate(); + mTest.getDevice().pressHome(); } }; } From b6e2cd8546c76b328aed512c7f67bc0256b326d4 Mon Sep 17 00:00:00 2001 From: vadimt Date: Fri, 21 Jun 2019 14:32:17 -0700 Subject: [PATCH 27/41] Enable @PortraitLandscape for testBackground Now that Recents rotation crash is fixed Change-Id: Ibca9460e45fc533e414537f7923a2d09fc59f58a --- .../tests/src/com/android/quickstep/TaplTestsQuickstep.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java b/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java index 9e3bf2f56e..f02859f462 100644 --- a/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java +++ b/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java @@ -208,7 +208,7 @@ public class TaplTestsQuickstep extends AbstractQuickStepTest { @Test @NavigationModeSwitch -// @PortraitLandscape + @PortraitLandscape public void testBackground() throws Exception { startAppFast(resolveSystemApp(Intent.CATEGORY_APP_CALCULATOR)); final Background background = mLauncher.getBackground(); From e9f1f093c71b112608d8b7ade5be00ba94c07923 Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Thu, 20 Jun 2019 15:17:13 -0700 Subject: [PATCH 28/41] Increasing assistant touch region based on the corner radius Bug: 135687556 Change-Id: I86f0cf3b0e6f3cd6608eab55be6ebf56bfdddf1c --- .../quickstep/TouchInteractionService.java | 45 ++++++++++++++++--- .../AssistantTouchConsumer.java | 11 ----- quickstep/res/values/dimens.xml | 3 +- 3 files changed, 41 insertions(+), 18 deletions(-) diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java index 769d207ab4..22ebe61405 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java @@ -267,6 +267,9 @@ public class TouchInteractionService extends Service implements private Mode mMode = Mode.THREE_BUTTONS; private int mDefaultDisplayId; private final RectF mSwipeTouchRegion = new RectF(); + private final RectF mAssistantLeftRegion = new RectF(); + private final RectF mAssistantRightRegion = new RectF(); + private ComponentName mGestureBlockingActivity; private Region mExclusionRegion; @@ -349,9 +352,25 @@ public class TouchInteractionService extends Service implements defaultDisplay.getRealSize(realSize); mSwipeTouchRegion.set(0, 0, realSize.x, realSize.y); if (mMode == Mode.NO_BUTTON) { - mSwipeTouchRegion.top = mSwipeTouchRegion.bottom - - getNavbarSize(ResourceUtils.NAVBAR_BOTTOM_GESTURE_SIZE); + int touchHeight = getNavbarSize(ResourceUtils.NAVBAR_BOTTOM_GESTURE_SIZE); + mSwipeTouchRegion.top = mSwipeTouchRegion.bottom - touchHeight; + + final int assistantWidth = getResources() + .getDimensionPixelSize(R.dimen.gestures_assistant_width); + final float assistantHeight = Math.max(touchHeight, + QuickStepContract.getWindowCornerRadius(getResources())); + mAssistantLeftRegion.bottom = mAssistantRightRegion.bottom = mSwipeTouchRegion.bottom; + mAssistantLeftRegion.top = mAssistantRightRegion.top = + mSwipeTouchRegion.bottom - assistantHeight; + + mAssistantLeftRegion.left = 0; + mAssistantLeftRegion.right = assistantWidth; + + mAssistantRightRegion.right = mSwipeTouchRegion.right; + mAssistantRightRegion.left = mSwipeTouchRegion.right - assistantWidth; } else { + mAssistantLeftRegion.setEmpty(); + mAssistantRightRegion.setEmpty(); switch (defaultDisplay.getRotation()) { case Surface.ROTATION_90: mSwipeTouchRegion.left = mSwipeTouchRegion.right @@ -491,6 +510,15 @@ public class TouchInteractionService extends Service implements mConsumer = newConsumer(useSharedState, event); TOUCH_INTERACTION_LOG.addLog("setInputConsumer", mConsumer.getType()); mUncheckedConsumer = mConsumer; + } else if (mIsUserUnlocked && mMode == Mode.NO_BUTTON + && canTriggerAssistantAction(event)) { + // Do not change mConsumer as if there is an ongoing QuickSwitch gesture, we should + // not interrupt it. QuickSwitch assumes that interruption can only happen if the + // next gesture is also quick switch. + mUncheckedConsumer = + new AssistantTouchConsumer(this, mISystemUiProxy, + mOverviewComponentObserver.getActivityControlHelper(), + InputConsumer.NO_OP, mInputMonitorCompat); } else { mUncheckedConsumer = InputConsumer.NO_OP; } @@ -505,6 +533,14 @@ public class TouchInteractionService extends Service implements || (mSystemUiStateFlags & SYSUI_STATE_OVERVIEW_DISABLED) == 0); } + private boolean canTriggerAssistantAction(MotionEvent ev) { + return mAssistantAvailable + && !QuickStepContract.isAssistantGestureDisabled(mSystemUiStateFlags) + && (mAssistantLeftRegion.contains(ev.getX(), ev.getY()) || + mAssistantRightRegion.contains(ev.getX(), ev.getY())) + && !ActivityManagerWrapper.getInstance().isLockToAppActive(); + } + private InputConsumer newConsumer(boolean useSharedState, MotionEvent event) { boolean isInValidSystemUiState = validSystemUiFlags(); @@ -525,10 +561,7 @@ public class TouchInteractionService extends Service implements if (mMode == Mode.NO_BUTTON) { final ActivityControlHelper activityControl = mOverviewComponentObserver.getActivityControlHelper(); - if (mAssistantAvailable - && !QuickStepContract.isAssistantGestureDisabled(mSystemUiStateFlags) - && AssistantTouchConsumer.withinTouchRegion(this, event) - && !ActivityManagerWrapper.getInstance().isLockToAppActive()) { + if (canTriggerAssistantAction(event)) { base = new AssistantTouchConsumer(this, mISystemUiProxy, activityControl, base, mInputMonitorCompat); } diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/AssistantTouchConsumer.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/AssistantTouchConsumer.java index 8f92772e83..38b5a137c4 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/AssistantTouchConsumer.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/AssistantTouchConsumer.java @@ -34,7 +34,6 @@ import static com.android.launcher3.userevent.nano.LauncherLogProto.ContainerTyp import android.animation.ValueAnimator; import android.content.Context; import android.content.res.Resources; -import android.gesture.Gesture; import android.graphics.PointF; import android.os.Bundle; import android.os.RemoteException; @@ -50,11 +49,9 @@ import com.android.launcher3.BaseDraggingActivity; import com.android.launcher3.R; import com.android.launcher3.anim.Interpolators; import com.android.launcher3.logging.UserEventDispatcher; -import com.android.launcher3.touch.SwipeDetector; import com.android.quickstep.ActivityControlHelper; import com.android.systemui.shared.recents.ISystemUiProxy; import com.android.systemui.shared.system.InputMonitorCompat; -import com.android.systemui.shared.system.QuickStepContract; /** * Touch consumer for handling events to launch assistant from launcher @@ -270,14 +267,6 @@ public class AssistantTouchConsumer extends DelegateInputConsumer { return (angle > mAngleThreshold && angle < 90); } - public static boolean withinTouchRegion(Context context, MotionEvent ev) { - final Resources res = context.getResources(); - final int width = res.getDisplayMetrics().widthPixels; - final int height = res.getDisplayMetrics().heightPixels; - final int size = res.getDimensionPixelSize(R.dimen.gestures_assistant_size); - return (ev.getX() > width - size || ev.getX() < size) && ev.getY() > height - size; - } - private class AssistantGestureListener extends SimpleOnGestureListener { @Override public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { diff --git a/quickstep/res/values/dimens.xml b/quickstep/res/values/dimens.xml index c7801a952c..71259fd7ae 100644 --- a/quickstep/res/values/dimens.xml +++ b/quickstep/res/values/dimens.xml @@ -66,7 +66,8 @@ 24dp - 48dp + + 48dp 55dp From 7946f557421a9193e0315e1bc54c7f5b7eed71d0 Mon Sep 17 00:00:00 2001 From: Vadim Tryshev Date: Sat, 22 Jun 2019 00:30:57 +0000 Subject: [PATCH 29/41] Revert "Enable @PortraitLandscape for testBackground" This reverts commit b6e2cd8546c76b328aed512c7f67bc0256b326d4. Reason for revert: b/135567630 Change-Id: I4cd87356177a141c99e3865b8794de58386d0f49 --- .../tests/src/com/android/quickstep/TaplTestsQuickstep.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java b/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java index f02859f462..9e3bf2f56e 100644 --- a/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java +++ b/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java @@ -208,7 +208,7 @@ public class TaplTestsQuickstep extends AbstractQuickStepTest { @Test @NavigationModeSwitch - @PortraitLandscape +// @PortraitLandscape public void testBackground() throws Exception { startAppFast(resolveSystemApp(Intent.CATEGORY_APP_CALCULATOR)); final Background background = mLauncher.getBackground(); From b8cb393f62e644c3169372b4cd91013092aa9dbf Mon Sep 17 00:00:00 2001 From: vadimt Date: Mon, 24 Jun 2019 17:05:12 -0700 Subject: [PATCH 30/41] TAPL: swiping up from workspace always starts at the screen bottom This avoids accidentally dragging an icon from hotseat on ultra-slow devices. Change-Id: Id2378a25972bc17dd227e7c227bf963fd1c0e4b2 --- tests/tapl/com/android/launcher3/tapl/Workspace.java | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/tests/tapl/com/android/launcher3/tapl/Workspace.java b/tests/tapl/com/android/launcher3/tapl/Workspace.java index 26c7b8f307..62050b9057 100644 --- a/tests/tapl/com/android/launcher3/tapl/Workspace.java +++ b/tests/tapl/com/android/launcher3/tapl/Workspace.java @@ -213,8 +213,6 @@ public final class Workspace extends Home { @Override protected int getSwipeStartY() { - return mLauncher.getDevice().isNaturalOrientation() ? - mLauncher.waitForLauncherObject("hotseat").getVisibleBounds().top - : mLauncher.getRealDisplaySize().y - 1; + return mLauncher.getRealDisplaySize().y - 1; } } \ No newline at end of file From 700dc99d9b3d7df7e1910fa5661d60714a7f6a0d Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Mon, 24 Jun 2019 16:01:13 -0700 Subject: [PATCH 31/41] Enabling simple gesture navigation for 3P launcher In case of 3P launcher, swipe-up will go to Launcher and there will be no way to reach overview. Bug: 135769778 Change-Id: Ib2c6bb1b13e6055d30b7360ec077b0a2fece66ff --- .../quickstep/TouchInteractionService.java | 5 + .../FallbackNoButtonInputConsumer.java | 358 ++++++++++++++++++ .../inputconsumers/InputConsumer.java | 2 + .../OtherActivityInputConsumer.java | 2 +- .../quickstep/OverviewComponentObserver.java | 27 +- 5 files changed, 392 insertions(+), 2 deletions(-) create mode 100644 quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/FallbackNoButtonInputConsumer.java diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java index 22ebe61405..8f08f0de9b 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java @@ -81,6 +81,7 @@ import com.android.quickstep.SysUINavigationMode.NavigationModeChangeListener; import com.android.quickstep.inputconsumers.AccessibilityInputConsumer; import com.android.quickstep.inputconsumers.AssistantTouchConsumer; import com.android.quickstep.inputconsumers.DeviceLockedInputConsumer; +import com.android.quickstep.inputconsumers.FallbackNoButtonInputConsumer; import com.android.quickstep.inputconsumers.InputConsumer; import com.android.quickstep.inputconsumers.OtherActivityInputConsumer; import com.android.quickstep.inputconsumers.OverviewInputConsumer; @@ -614,6 +615,10 @@ public class TouchInteractionService extends Service implements } else if (mGestureBlockingActivity != null && runningTaskInfo != null && mGestureBlockingActivity.equals(runningTaskInfo.topActivity)) { return mResetGestureInputConsumer; + } else if (mMode == Mode.NO_BUTTON && !mOverviewComponentObserver.isHomeAndOverviewSame()) { + return new FallbackNoButtonInputConsumer(this, activityControl, + mInputMonitorCompat, mSwipeSharedState, mSwipeTouchRegion, + mOverviewComponentObserver, disableHorizontalSwipe(event), runningTaskInfo); } else { return createOtherActivityInputConsumer(event, runningTaskInfo); } diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/FallbackNoButtonInputConsumer.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/FallbackNoButtonInputConsumer.java new file mode 100644 index 0000000000..d05ca2a161 --- /dev/null +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/FallbackNoButtonInputConsumer.java @@ -0,0 +1,358 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.quickstep.inputconsumers; + +import static android.view.MotionEvent.ACTION_CANCEL; +import static android.view.MotionEvent.ACTION_DOWN; +import static android.view.MotionEvent.ACTION_MOVE; +import static android.view.MotionEvent.ACTION_POINTER_DOWN; +import static android.view.MotionEvent.ACTION_POINTER_UP; +import static android.view.MotionEvent.ACTION_UP; +import static android.view.MotionEvent.INVALID_POINTER_ID; + +import static com.android.quickstep.WindowTransformSwipeHandler.MAX_SWIPE_DURATION; +import static com.android.quickstep.WindowTransformSwipeHandler.MIN_PROGRESS_FOR_OVERVIEW; +import static com.android.quickstep.WindowTransformSwipeHandler.MIN_SWIPE_DURATION; +import static com.android.quickstep.inputconsumers.OtherActivityInputConsumer.QUICKSTEP_TOUCH_SLOP_RATIO; +import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_RECENTS; + +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.animation.ValueAnimator; +import android.app.ActivityManager.RunningTaskInfo; +import android.content.Context; +import android.content.Intent; +import android.graphics.PointF; +import android.graphics.Rect; +import android.graphics.RectF; +import android.view.MotionEvent; +import android.view.VelocityTracker; +import android.view.ViewConfiguration; +import android.view.WindowManager; + +import com.android.launcher3.DeviceProfile; +import com.android.launcher3.InvariantDeviceProfile; +import com.android.launcher3.R; +import com.android.quickstep.ActivityControlHelper; +import com.android.quickstep.OverviewComponentObserver; +import com.android.quickstep.SwipeSharedState; +import com.android.quickstep.util.ClipAnimationHelper; +import com.android.quickstep.util.ClipAnimationHelper.TransformParams; +import com.android.quickstep.util.NavBarPosition; +import com.android.quickstep.util.RecentsAnimationListenerSet; +import com.android.quickstep.util.SwipeAnimationTargetSet; +import com.android.quickstep.util.SwipeAnimationTargetSet.SwipeAnimationListener; +import com.android.systemui.shared.system.ActivityManagerWrapper; +import com.android.systemui.shared.system.BackgroundExecutor; +import com.android.systemui.shared.system.InputMonitorCompat; +import com.android.systemui.shared.system.RemoteAnimationTargetCompat; + +public class FallbackNoButtonInputConsumer implements InputConsumer, SwipeAnimationListener { + + private static final int STATE_NOT_FINISHED = 0; + private static final int STATE_FINISHED_TO_HOME = 1; + private static final int STATE_FINISHED_TO_APP = 2; + + private static final float PROGRESS_TO_END_GESTURE = -2; + + private final ActivityControlHelper mActivityControlHelper; + private final InputMonitorCompat mInputMonitor; + private final Context mContext; + private final NavBarPosition mNavBarPosition; + private final SwipeSharedState mSwipeSharedState; + private final OverviewComponentObserver mOverviewComponentObserver; + private final int mRunningTaskId; + + private final ClipAnimationHelper mClipAnimationHelper; + private final TransformParams mTransformParams = new TransformParams(); + private final float mTransitionDragLength; + private final DeviceProfile mDP; + + private final RectF mSwipeTouchRegion; + private final boolean mDisableHorizontalSwipe; + + private final PointF mDownPos = new PointF(); + private final PointF mLastPos = new PointF(); + + private int mActivePointerId = -1; + // Slop used to determine when we say that the gesture has started. + private boolean mPassedPilferInputSlop; + + private VelocityTracker mVelocityTracker; + + // Distance after which we start dragging the window. + private final float mTouchSlop; + + // Might be displacement in X or Y, depending on the direction we are swiping from the nav bar. + private float mStartDisplacement; + private SwipeAnimationTargetSet mSwipeAnimationTargetSet; + private float mProgress; + + private int mState = STATE_NOT_FINISHED; + + public FallbackNoButtonInputConsumer(Context context, + ActivityControlHelper activityControlHelper, InputMonitorCompat inputMonitor, + SwipeSharedState swipeSharedState, RectF swipeTouchRegion, + OverviewComponentObserver overviewComponentObserver, + boolean disableHorizontalSwipe, RunningTaskInfo runningTaskInfo) { + mContext = context; + mActivityControlHelper = activityControlHelper; + mInputMonitor = inputMonitor; + mOverviewComponentObserver = overviewComponentObserver; + mRunningTaskId = runningTaskInfo.id; + + mSwipeSharedState = swipeSharedState; + mSwipeTouchRegion = swipeTouchRegion; + mDisableHorizontalSwipe = disableHorizontalSwipe; + + mNavBarPosition = new NavBarPosition(context); + mVelocityTracker = VelocityTracker.obtain(); + + mTouchSlop = QUICKSTEP_TOUCH_SLOP_RATIO + * ViewConfiguration.get(context).getScaledTouchSlop(); + + mClipAnimationHelper = new ClipAnimationHelper(context); + + mDP = InvariantDeviceProfile.INSTANCE.get(context).getDeviceProfile(context).copy(context); + Rect tempRect = new Rect(); + mTransitionDragLength = mActivityControlHelper.getSwipeUpDestinationAndLength( + mDP, context, tempRect); + mClipAnimationHelper.updateTargetRect(tempRect); + } + + @Override + public int getType() { + return TYPE_FALLBACK_NO_BUTTON; + } + + @Override + public void onMotionEvent(MotionEvent ev) { + if (mVelocityTracker == null) { + return; + } + + mVelocityTracker.addMovement(ev); + if (ev.getActionMasked() == ACTION_POINTER_UP) { + mVelocityTracker.clear(); + } + + switch (ev.getActionMasked()) { + case ACTION_DOWN: { + mActivePointerId = ev.getPointerId(0); + mDownPos.set(ev.getX(), ev.getY()); + mLastPos.set(mDownPos); + break; + } + case ACTION_POINTER_DOWN: { + if (!mPassedPilferInputSlop) { + // Cancel interaction in case of multi-touch interaction + int ptrIdx = ev.getActionIndex(); + if (!mSwipeTouchRegion.contains(ev.getX(ptrIdx), ev.getY(ptrIdx))) { + forceCancelGesture(ev); + } + } + break; + } + case ACTION_POINTER_UP: { + int ptrIdx = ev.getActionIndex(); + int ptrId = ev.getPointerId(ptrIdx); + if (ptrId == mActivePointerId) { + final int newPointerIdx = ptrIdx == 0 ? 1 : 0; + mDownPos.set( + ev.getX(newPointerIdx) - (mLastPos.x - mDownPos.x), + ev.getY(newPointerIdx) - (mLastPos.y - mDownPos.y)); + mLastPos.set(ev.getX(newPointerIdx), ev.getY(newPointerIdx)); + mActivePointerId = ev.getPointerId(newPointerIdx); + } + break; + } + case ACTION_MOVE: { + int pointerIndex = ev.findPointerIndex(mActivePointerId); + if (pointerIndex == INVALID_POINTER_ID) { + break; + } + mLastPos.set(ev.getX(pointerIndex), ev.getY(pointerIndex)); + float displacement = getDisplacement(ev); + + if (!mPassedPilferInputSlop) { + if (mDisableHorizontalSwipe && Math.abs(mLastPos.x - mDownPos.x) + > Math.abs(mLastPos.y - mDownPos.y)) { + // Horizontal gesture is not allowed in this region + forceCancelGesture(ev); + break; + } + + if (Math.abs(displacement) >= mTouchSlop) { + mPassedPilferInputSlop = true; + + // Deferred gesture, start the animation and gesture tracking once + // we pass the actual touch slop + startTouchTrackingForWindowAnimation(displacement); + } + } else { + updateDisplacement(displacement - mStartDisplacement); + } + break; + } + case ACTION_CANCEL: + case ACTION_UP: { + finishTouchTracking(ev); + break; + } + } + } + + private void startTouchTrackingForWindowAnimation(float displacement) { + mStartDisplacement = Math.min(displacement, -mTouchSlop); + + RecentsAnimationListenerSet listenerSet = + mSwipeSharedState.newRecentsAnimationListenerSet(); + listenerSet.addListener(this); + Intent homeIntent = mOverviewComponentObserver.getHomeIntent(); + BackgroundExecutor.get().submit( + () -> ActivityManagerWrapper.getInstance().startRecentsActivity( + homeIntent, null, listenerSet, null, null)); + + ActivityManagerWrapper.getInstance().closeSystemWindows( + CLOSE_SYSTEM_WINDOWS_REASON_RECENTS); + mInputMonitor.pilferPointers(); + } + + private void updateDisplacement(float displacement) { + mProgress = displacement / mTransitionDragLength; + mTransformParams.setProgress(mProgress); + + if (mSwipeAnimationTargetSet != null) { + mClipAnimationHelper.applyTransform(mSwipeAnimationTargetSet, mTransformParams); + } + } + + private void forceCancelGesture(MotionEvent ev) { + int action = ev.getAction(); + ev.setAction(ACTION_CANCEL); + finishTouchTracking(ev); + ev.setAction(action); + } + + /** + * Called when the gesture has ended. Does not correlate to the completion of the interaction as + * the animation can still be running. + */ + private void finishTouchTracking(MotionEvent ev) { + if (ev.getAction() == ACTION_CANCEL) { + mState = STATE_FINISHED_TO_APP; + } else { + mVelocityTracker.computeCurrentVelocity(1000, + ViewConfiguration.get(mContext).getScaledMaximumFlingVelocity()); + float velocityX = mVelocityTracker.getXVelocity(mActivePointerId); + float velocityY = mVelocityTracker.getYVelocity(mActivePointerId); + float velocity = mNavBarPosition.isRightEdge() ? velocityX + : mNavBarPosition.isLeftEdge() ? -velocityX + : velocityY; + float flingThreshold = mContext.getResources() + .getDimension(R.dimen.quickstep_fling_threshold_velocity); + boolean isFling = Math.abs(velocity) > flingThreshold; + + boolean goingHome; + if (!isFling) { + goingHome = -mProgress >= MIN_PROGRESS_FOR_OVERVIEW; + } else { + goingHome = velocity < 0; + } + + if (goingHome) { + mState = STATE_FINISHED_TO_HOME; + } else { + mState = STATE_FINISHED_TO_APP; + } + } + + if (mSwipeAnimationTargetSet != null) { + finishAnimationTargetSet(); + } + } + + private void finishAnimationTargetSet() { + if (mState == STATE_FINISHED_TO_APP) { + mSwipeAnimationTargetSet.finishController(false, null, false); + } else { + if (mProgress < PROGRESS_TO_END_GESTURE) { + mSwipeAnimationTargetSet.finishController(true, null, true); + } else { + long duration = (long) (Math.min(mProgress - PROGRESS_TO_END_GESTURE, 1) + * MAX_SWIPE_DURATION / Math.abs(PROGRESS_TO_END_GESTURE)); + if (duration < 0) { + duration = MIN_SWIPE_DURATION; + } + + ValueAnimator anim = ValueAnimator.ofFloat(mProgress, PROGRESS_TO_END_GESTURE); + anim.addUpdateListener(a -> { + float p = (Float) anim.getAnimatedValue(); + mTransformParams.setProgress(p); + mClipAnimationHelper.applyTransform(mSwipeAnimationTargetSet, mTransformParams); + }); + anim.setDuration(duration); + anim.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + mSwipeAnimationTargetSet.finishController(true, null, true); + } + }); + anim.start(); + } + } + } + + @Override + public void onRecentsAnimationStart(SwipeAnimationTargetSet targetSet) { + mSwipeAnimationTargetSet = targetSet; + Rect overviewStackBounds = new Rect(0, 0, mDP.widthPx, mDP.heightPx); + RemoteAnimationTargetCompat runningTaskTarget = targetSet.findTask(mRunningTaskId); + + mDP.updateIsSeascape(mContext.getSystemService(WindowManager.class)); + if (runningTaskTarget != null) { + mClipAnimationHelper.updateSource(overviewStackBounds, runningTaskTarget); + } + mClipAnimationHelper.prepareAnimation(mDP, false /* isOpening */); + + overviewStackBounds + .inset(-overviewStackBounds.width() / 5, -overviewStackBounds.height() / 5); + mClipAnimationHelper.updateTargetRect(overviewStackBounds); + mClipAnimationHelper.applyTransform(mSwipeAnimationTargetSet, mTransformParams); + + if (mState != STATE_NOT_FINISHED) { + finishAnimationTargetSet(); + } + } + + @Override + public void onRecentsAnimationCanceled() { } + + private float getDisplacement(MotionEvent ev) { + if (mNavBarPosition.isRightEdge()) { + return ev.getX() - mDownPos.x; + } else if (mNavBarPosition.isLeftEdge()) { + return mDownPos.x - ev.getX(); + } else { + return ev.getY() - mDownPos.y; + } + } + + @Override + public boolean allowInterceptByParent() { + return !mPassedPilferInputSlop; + } +} diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/InputConsumer.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/InputConsumer.java index a1e5d47a53..f5cf654b15 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/InputConsumer.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/InputConsumer.java @@ -33,6 +33,7 @@ public interface InputConsumer { int TYPE_SCREEN_PINNED = 1 << 6; int TYPE_OVERVIEW_WITHOUT_FOCUS = 1 << 7; int TYPE_RESET_GESTURE = 1 << 8; + int TYPE_FALLBACK_NO_BUTTON = 1 << 9; String[] NAMES = new String[] { "TYPE_NO_OP", // 0 @@ -44,6 +45,7 @@ public interface InputConsumer { "TYPE_SCREEN_PINNED", // 6 "TYPE_OVERVIEW_WITHOUT_FOCUS", // 7 "TYPE_RESET_GESTURE", // 8 + "TYPE_FALLBACK_NO_BUTTON", // 9 }; InputConsumer NO_OP = () -> TYPE_NO_OP; diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java index 6bc543f075..4c137d3bf9 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java @@ -79,7 +79,7 @@ public class OtherActivityInputConsumer extends ContextWrapper implements InputC private static final String UP_EVT = "OtherActivityInputConsumer.UP"; // TODO: Move to quickstep contract - private static final float QUICKSTEP_TOUCH_SLOP_RATIO = 3; + public static final float QUICKSTEP_TOUCH_SLOP_RATIO = 3; private final CachedEventDispatcher mRecentsViewDispatcher = new CachedEventDispatcher(); private final RunningTaskInfo mRunningTask; diff --git a/quickstep/src/com/android/quickstep/OverviewComponentObserver.java b/quickstep/src/com/android/quickstep/OverviewComponentObserver.java index 0a73b8b195..0738affa93 100644 --- a/quickstep/src/com/android/quickstep/OverviewComponentObserver.java +++ b/quickstep/src/com/android/quickstep/OverviewComponentObserver.java @@ -33,7 +33,6 @@ import android.content.pm.ResolveInfo; import com.android.systemui.shared.system.PackageManagerWrapper; -import com.android.systemui.shared.system.QuickStepContract; import java.util.ArrayList; /** @@ -58,7 +57,9 @@ public final class OverviewComponentObserver { private String mUpdateRegisteredPackage; private ActivityControlHelper mActivityControlHelper; private Intent mOverviewIntent; + private Intent mHomeIntent; private int mSystemUiStateFlags; + private boolean mIsHomeAndOverviewSame; public OverviewComponentObserver(Context context) { mContext = context; @@ -93,11 +94,14 @@ public final class OverviewComponentObserver { final String overviewIntentCategory; ComponentName overviewComponent; + mHomeIntent = null; + if ((mSystemUiStateFlags & SYSUI_STATE_HOME_DISABLED) == 0 && (defaultHome == null || mMyHomeComponent.equals(defaultHome))) { // User default home is same as out home app. Use Overview integrated in Launcher. overviewComponent = mMyHomeComponent; mActivityControlHelper = new LauncherActivityControllerHelper(); + mIsHomeAndOverviewSame = true; overviewIntentCategory = Intent.CATEGORY_HOME; if (mUpdateRegisteredPackage != null) { @@ -109,8 +113,12 @@ public final class OverviewComponentObserver { // The default home app is a different launcher. Use the fallback Overview instead. overviewComponent = new ComponentName(mContext, RecentsActivity.class); mActivityControlHelper = new FallbackActivityControllerHelper(); + mIsHomeAndOverviewSame = false; overviewIntentCategory = Intent.CATEGORY_DEFAULT; + mHomeIntent = new Intent(Intent.ACTION_MAIN) + .addCategory(Intent.CATEGORY_HOME) + .setComponent(defaultHome); // User's default home app can change as a result of package updates of this app (such // as uninstalling the app or removing the "Launcher" feature in an update). // Listen for package updates of this app (and remove any previously attached @@ -135,6 +143,9 @@ public final class OverviewComponentObserver { .addCategory(overviewIntentCategory) .setComponent(overviewComponent) .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + if (mHomeIntent == null) { + mHomeIntent = mOverviewIntent; + } } /** @@ -158,6 +169,20 @@ public final class OverviewComponentObserver { return mOverviewIntent; } + /** + * Get the current intent for going to the home activity. + */ + public Intent getHomeIntent() { + return mHomeIntent; + } + + /** + * Returns true if home and overview are same activity. + */ + public boolean isHomeAndOverviewSame() { + return mIsHomeAndOverviewSame; + } + /** * Get the current activity control helper for managing interactions to the overview activity. * From 8ca7205c24356f35d3e72bdd0ca4b3e2db53925e Mon Sep 17 00:00:00 2001 From: Rajeev Kumar Date: Mon, 17 Jun 2019 11:35:03 -0700 Subject: [PATCH 32/41] Fix NPE in OverviewComponentObserver constructor while accessing activityInfo. Bug: 132461400 Test: Manual tests Change-Id: Icee3730454db8129e597714c147c2cbaddc70987 (cherry picked from commit 219208a0f53e8d5f613b9d197cf4072685dd9c0f) --- .../quickstep/TouchInteractionService.java | 55 +++++++++++++++---- 1 file changed, 45 insertions(+), 10 deletions(-) diff --git a/go/quickstep/src/com/android/quickstep/TouchInteractionService.java b/go/quickstep/src/com/android/quickstep/TouchInteractionService.java index 39f8448d1f..900b94e186 100644 --- a/go/quickstep/src/com/android/quickstep/TouchInteractionService.java +++ b/go/quickstep/src/com/android/quickstep/TouchInteractionService.java @@ -19,15 +19,21 @@ import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SYS import android.annotation.TargetApi; import android.app.Service; +import android.content.BroadcastReceiver; +import android.content.Context; import android.content.Intent; +import android.content.IntentFilter; import android.graphics.Region; import android.os.Build; import android.os.Bundle; import android.os.IBinder; +import android.os.Process; import android.os.RemoteException; import android.util.Log; import android.view.MotionEvent; +import com.android.launcher3.Utilities; +import com.android.launcher3.compat.UserManagerCompat; import com.android.systemui.shared.recents.IOverviewProxy; import com.android.systemui.shared.recents.ISystemUiProxy; @@ -37,7 +43,16 @@ import com.android.systemui.shared.recents.ISystemUiProxy; @TargetApi(Build.VERSION_CODES.O) public class TouchInteractionService extends Service { - private static final String TAG = "TouchInteractionService"; + private static final String TAG = "GoTouchInteractionService"; + private boolean mIsUserUnlocked; + private BroadcastReceiver mUserUnlockedReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + if (Intent.ACTION_USER_UNLOCKED.equals(intent.getAction())) { + initWhenUserUnlocked(); + } + } + }; private final IBinder mMyBinder = new IOverviewProxy.Stub() { @@ -53,17 +68,21 @@ public class TouchInteractionService extends Service { @Override public void onOverviewToggle() { - mOverviewCommandHelper.onOverviewToggle(); + if (mIsUserUnlocked) { + mOverviewCommandHelper.onOverviewToggle(); + } } @Override public void onOverviewShown(boolean triggeredFromAltTab) { - mOverviewCommandHelper.onOverviewShown(triggeredFromAltTab); + if (mIsUserUnlocked) { + mOverviewCommandHelper.onOverviewShown(triggeredFromAltTab); + } } @Override public void onOverviewHidden(boolean triggeredFromAltTab, boolean triggeredFromHomeKey) { - if (triggeredFromAltTab && !triggeredFromHomeKey) { + if (mIsUserUnlocked && triggeredFromAltTab && !triggeredFromHomeKey) { // onOverviewShownFromAltTab hides the overview and ends at the target app mOverviewCommandHelper.onOverviewHidden(); } @@ -71,7 +90,9 @@ public class TouchInteractionService extends Service { @Override public void onTip(int actionType, int viewType) { - mOverviewCommandHelper.onTip(actionType, viewType); + if (mIsUserUnlocked) { + mOverviewCommandHelper.onTip(actionType, viewType); + } } @Override @@ -123,17 +144,31 @@ public class TouchInteractionService extends Service { @Override public void onCreate() { super.onCreate(); - mRecentsModel = RecentsModel.INSTANCE.get(this); - mOverviewComponentObserver = new OverviewComponentObserver(this); - mOverviewCommandHelper = new OverviewCommandHelper(this, - mOverviewComponentObserver); + if (UserManagerCompat.getInstance(this).isUserUnlocked(Process.myUserHandle())) { + initWhenUserUnlocked(); + } else { + mIsUserUnlocked = false; + registerReceiver(mUserUnlockedReceiver, new IntentFilter(Intent.ACTION_USER_UNLOCKED)); + } sConnected = true; } + private void initWhenUserUnlocked() { + mRecentsModel = RecentsModel.INSTANCE.get(this); + mOverviewComponentObserver = new OverviewComponentObserver(this); + mOverviewCommandHelper = new OverviewCommandHelper(this, + mOverviewComponentObserver); + mIsUserUnlocked = true; + Utilities.unregisterReceiverSafely(this, mUserUnlockedReceiver); + } + @Override public void onDestroy() { - mOverviewComponentObserver.onDestroy(); + if (mIsUserUnlocked) { + mOverviewComponentObserver.onDestroy(); + } + Utilities.unregisterReceiverSafely(this, mUserUnlockedReceiver); sConnected = false; super.onDestroy(); } From df9fc63e8c0dd75af1bd1d50d090f9821fd62fc6 Mon Sep 17 00:00:00 2001 From: Winson Chung Date: Tue, 25 Jun 2019 14:44:05 -0700 Subject: [PATCH 33/41] Clean up some more refs to thumbnail data - Always return a copy of the task list to ensure that the model doesn't hold refs to the thumbnail data if it was loaded into the same task - Always clear the task thumbnail data ref once the visibility of the task view changes to be invisible Bug: 132309376 Test: Enter overview scroll to the end of the list, relaunch app and take heap dump Change-Id: I4437fd30172a5fe2a78c111f780163a1e6bbbb54 --- .../src/com/android/quickstep/views/TaskView.java | 6 +++--- .../src/com/android/quickstep/RecentTasksList.java | 12 +++++++++++- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskView.java index e7e41893cf..b26fdce92f 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskView.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskView.java @@ -368,6 +368,9 @@ public class TaskView extends FrameLayout implements PageCallbacks, Reusable { } else { mSnapshotView.setThumbnail(null, null); setIcon(null); + // Reset the task thumbnail reference as well (it will be fetched from the cache or + // reloaded next time we need it) + mTask.thumbnail = null; } } @@ -488,9 +491,6 @@ public class TaskView extends FrameLayout implements PageCallbacks, Reusable { mSnapshotView.setThumbnail(mTask, null); setOverlayEnabled(false); onTaskListVisibilityChanged(false); - if (mTask != null) { - mTask.thumbnail = null; - } } @Override diff --git a/quickstep/src/com/android/quickstep/RecentTasksList.java b/quickstep/src/com/android/quickstep/RecentTasksList.java index 3538373122..f27ba85388 100644 --- a/quickstep/src/com/android/quickstep/RecentTasksList.java +++ b/quickstep/src/com/android/quickstep/RecentTasksList.java @@ -84,7 +84,7 @@ public class RecentTasksList extends TaskStackChangeListener { final int requestLoadId = mChangeId; Runnable resultCallback = callback == null ? () -> { } - : () -> callback.accept(mTasks); + : () -> callback.accept(copyOf(mTasks)); if (mLastLoadedId == mChangeId && (!mLastLoadHadKeysOnly || loadKeysOnly)) { // The list is up to date, callback with the same list @@ -183,4 +183,14 @@ public class RecentTasksList extends TaskStackChangeListener { return allTasks; } + + private ArrayList copyOf(ArrayList tasks) { + ArrayList newTasks = new ArrayList<>(); + for (int i = 0; i < tasks.size(); i++) { + Task t = tasks.get(i); + newTasks.add(new Task(t.key, t.colorPrimary, t.colorBackground, t.isDockable, + t.isLocked, t.taskDescription, t.topActivity)); + } + return newTasks; + } } \ No newline at end of file From 26cfff41bad35e6a4f770dcd45634551a1585ef3 Mon Sep 17 00:00:00 2001 From: vadimt Date: Tue, 25 Jun 2019 16:13:42 -0700 Subject: [PATCH 34/41] Taking screenshots for local tests for FallbackRecentsTest Change-Id: I604e7e90f2146069827b520d44db1854edc7aa77 --- .../android/quickstep/FallbackRecentsTest.java | 17 +++++++++++++---- .../launcher3/ui/AbstractLauncherUiTest.java | 2 +- .../launcher3/util/rule/FailureWatcher.java | 16 ++++++++-------- 3 files changed, 22 insertions(+), 13 deletions(-) diff --git a/quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java b/quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java index 013591171c..e5f949b880 100644 --- a/quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java +++ b/quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java @@ -43,10 +43,12 @@ import androidx.test.uiautomator.Until; import com.android.launcher3.tapl.LauncherInstrumentation; import com.android.launcher3.testcomponent.TestCommandReceiver; +import com.android.launcher3.util.rule.FailureWatcher; import com.android.quickstep.NavigationModeSwitchRule.NavigationModeSwitch; import org.junit.Rule; import org.junit.Test; +import org.junit.rules.RuleChain; import org.junit.rules.TestRule; import org.junit.runner.RunWith; import org.junit.runners.model.Statement; @@ -62,10 +64,14 @@ public class FallbackRecentsTest { private final LauncherInstrumentation mLauncher; private final ActivityInfo mOtherLauncherActivity; - @Rule public final TestRule mDisableHeadsUpNotification = disableHeadsUpNotification(); - @Rule public final TestRule mQuickstepOnOffExecutor; + @Rule + public final TestRule mDisableHeadsUpNotification = disableHeadsUpNotification(); - @Rule public final TestRule mSetLauncherCommand; + @Rule + public final TestRule mSetLauncherCommand; + + @Rule + public final TestRule mOrderSensitiveRules; public FallbackRecentsTest() throws RemoteException { Instrumentation instrumentation = getInstrumentation(); @@ -74,7 +80,10 @@ public class FallbackRecentsTest { mDevice.setOrientationNatural(); mLauncher = new LauncherInstrumentation(instrumentation); - mQuickstepOnOffExecutor = new NavigationModeSwitchRule(mLauncher); + mOrderSensitiveRules = RuleChain. + outerRule(new NavigationModeSwitchRule(mLauncher)). + around(new FailureWatcher(mDevice)); + mOtherLauncherActivity = context.getPackageManager().queryIntentActivities( getHomeIntentInPackage(context), MATCH_DISABLED_COMPONENTS).get(0).activityInfo; diff --git a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java index e663e70305..4a0ca5c245 100644 --- a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java +++ b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java @@ -125,7 +125,7 @@ public abstract class AbstractLauncherUiTest { protected TestRule getRulesInsideActivityMonitor() { return RuleChain. outerRule(new PortraitLandscapeRunner(this)). - around(new FailureWatcher(this)); + around(new FailureWatcher(mDevice)); } @Rule diff --git a/tests/src/com/android/launcher3/util/rule/FailureWatcher.java b/tests/src/com/android/launcher3/util/rule/FailureWatcher.java index 09cc98de08..eef2f24baa 100644 --- a/tests/src/com/android/launcher3/util/rule/FailureWatcher.java +++ b/tests/src/com/android/launcher3/util/rule/FailureWatcher.java @@ -4,7 +4,7 @@ import static androidx.test.InstrumentationRegistry.getInstrumentation; import android.util.Log; -import com.android.launcher3.ui.AbstractLauncherUiTest; +import androidx.test.uiautomator.UiDevice; import org.junit.rules.TestWatcher; import org.junit.runner.Description; @@ -16,16 +16,16 @@ import java.io.IOException; public class FailureWatcher extends TestWatcher { private static final String TAG = "FailureWatcher"; private static int sScreenshotCount = 0; - private AbstractLauncherUiTest mAbstractLauncherUiTest; + final private UiDevice mDevice; - public FailureWatcher(AbstractLauncherUiTest abstractLauncherUiTest) { - mAbstractLauncherUiTest = abstractLauncherUiTest; + public FailureWatcher(UiDevice device) { + mDevice = device; } private void dumpViewHierarchy() { final ByteArrayOutputStream stream = new ByteArrayOutputStream(); try { - mAbstractLauncherUiTest.getDevice().dumpWindowHierarchy(stream); + mDevice.dumpWindowHierarchy(stream); stream.flush(); stream.close(); for (String line : stream.toString().split("\\r?\\n")) { @@ -38,7 +38,7 @@ public class FailureWatcher extends TestWatcher { @Override protected void failed(Throwable e, Description description) { - if (mAbstractLauncherUiTest.getDevice() == null) return; + if (mDevice == null) return; final String pathname = getInstrumentation().getTargetContext(). getFilesDir().getPath() + "/TaplTestScreenshot" + sScreenshotCount++ + ".png"; Log.e(TAG, "Failed test " + description.getMethodName() + @@ -48,12 +48,12 @@ public class FailureWatcher extends TestWatcher { dumpViewHierarchy(); try { - final String dumpsysResult = mAbstractLauncherUiTest.getDevice().executeShellCommand( + final String dumpsysResult = mDevice.executeShellCommand( "dumpsys activity service TouchInteractionService"); Log.d(TAG, "TouchInteractionService: " + dumpsysResult); } catch (IOException ex) { } - mAbstractLauncherUiTest.getDevice().takeScreenshot(new File(pathname)); + mDevice.takeScreenshot(new File(pathname)); } } From 3a6eedc61b6d1701ac4aee4e46a4d9ed361406c7 Mon Sep 17 00:00:00 2001 From: vadimt Date: Tue, 25 Jun 2019 18:34:48 -0700 Subject: [PATCH 35/41] Converting some widget tests to TAPL Bug: 115582915 Change-Id: I07c6ca5ef712cf1a85d81e7d4b4bda3604777ac8 --- .../launcher3/ui/widget/BindWidgetTest.java | 47 +++++++------------ .../ui/widget/RequestPinItemTest.java | 41 +++++----------- .../tapl/LauncherInstrumentation.java | 32 ++++++++----- .../com/android/launcher3/tapl/Widget.java | 24 ++++++++++ .../android/launcher3/tapl/WidgetCell.java | 28 +++++++++++ .../com/android/launcher3/tapl/Workspace.java | 16 +++++++ 6 files changed, 118 insertions(+), 70 deletions(-) create mode 100644 tests/tapl/com/android/launcher3/tapl/Widget.java create mode 100644 tests/tapl/com/android/launcher3/tapl/WidgetCell.java diff --git a/tests/src/com/android/launcher3/ui/widget/BindWidgetTest.java b/tests/src/com/android/launcher3/ui/widget/BindWidgetTest.java index 874ff1995b..d36126bb17 100644 --- a/tests/src/com/android/launcher3/ui/widget/BindWidgetTest.java +++ b/tests/src/com/android/launcher3/ui/widget/BindWidgetTest.java @@ -15,8 +15,9 @@ */ package com.android.launcher3.ui.widget; +import static com.android.launcher3.WorkspaceLayoutManager.FIRST_SCREEN_ID; + import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; @@ -24,43 +25,37 @@ import android.appwidget.AppWidgetHost; import android.appwidget.AppWidgetManager; import android.content.ComponentName; import android.content.ContentResolver; -import android.content.ContentValues; import android.content.pm.PackageInstaller; import android.content.pm.PackageInstaller.SessionParams; import android.content.pm.PackageManager; import android.database.Cursor; import android.os.Bundle; +import androidx.test.filters.LargeTest; +import androidx.test.runner.AndroidJUnit4; + import com.android.launcher3.LauncherAppWidgetHost; import com.android.launcher3.LauncherAppWidgetInfo; import com.android.launcher3.LauncherAppWidgetProviderInfo; import com.android.launcher3.LauncherSettings; -import com.android.launcher3.Workspace; import com.android.launcher3.compat.AppWidgetManagerCompat; import com.android.launcher3.compat.PackageInstallerCompat; +import com.android.launcher3.tapl.Workspace; import com.android.launcher3.ui.AbstractLauncherUiTest; import com.android.launcher3.ui.TestViewHelpers; import com.android.launcher3.util.ContentWriter; import com.android.launcher3.util.rule.ShellCommandRule; -import com.android.launcher3.widget.LauncherAppWidgetHostView; import com.android.launcher3.widget.PendingAddWidgetInfo; -import com.android.launcher3.widget.PendingAppWidgetHostView; import com.android.launcher3.widget.WidgetHostViewLoader; import org.junit.After; import org.junit.Before; -import org.junit.Ignore; import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import java.util.Set; -import androidx.test.filters.LargeTest; -import androidx.test.runner.AndroidJUnit4; -import androidx.test.uiautomator.UiSelector; -import java.util.concurrent.Callable; - /** * Tests for bind widget flow. * @@ -131,16 +126,14 @@ public class BindWidgetTest extends AbstractLauncherUiTest { setupContents(item); - // Since there is no widget to verify, just wait until the workspace is ready. - // TODO: fix LauncherInstrumentation#LAUNCHER_PKG - mLauncher.getWorkspace(); + final Workspace workspace = mLauncher.getWorkspace(); // Item deleted from db mCursor = mResolver.query(LauncherSettings.Favorites.getContentUri(item.id), null, null, null, null, null); assertEquals(0, mCursor.getCount()); // The view does not exist - assertFalse(mDevice.findObject(new UiSelector().description(info.label)).exists()); + assertTrue("Widget exists", workspace.tryGetWidget(info.label, 0) == null); } @Test @@ -189,12 +182,8 @@ public class BindWidgetTest extends AbstractLauncherUiTest { setupContents(item); - // Since there is no widget to verify, just wait until the workspace is ready. - // TODO: fix LauncherInstrumentation#LAUNCHER_PKG - mLauncher.getWorkspace(); - // The view does not exist - assertFalse(mDevice.findObject( - new UiSelector().className(PendingAppWidgetHostView.class)).exists()); + assertTrue("Pending widget exists", + mLauncher.getWorkspace().tryGetPendingWidget(0) == null); // Item deleted from db mCursor = mResolver.query(LauncherSettings.Favorites.getContentUri(item.id), null, null, null, null, null); @@ -258,12 +247,12 @@ public class BindWidgetTest extends AbstractLauncherUiTest { * widget class is displayed on the homescreen. */ private void setupContents(LauncherAppWidgetInfo item) { - int screenId = Workspace.FIRST_SCREEN_ID; + int screenId = FIRST_SCREEN_ID; // Update the screen id counter for the provider. LauncherSettings.Settings.call(mResolver, LauncherSettings.Settings.METHOD_NEW_SCREEN_ID); - if (screenId > Workspace.FIRST_SCREEN_ID) { - screenId = Workspace.FIRST_SCREEN_ID; + if (screenId > FIRST_SCREEN_ID) { + screenId = FIRST_SCREEN_ID; } // Insert the item @@ -283,15 +272,13 @@ public class BindWidgetTest extends AbstractLauncherUiTest { } private void verifyWidgetPresent(LauncherAppWidgetProviderInfo info) { - UiSelector selector = new UiSelector().packageName(mTargetContext.getPackageName()) - .className(LauncherAppWidgetHostView.class).description(info.label); - assertTrue(mDevice.findObject(selector).waitForExists(DEFAULT_UI_TIMEOUT)); + assertTrue("Widget is not present", + mLauncher.getWorkspace().tryGetWidget(info.label, DEFAULT_UI_TIMEOUT) != null); } private void verifyPendingWidgetPresent() { - UiSelector selector = new UiSelector().packageName(mTargetContext.getPackageName()) - .className(PendingAppWidgetHostView.class); - assertTrue(mDevice.findObject(selector).waitForExists(DEFAULT_UI_TIMEOUT)); + assertTrue("Pending widget is not present", + mLauncher.getWorkspace().tryGetPendingWidget(DEFAULT_UI_TIMEOUT) != null); } /** diff --git a/tests/src/com/android/launcher3/ui/widget/RequestPinItemTest.java b/tests/src/com/android/launcher3/ui/widget/RequestPinItemTest.java index 2766a3e3bd..edec914194 100644 --- a/tests/src/com/android/launcher3/ui/widget/RequestPinItemTest.java +++ b/tests/src/com/android/launcher3/ui/widget/RequestPinItemTest.java @@ -29,7 +29,6 @@ import android.view.View; import androidx.test.filters.LargeTest; import androidx.test.runner.AndroidJUnit4; import androidx.test.uiautomator.By; -import androidx.test.uiautomator.UiObject2; import androidx.test.uiautomator.Until; import com.android.launcher3.ItemInfo; @@ -81,15 +80,10 @@ public class RequestPinItemTest extends AbstractLauncherUiTest { @Test public void testPinWidgetNoConfig() throws Throwable { - runTest("pinWidgetNoConfig", true, new ItemOperator() { - @Override - public boolean evaluate(ItemInfo info, View view) { - return info instanceof LauncherAppWidgetInfo && - ((LauncherAppWidgetInfo) info).appWidgetId == mAppWidgetId && - ((LauncherAppWidgetInfo) info).providerName.getClassName() - .equals(AppWidgetNoConfig.class.getName()); - } - }); + runTest("pinWidgetNoConfig", true, (info, view) -> info instanceof LauncherAppWidgetInfo && + ((LauncherAppWidgetInfo) info).appWidgetId == mAppWidgetId && + ((LauncherAppWidgetInfo) info).providerName.getClassName() + .equals(AppWidgetNoConfig.class.getName())); } @Test @@ -99,28 +93,19 @@ public class RequestPinItemTest extends AbstractLauncherUiTest { RequestPinItemActivity.class, "setRemoteViewColor").putExtra( RequestPinItemActivity.EXTRA_PARAM + "0", Color.RED); - runTest("pinWidgetNoConfig", true, new ItemOperator() { - @Override - public boolean evaluate(ItemInfo info, View view) { - return info instanceof LauncherAppWidgetInfo && - ((LauncherAppWidgetInfo) info).appWidgetId == mAppWidgetId && - ((LauncherAppWidgetInfo) info).providerName.getClassName() - .equals(AppWidgetNoConfig.class.getName()); - } - }, command); + runTest("pinWidgetNoConfig", true, (info, view) -> info instanceof LauncherAppWidgetInfo && + ((LauncherAppWidgetInfo) info).appWidgetId == mAppWidgetId && + ((LauncherAppWidgetInfo) info).providerName.getClassName() + .equals(AppWidgetNoConfig.class.getName()), command); } @Test public void testPinWidgetWithConfig() throws Throwable { - runTest("pinWidgetWithConfig", true, new ItemOperator() { - @Override - public boolean evaluate(ItemInfo info, View view) { - return info instanceof LauncherAppWidgetInfo && + runTest("pinWidgetWithConfig", true, + (info, view) -> info instanceof LauncherAppWidgetInfo && ((LauncherAppWidgetInfo) info).appWidgetId == mAppWidgetId && ((LauncherAppWidgetInfo) info).providerName.getClassName() - .equals(AppWidgetWithConfig.class.getName()); - } - }); + .equals(AppWidgetWithConfig.class.getName())); } @Test @@ -174,9 +159,7 @@ public class RequestPinItemTest extends AbstractLauncherUiTest { // call the requested method to start the flow mTargetContext.sendBroadcast(RequestPinItemActivity.getCommandIntent( RequestPinItemActivity.class, activityMethod)); - UiObject2 widgetCell = mDevice.wait( - Until.findObject(By.clazz(WidgetCell.class)), DEFAULT_ACTIVITY_TIMEOUT); - assertNotNull(widgetCell); + mLauncher.getWidgetCell(); // Accept confirmation: BlockingBroadcastReceiver resultReceiver = new BlockingBroadcastReceiver(mCallbackAction); diff --git a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java index 7171bf9ac1..671e8fdf92 100644 --- a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java +++ b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java @@ -502,6 +502,13 @@ public final class LauncherInstrumentation { } } + @NonNull + public WidgetCell getWidgetCell() { + try (LauncherInstrumentation.Closable c = addContextLayer("want to get widget cell")) { + return new WidgetCell(this); + } + } + /** * Gets the Overview object if the current state is showing the overview panel. Fails if the * launcher is not in that state. @@ -515,17 +522,6 @@ public final class LauncherInstrumentation { } } - /** - * Gets the Base overview object if either Launcher is in overview state or the fallback - * overview activity is showing. Fails otherwise. - * - * @return BaseOverview object. - */ - @NonNull - public BaseOverview getBaseOverview() { - return new BaseOverview(this); - } - /** * Gets the All Apps object if the current state is showing the all apps panel opened by swiping * from workspace. Fails if the launcher is not in that state. Please don't call this method if @@ -616,6 +612,16 @@ public final class LauncherInstrumentation { return waitForObjectBySelector(getLauncherObjectSelector(resName)); } + @NonNull + UiObject2 waitForLauncherObject(BySelector selector) { + return waitForObjectBySelector(selector.pkg(getLauncherPackageName())); + } + + @NonNull + UiObject2 tryWaitForLauncherObject(BySelector selector, long timeout) { + return tryWaitForObjectBySelector(selector.pkg(getLauncherPackageName()), timeout); + } + @NonNull UiObject2 waitForFallbackLauncherObject(String resName) { return waitForObjectBySelector(getFallbackLauncherObjectSelector(resName)); @@ -627,6 +633,10 @@ public final class LauncherInstrumentation { return object; } + private UiObject2 tryWaitForObjectBySelector(BySelector selector, long timeout) { + return mDevice.wait(Until.findObject(selector), timeout); + } + BySelector getLauncherObjectSelector(String resName) { return By.res(getLauncherPackageName(), resName); } diff --git a/tests/tapl/com/android/launcher3/tapl/Widget.java b/tests/tapl/com/android/launcher3/tapl/Widget.java new file mode 100644 index 0000000000..128789dbc1 --- /dev/null +++ b/tests/tapl/com/android/launcher3/tapl/Widget.java @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.launcher3.tapl; + +import androidx.test.uiautomator.UiObject2; + +public class Widget { + Widget(LauncherInstrumentation launcher, UiObject2 widget) { + } +} diff --git a/tests/tapl/com/android/launcher3/tapl/WidgetCell.java b/tests/tapl/com/android/launcher3/tapl/WidgetCell.java new file mode 100644 index 0000000000..adb69ec5be --- /dev/null +++ b/tests/tapl/com/android/launcher3/tapl/WidgetCell.java @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.launcher3.tapl; + +import androidx.test.uiautomator.By; +import androidx.test.uiautomator.UiObject2; + +public class WidgetCell { + WidgetCell(LauncherInstrumentation launcher) { + final UiObject2 widgetCell = launcher.waitForLauncherObject(By.clazz( + "com.android.launcher3.widget.WidgetCell")); + launcher.assertNotNull("Can't find widget cell object", widgetCell); + } +} diff --git a/tests/tapl/com/android/launcher3/tapl/Workspace.java b/tests/tapl/com/android/launcher3/tapl/Workspace.java index 62050b9057..fc0a79329f 100644 --- a/tests/tapl/com/android/launcher3/tapl/Workspace.java +++ b/tests/tapl/com/android/launcher3/tapl/Workspace.java @@ -27,6 +27,7 @@ import android.view.MotionEvent; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import androidx.test.uiautomator.By; import androidx.test.uiautomator.Direction; import androidx.test.uiautomator.UiObject2; @@ -215,4 +216,19 @@ public final class Workspace extends Home { protected int getSwipeStartY() { return mLauncher.getRealDisplaySize().y - 1; } + + @Nullable + public Widget tryGetWidget(String label, long timeout) { + final UiObject2 widget = mLauncher.tryWaitForLauncherObject( + By.clazz("com.android.launcher3.widget.LauncherAppWidgetHostView").desc(label), + timeout); + return widget != null ? new Widget(mLauncher, widget) : null; + } + + @Nullable + public Widget tryGetPendingWidget(long timeout) { + final UiObject2 widget = mLauncher.tryWaitForLauncherObject( + By.clazz("com.android.launcher3.widget.PendingAppWidgetHostView"), timeout); + return widget != null ? new Widget(mLauncher, widget) : null; + } } \ No newline at end of file From 2d4b05f295449107906ed1644646c3489154ebfa Mon Sep 17 00:00:00 2001 From: vadimt Date: Tue, 25 Jun 2019 19:57:13 -0700 Subject: [PATCH 36/41] Converting more tests to TAPL Bug: 131116002 Bug: 115582915 Change-Id: I3346f96ddba3224eb4b92b15344d71b8ed6e32e0 --- .../ui/DefaultLayoutProviderTest.java | 29 ++++--------- .../ui/widget/RequestPinItemTest.java | 10 ++--- .../launcher3/tapl/AddToHomeScreenPrompt.java | 41 +++++++++++++++++++ .../tapl/{WidgetCell.java => Folder.java} | 17 +++++--- .../tapl/LauncherInstrumentation.java | 4 +- .../com/android/launcher3/tapl/Workspace.java | 8 +++- 6 files changed, 73 insertions(+), 36 deletions(-) create mode 100644 tests/tapl/com/android/launcher3/tapl/AddToHomeScreenPrompt.java rename tests/tapl/com/android/launcher3/tapl/{WidgetCell.java => Folder.java} (64%) diff --git a/tests/src/com/android/launcher3/ui/DefaultLayoutProviderTest.java b/tests/src/com/android/launcher3/ui/DefaultLayoutProviderTest.java index 48335a602c..58c74cef16 100644 --- a/tests/src/com/android/launcher3/ui/DefaultLayoutProviderTest.java +++ b/tests/src/com/android/launcher3/ui/DefaultLayoutProviderTest.java @@ -25,26 +25,23 @@ import android.net.Uri; import android.os.ParcelFileDescriptor; import android.os.ParcelFileDescriptor.AutoCloseOutputStream; +import androidx.test.InstrumentationRegistry; +import androidx.test.filters.MediumTest; +import androidx.test.runner.AndroidJUnit4; + import com.android.launcher3.LauncherAppWidgetProviderInfo; import com.android.launcher3.testcomponent.TestCommandReceiver; import com.android.launcher3.util.LauncherLayoutBuilder; import com.android.launcher3.util.rule.ShellCommandRule; -import com.android.launcher3.widget.LauncherAppWidgetHostView; import org.junit.After; import org.junit.Before; -import org.junit.Ignore; import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import java.io.OutputStreamWriter; -import androidx.test.InstrumentationRegistry; -import androidx.test.filters.MediumTest; -import androidx.test.runner.AndroidJUnit4; -import androidx.test.uiautomator.UiSelector; - @MediumTest @RunWith(AndroidJUnit4.class) public class DefaultLayoutProviderTest extends AbstractLauncherUiTest { @@ -71,7 +68,6 @@ public class DefaultLayoutProviderTest extends AbstractLauncherUiTest { } @Test - // Convert test to TAPL; b/131116002 public void testCustomProfileLoaded_with_icon_on_hotseat() throws Exception { writeLayout(new LauncherLayoutBuilder().atHotseat(0).putApp(SETTINGS_APP, SETTINGS_APP)); @@ -79,14 +75,10 @@ public class DefaultLayoutProviderTest extends AbstractLauncherUiTest { mActivityMonitor.startLauncher(); waitForModelLoaded(); - // Verify widget present - UiSelector selector = new UiSelector().packageName(mTargetContext.getPackageName()) - .description(getSettingsApp().getLabel().toString()); - assertTrue(mDevice.findObject(selector).waitForExists(DEFAULT_UI_TIMEOUT)); + mLauncher.getWorkspace().getHotseatAppIcon(getSettingsApp().getLabel().toString()); } @Test - // Convert test to TAPL; b/131116002 public void testCustomProfileLoaded_with_widget() throws Exception { // A non-restored widget with no config screen gets restored automatically. LauncherAppWidgetProviderInfo info = TestViewHelpers.findWidgetProvider(this, false); @@ -100,13 +92,11 @@ public class DefaultLayoutProviderTest extends AbstractLauncherUiTest { waitForModelLoaded(); // Verify widget present - UiSelector selector = new UiSelector().packageName(mTargetContext.getPackageName()) - .className(LauncherAppWidgetHostView.class).description(info.label); - assertTrue(mDevice.findObject(selector).waitForExists(DEFAULT_UI_TIMEOUT)); + assertTrue("Widget is not present", + mLauncher.getWorkspace().tryGetWidget(info.label, DEFAULT_UI_TIMEOUT) != null); } @Test - // Convert test to TAPL; b/131116002 public void testCustomProfileLoaded_with_folder() throws Exception { writeLayout(new LauncherLayoutBuilder().atHotseat(0).putFolder(android.R.string.copy) .addApp(SETTINGS_APP, SETTINGS_APP) @@ -118,10 +108,7 @@ public class DefaultLayoutProviderTest extends AbstractLauncherUiTest { mActivityMonitor.startLauncher(); waitForModelLoaded(); - // Verify widget present - UiSelector selector = new UiSelector().packageName(mTargetContext.getPackageName()) - .descriptionContains(mTargetContext.getString(android.R.string.copy)); - assertTrue(mDevice.findObject(selector).waitForExists(DEFAULT_UI_TIMEOUT)); + mLauncher.getWorkspace().getHotseatFolder("Folder: Copy"); } @After diff --git a/tests/src/com/android/launcher3/ui/widget/RequestPinItemTest.java b/tests/src/com/android/launcher3/ui/widget/RequestPinItemTest.java index edec914194..6122daec2c 100644 --- a/tests/src/com/android/launcher3/ui/widget/RequestPinItemTest.java +++ b/tests/src/com/android/launcher3/ui/widget/RequestPinItemTest.java @@ -28,8 +28,6 @@ import android.view.View; import androidx.test.filters.LargeTest; import androidx.test.runner.AndroidJUnit4; -import androidx.test.uiautomator.By; -import androidx.test.uiautomator.Until; import com.android.launcher3.ItemInfo; import com.android.launcher3.LauncherAppWidgetInfo; @@ -38,6 +36,7 @@ import com.android.launcher3.Utilities; import com.android.launcher3.Workspace.ItemOperator; import com.android.launcher3.WorkspaceItemInfo; import com.android.launcher3.shortcuts.ShortcutKey; +import com.android.launcher3.tapl.AddToHomeScreenPrompt; import com.android.launcher3.testcomponent.AppWidgetNoConfig; import com.android.launcher3.testcomponent.AppWidgetWithConfig; import com.android.launcher3.testcomponent.RequestPinItemActivity; @@ -45,7 +44,6 @@ import com.android.launcher3.ui.AbstractLauncherUiTest; import com.android.launcher3.util.Condition; import com.android.launcher3.util.Wait; import com.android.launcher3.util.rule.ShellCommandRule; -import com.android.launcher3.widget.WidgetCell; import org.junit.Before; import org.junit.Rule; @@ -159,13 +157,11 @@ public class RequestPinItemTest extends AbstractLauncherUiTest { // call the requested method to start the flow mTargetContext.sendBroadcast(RequestPinItemActivity.getCommandIntent( RequestPinItemActivity.class, activityMethod)); - mLauncher.getWidgetCell(); + final AddToHomeScreenPrompt addToHomeScreenPrompt = mLauncher.getAddToHomeScreenPrompt(); // Accept confirmation: BlockingBroadcastReceiver resultReceiver = new BlockingBroadcastReceiver(mCallbackAction); - mDevice.wait(Until.findObject( - By.text(mLauncher.isAvd() ? "ADD AUTOMATICALLY" : "Add automatically")), - DEFAULT_UI_TIMEOUT).click(); + addToHomeScreenPrompt.addAutomatically(); Intent result = resultReceiver.blockingGetIntent(); assertNotNull(result); mAppWidgetId = result.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, -1); diff --git a/tests/tapl/com/android/launcher3/tapl/AddToHomeScreenPrompt.java b/tests/tapl/com/android/launcher3/tapl/AddToHomeScreenPrompt.java new file mode 100644 index 0000000000..7f561a2af3 --- /dev/null +++ b/tests/tapl/com/android/launcher3/tapl/AddToHomeScreenPrompt.java @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.launcher3.tapl; + +import androidx.test.uiautomator.By; +import androidx.test.uiautomator.UiObject2; + +public class AddToHomeScreenPrompt { + private final LauncherInstrumentation mLauncher; + private final UiObject2 mWidgetCell; + + AddToHomeScreenPrompt(LauncherInstrumentation launcher) { + mLauncher = launcher; + mWidgetCell = launcher.waitForLauncherObject(By.clazz( + "com.android.launcher3.widget.WidgetCell")); + mLauncher.assertNotNull("Can't find widget cell object", mWidgetCell); + } + + public void addAutomatically() { + mLauncher.waitForObjectInContainer( + mWidgetCell.getParent().getParent().getParent().getParent(), + By.text(LauncherInstrumentation.isAvd() + ? "ADD AUTOMATICALLY" + : "Add automatically")). + click(); + } +} diff --git a/tests/tapl/com/android/launcher3/tapl/WidgetCell.java b/tests/tapl/com/android/launcher3/tapl/Folder.java similarity index 64% rename from tests/tapl/com/android/launcher3/tapl/WidgetCell.java rename to tests/tapl/com/android/launcher3/tapl/Folder.java index adb69ec5be..6e6734d81d 100644 --- a/tests/tapl/com/android/launcher3/tapl/WidgetCell.java +++ b/tests/tapl/com/android/launcher3/tapl/Folder.java @@ -16,13 +16,20 @@ package com.android.launcher3.tapl; +import android.widget.FrameLayout; + import androidx.test.uiautomator.By; +import androidx.test.uiautomator.BySelector; import androidx.test.uiautomator.UiObject2; -public class WidgetCell { - WidgetCell(LauncherInstrumentation launcher) { - final UiObject2 widgetCell = launcher.waitForLauncherObject(By.clazz( - "com.android.launcher3.widget.WidgetCell")); - launcher.assertNotNull("Can't find widget cell object", widgetCell); +/** + * App folder in workspace/ + */ +public final class Folder { + Folder(LauncherInstrumentation launcher, UiObject2 icon) { + } + + static BySelector getSelector(String folderName, LauncherInstrumentation launcher) { + return By.clazz(FrameLayout.class).desc(folderName).pkg(launcher.getLauncherPackageName()); } } diff --git a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java index 671e8fdf92..a7e6336199 100644 --- a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java +++ b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java @@ -503,9 +503,9 @@ public final class LauncherInstrumentation { } @NonNull - public WidgetCell getWidgetCell() { + public AddToHomeScreenPrompt getAddToHomeScreenPrompt() { try (LauncherInstrumentation.Closable c = addContextLayer("want to get widget cell")) { - return new WidgetCell(this); + return new AddToHomeScreenPrompt(this); } } diff --git a/tests/tapl/com/android/launcher3/tapl/Workspace.java b/tests/tapl/com/android/launcher3/tapl/Workspace.java index fc0a79329f..b01b6f363c 100644 --- a/tests/tapl/com/android/launcher3/tapl/Workspace.java +++ b/tests/tapl/com/android/launcher3/tapl/Workspace.java @@ -143,11 +143,17 @@ public final class Workspace extends Home { } @NonNull - private AppIcon getHotseatAppIcon(String appName) { + public AppIcon getHotseatAppIcon(String appName) { return new AppIcon(mLauncher, mLauncher.getObjectInContainer( mHotseat, AppIcon.getAppIconSelector(appName, mLauncher))); } + @NonNull + public Folder getHotseatFolder(String appName) { + return new Folder(mLauncher, mLauncher.getObjectInContainer( + mHotseat, Folder.getSelector(appName, mLauncher))); + } + static void dragIconToWorkspace( LauncherInstrumentation launcher, Launchable launchable, Point dest, String longPressIndicator) { From 406d49bdd724a9707a218f6b7ba3d40bdfb87028 Mon Sep 17 00:00:00 2001 From: vadimt Date: Wed, 26 Jun 2019 13:40:42 -0700 Subject: [PATCH 37/41] Converting even more tests to TAPL Bug: 131116002 Bug: 115582915 Change-Id: I08587e7a50420e57970ed8feec4524111145dc31 --- .../launcher3/ui/AbstractLauncherUiTest.java | 31 ----- .../launcher3/ui/TaplTestsLauncher3.java | 19 +-- .../android/launcher3/ui/TestViewHelpers.java | 114 ------------------ .../ui/widget/AddConfigWidgetTest.java | 16 +-- .../launcher3/ui/widget/AddWidgetTest.java | 32 ++--- .../android/launcher3/tapl/Launchable.java | 6 +- .../tapl/LauncherInstrumentation.java | 4 +- .../com/android/launcher3/tapl/Widget.java | 13 +- .../com/android/launcher3/tapl/Widgets.java | 30 +++++ 9 files changed, 63 insertions(+), 202 deletions(-) diff --git a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java index 4a0ca5c245..abaad20b2a 100644 --- a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java +++ b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java @@ -20,7 +20,6 @@ import static androidx.test.InstrumentationRegistry.getInstrumentation; import static com.android.launcher3.ui.TaplTestsLauncher3.getAppPackageName; import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; import static java.lang.System.exit; @@ -38,10 +37,7 @@ import android.util.Log; import androidx.test.InstrumentationRegistry; import androidx.test.uiautomator.By; -import androidx.test.uiautomator.BySelector; -import androidx.test.uiautomator.Direction; import androidx.test.uiautomator.UiDevice; -import androidx.test.uiautomator.UiObject2; import androidx.test.uiautomator.Until; import com.android.launcher3.Launcher; @@ -50,7 +46,6 @@ import com.android.launcher3.LauncherModel; import com.android.launcher3.LauncherSettings; import com.android.launcher3.LauncherState; import com.android.launcher3.MainThreadExecutor; -import com.android.launcher3.ResourceUtils; import com.android.launcher3.Utilities; import com.android.launcher3.compat.LauncherAppsCompat; import com.android.launcher3.model.AppLaunchTracker; @@ -86,7 +81,6 @@ public abstract class AbstractLauncherUiTest { public static final long DEFAULT_ACTIVITY_TIMEOUT = TimeUnit.SECONDS.toMillis(10); public static final long DEFAULT_BROADCAST_TIMEOUT_SECS = 5; - public static final long SHORT_UI_TIMEOUT = 300; public static final long DEFAULT_UI_TIMEOUT = 10000; private static final String TAG = "AbstractLauncherUiTest"; @@ -180,31 +174,6 @@ public abstract class AbstractLauncherUiTest { } } - /** - * Scrolls the {@param container} until it finds an object matching {@param condition}. - * - * @return the matching object. - */ - protected UiObject2 scrollAndFind(UiObject2 container, BySelector condition) { - final int margin = ResourceUtils.getNavbarSize( - ResourceUtils.NAVBAR_BOTTOM_GESTURE_SIZE, mLauncher.getResources()) + 1; - container.setGestureMargins(0, 0, 0, margin); - - int i = 0; - for (; ; ) { - // findObject can only execute after spring settles. - mDevice.wait(Until.findObject(condition), SHORT_UI_TIMEOUT); - UiObject2 widget = container.findObject(condition); - if (widget != null && widget.getVisibleBounds().intersects( - 0, 0, mDevice.getDisplayWidth(), - mDevice.getDisplayHeight() - margin)) { - return widget; - } - if (++i > 40) fail("Too many attempts"); - container.scroll(Direction.DOWN, 1f); - } - } - /** * Removes all icons from homescreen and hotseat. */ diff --git a/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java b/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java index 06c56f2c44..25c461b599 100644 --- a/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java +++ b/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java @@ -216,7 +216,8 @@ public class TaplTestsLauncher3 extends AbstractLauncherUiTest { final AppIcon app = allApps.getAppIcon("TestActivity7"); assertNotNull("AppIcon.launch returned null", app.launch(getAppPackageName())); test.executeOnLauncher(launcher -> assertTrue( - "Launcher activity is the top activity; expecting another activity to be the top " + "Launcher activity is the top activity; expecting another activity to be the " + + "top " + "one", test.isInBackground(launcher))); } finally { @@ -306,11 +307,8 @@ public class TaplTestsLauncher3 extends AbstractLauncherUiTest { switchToAllApps(); allApps.freeze(); try { - allApps. - getAppIcon(APP_NAME). - dragToWorkspace(). - getWorkspaceAppIcon(APP_NAME). - launch(getAppPackageName()); + allApps.getAppIcon(APP_NAME).dragToWorkspace(); + mLauncher.getWorkspace().getWorkspaceAppIcon(APP_NAME).launch(getAppPackageName()); } finally { allApps.unfreeze(); } @@ -339,13 +337,8 @@ public class TaplTestsLauncher3 extends AbstractLauncherUiTest { getMenuItem(0); final String shortcutName = menuItem.getText(); - // 4. Drag the first shortcut to the home screen. - // 5. Verify that the shortcut works on home screen - // (the app opens and has the same text as the shortcut). - menuItem. - dragToWorkspace(). - getWorkspaceAppIcon(shortcutName). - launch(getAppPackageName()); + menuItem.dragToWorkspace(); + mLauncher.getWorkspace().getWorkspaceAppIcon(shortcutName).launch(getAppPackageName()); } finally { allApps.unfreeze(); } diff --git a/tests/src/com/android/launcher3/ui/TestViewHelpers.java b/tests/src/com/android/launcher3/ui/TestViewHelpers.java index d13d319525..d0df66485e 100644 --- a/tests/src/com/android/launcher3/ui/TestViewHelpers.java +++ b/tests/src/com/android/launcher3/ui/TestViewHelpers.java @@ -18,28 +18,15 @@ package com.android.launcher3.ui; import static androidx.test.InstrumentationRegistry.getInstrumentation; import static androidx.test.InstrumentationRegistry.getTargetContext; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; - import android.content.ComponentName; -import android.content.Context; -import android.graphics.Point; import android.os.Process; -import android.os.SystemClock; import android.util.Log; -import android.view.KeyEvent; -import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; -import androidx.test.uiautomator.By; -import androidx.test.uiautomator.BySelector; import androidx.test.uiautomator.UiDevice; -import androidx.test.uiautomator.UiObject2; -import androidx.test.uiautomator.Until; import com.android.launcher3.LauncherAppWidgetProviderInfo; -import com.android.launcher3.R; import com.android.launcher3.compat.AppWidgetManagerCompat; import com.android.launcher3.testcomponent.AppWidgetNoConfig; import com.android.launcher3.testcomponent.AppWidgetWithConfig; @@ -50,21 +37,6 @@ import java.util.function.Function; public class TestViewHelpers { private static final String TAG = "TestViewHelpers"; - private static UiDevice getDevice() { - return UiDevice.getInstance(getInstrumentation()); - } - - public static UiObject2 findViewById(int id) { - return getDevice().wait(Until.findObject(getSelectorForId(id)), - AbstractLauncherUiTest.DEFAULT_UI_TIMEOUT); - } - - public static BySelector getSelectorForId(int id) { - final Context targetContext = getTargetContext(); - String name = targetContext.getResources().getResourceEntryName(id); - return By.res(targetContext.getPackageName(), name); - } - /** * Finds a widget provider which can fit on the home screen. * @@ -91,92 +63,6 @@ public class TestViewHelpers { return info; } - /** - * Drags an icon to the center of homescreen. - * - * @param icon object that is either app icon or shortcut icon - */ - public static void dragToWorkspace(UiObject2 icon, boolean expectedToShowShortcuts) { - Point center = icon.getVisibleCenter(); - - // Action Down - final long downTime = SystemClock.uptimeMillis(); - sendPointer(downTime, MotionEvent.ACTION_DOWN, center); - - UiObject2 dragLayer = findViewById(R.id.drag_layer); - - if (expectedToShowShortcuts) { - // Make sure shortcuts show up, and then move a bit to hide them. - assertNotNull(findViewById(R.id.deep_shortcuts_container)); - - Point moveLocation = new Point(center); - int distanceToMove = - getTargetContext().getResources().getDimensionPixelSize( - R.dimen.deep_shortcuts_start_drag_threshold) + 50; - if (moveLocation.y - distanceToMove >= dragLayer.getVisibleBounds().top) { - moveLocation.y -= distanceToMove; - } else { - moveLocation.y += distanceToMove; - } - movePointer(downTime, center, moveLocation); - - assertNull(findViewById(R.id.deep_shortcuts_container)); - } - - // Wait until Remove/Delete target is visible - assertNotNull(findViewById(R.id.delete_target_text)); - - Point moveLocation = dragLayer.getVisibleCenter(); - - // Move to center - movePointer(downTime, center, moveLocation); - sendPointer(downTime, MotionEvent.ACTION_UP, moveLocation); - - // Wait until remove target is gone. - getDevice().wait(Until.gone(getSelectorForId(R.id.delete_target_text)), - AbstractLauncherUiTest.DEFAULT_UI_TIMEOUT); - } - - private static void movePointer(long downTime, Point from, Point to) { - while (!from.equals(to)) { - try { - Thread.sleep(20); - } catch (InterruptedException e) { - e.printStackTrace(); - } - from.x = getNextMoveValue(to.x, from.x); - from.y = getNextMoveValue(to.y, from.y); - sendPointer(downTime, MotionEvent.ACTION_MOVE, from); - } - } - - private static int getNextMoveValue(int targetValue, int oldValue) { - if (targetValue - oldValue > 10) { - return oldValue + 10; - } else if (targetValue - oldValue < -10) { - return oldValue - 10; - } else { - return targetValue; - } - } - - public static void sendPointer(long downTime, int action, Point point) { - MotionEvent event = MotionEvent.obtain(downTime, - SystemClock.uptimeMillis(), action, point.x, point.y, 0); - getInstrumentation().sendPointerSync(event); - event.recycle(); - } - - /** - * Opens widget tray and returns the recycler view. - */ - public static UiObject2 openWidgetsTray() { - final UiDevice device = getDevice(); - device.pressKeyCode(KeyEvent.KEYCODE_W, KeyEvent.META_CTRL_ON); - device.waitForIdle(); - return findViewById(R.id.widgets_list_view); - } - public static View findChildView(ViewGroup parent, Function condition) { for (int i = 0; i < parent.getChildCount(); ++i) { final View child = parent.getChildAt(i); diff --git a/tests/src/com/android/launcher3/ui/widget/AddConfigWidgetTest.java b/tests/src/com/android/launcher3/ui/widget/AddConfigWidgetTest.java index 5eb5f19928..dc72bda9a3 100644 --- a/tests/src/com/android/launcher3/ui/widget/AddConfigWidgetTest.java +++ b/tests/src/com/android/launcher3/ui/widget/AddConfigWidgetTest.java @@ -27,20 +27,18 @@ import android.view.View; import androidx.test.filters.LargeTest; import androidx.test.runner.AndroidJUnit4; -import androidx.test.uiautomator.By; -import androidx.test.uiautomator.UiObject2; import com.android.launcher3.ItemInfo; import com.android.launcher3.LauncherAppWidgetInfo; import com.android.launcher3.LauncherAppWidgetProviderInfo; import com.android.launcher3.Workspace; +import com.android.launcher3.tapl.Widgets; import com.android.launcher3.testcomponent.WidgetConfigActivity; import com.android.launcher3.ui.AbstractLauncherUiTest; import com.android.launcher3.ui.TestViewHelpers; import com.android.launcher3.util.Condition; import com.android.launcher3.util.Wait; import com.android.launcher3.util.rule.ShellCommandRule; -import com.android.launcher3.widget.WidgetCell; import org.junit.Before; import org.junit.Ignore; @@ -71,7 +69,6 @@ public class AddConfigWidgetTest extends AbstractLauncherUiTest { } @Test - // Convert test to TAPL b/131116002 public void testWidgetConfig() throws Throwable { runTest(false, true); } @@ -83,7 +80,6 @@ public class AddConfigWidgetTest extends AbstractLauncherUiTest { } @Test - // Convert test to TAPL b/131116002 public void testConfigCancelled() throws Throwable { runTest(false, false); } @@ -104,15 +100,13 @@ public class AddConfigWidgetTest extends AbstractLauncherUiTest { clearHomescreen(); mActivityMonitor.startLauncher(); - // Open widget tray and wait for load complete. - final UiObject2 widgetContainer = TestViewHelpers.openWidgetsTray(); - Wait.atMost(null, Condition.minChildCount(widgetContainer, 2), DEFAULT_UI_TIMEOUT); + final Widgets widgets = mLauncher.getWorkspace().openAllWidgets(); // Drag widget to homescreen WidgetConfigStartupMonitor monitor = new WidgetConfigStartupMonitor(); - UiObject2 widget = scrollAndFind(widgetContainer, By.clazz(WidgetCell.class) - .hasDescendant(By.text(mWidgetInfo.getLabel(mTargetContext.getPackageManager())))); - TestViewHelpers.dragToWorkspace(widget, false); + widgets. + getWidget(mWidgetInfo.getLabel(mTargetContext.getPackageManager())). + dragToWorkspace(); // Widget id for which the config activity was opened mWidgetId = monitor.getWidgetId(); diff --git a/tests/src/com/android/launcher3/ui/widget/AddWidgetTest.java b/tests/src/com/android/launcher3/ui/widget/AddWidgetTest.java index 0061568cce..4529a80d1d 100644 --- a/tests/src/com/android/launcher3/ui/widget/AddWidgetTest.java +++ b/tests/src/com/android/launcher3/ui/widget/AddWidgetTest.java @@ -19,20 +19,12 @@ import static org.junit.Assert.assertTrue; import androidx.test.filters.LargeTest; import androidx.test.runner.AndroidJUnit4; -import androidx.test.uiautomator.By; -import androidx.test.uiautomator.UiObject2; -import android.view.View; -import com.android.launcher3.ItemInfo; import com.android.launcher3.LauncherAppWidgetInfo; import com.android.launcher3.LauncherAppWidgetProviderInfo; -import com.android.launcher3.Workspace.ItemOperator; import com.android.launcher3.ui.AbstractLauncherUiTest; import com.android.launcher3.ui.TestViewHelpers; -import com.android.launcher3.util.Condition; -import com.android.launcher3.util.Wait; import com.android.launcher3.util.rule.ShellCommandRule; -import com.android.launcher3.widget.WidgetCell; import org.junit.Ignore; import org.junit.Rule; @@ -61,7 +53,6 @@ public class AddWidgetTest extends AbstractLauncherUiTest { performTest(); } - // Convert to TAPL b/131116002 private void performTest() throws Throwable { clearHomescreen(); mActivityMonitor.startLauncher(); @@ -69,22 +60,15 @@ public class AddWidgetTest extends AbstractLauncherUiTest { final LauncherAppWidgetProviderInfo widgetInfo = TestViewHelpers.findWidgetProvider(this, false /* hasConfigureScreen */); - // Open widget tray and wait for load complete. - final UiObject2 widgetContainer = TestViewHelpers.openWidgetsTray(); - Wait.atMost(null, Condition.minChildCount(widgetContainer, 2), DEFAULT_UI_TIMEOUT); + mLauncher. + getWorkspace(). + openAllWidgets(). + getWidget(widgetInfo.getLabel(mTargetContext.getPackageManager())). + dragToWorkspace(); - // Drag widget to homescreen - UiObject2 widget = scrollAndFind(widgetContainer, By.clazz(WidgetCell.class) - .hasDescendant(By.text(widgetInfo.getLabel(mTargetContext.getPackageManager())))); - TestViewHelpers.dragToWorkspace(widget, false); - - assertTrue(mActivityMonitor.itemExists(new ItemOperator() { - @Override - public boolean evaluate(ItemInfo info, View view) { - return info instanceof LauncherAppWidgetInfo && + assertTrue(mActivityMonitor.itemExists( + (info, view) -> info instanceof LauncherAppWidgetInfo && ((LauncherAppWidgetInfo) info).providerName.getClassName().equals( - widgetInfo.provider.getClassName()); - } - }).call()); + widgetInfo.provider.getClassName())).call()); } } diff --git a/tests/tapl/com/android/launcher3/tapl/Launchable.java b/tests/tapl/com/android/launcher3/tapl/Launchable.java index d4bdafa764..04b8019450 100644 --- a/tests/tapl/com/android/launcher3/tapl/Launchable.java +++ b/tests/tapl/com/android/launcher3/tapl/Launchable.java @@ -68,7 +68,7 @@ abstract class Launchable { /** * Drags an object to the center of homescreen. */ - public Workspace dragToWorkspace() { + public void dragToWorkspace() { final Point launchableCenter = getObject().getVisibleCenter(); final Point displaySize = mLauncher.getRealDisplaySize(); final int width = displaySize.x / 2; @@ -80,10 +80,6 @@ abstract class Launchable { launchableCenter.x - width / 2 : launchableCenter.x + width / 2, displaySize.y / 2), getLongPressIndicator()); - try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer( - "dragged launchable to workspace")) { - return new Workspace(mLauncher); - } } protected abstract String getLongPressIndicator(); diff --git a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java index a7e6336199..1641d702d3 100644 --- a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java +++ b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java @@ -614,12 +614,12 @@ public final class LauncherInstrumentation { @NonNull UiObject2 waitForLauncherObject(BySelector selector) { - return waitForObjectBySelector(selector.pkg(getLauncherPackageName())); + return waitForObjectBySelector(By.copy(selector).pkg(getLauncherPackageName())); } @NonNull UiObject2 tryWaitForLauncherObject(BySelector selector, long timeout) { - return tryWaitForObjectBySelector(selector.pkg(getLauncherPackageName()), timeout); + return tryWaitForObjectBySelector(By.copy(selector).pkg(getLauncherPackageName()), timeout); } @NonNull diff --git a/tests/tapl/com/android/launcher3/tapl/Widget.java b/tests/tapl/com/android/launcher3/tapl/Widget.java index 128789dbc1..1b6d8c4a6e 100644 --- a/tests/tapl/com/android/launcher3/tapl/Widget.java +++ b/tests/tapl/com/android/launcher3/tapl/Widget.java @@ -18,7 +18,16 @@ package com.android.launcher3.tapl; import androidx.test.uiautomator.UiObject2; -public class Widget { - Widget(LauncherInstrumentation launcher, UiObject2 widget) { +/** + * Widget in workspace or a widget list. + */ +public final class Widget extends Launchable { + Widget(LauncherInstrumentation launcher, UiObject2 icon) { + super(launcher, icon); + } + + @Override + protected String getLongPressIndicator() { + return "drop_target_bar"; } } diff --git a/tests/tapl/com/android/launcher3/tapl/Widgets.java b/tests/tapl/com/android/launcher3/tapl/Widgets.java index 94003be919..cbc3a815f8 100644 --- a/tests/tapl/com/android/launcher3/tapl/Widgets.java +++ b/tests/tapl/com/android/launcher3/tapl/Widgets.java @@ -16,6 +16,12 @@ package com.android.launcher3.tapl; +import static org.junit.Assert.fail; + +import android.graphics.Point; + +import androidx.test.uiautomator.By; +import androidx.test.uiautomator.BySelector; import androidx.test.uiautomator.Direction; import androidx.test.uiautomator.UiObject2; @@ -75,4 +81,28 @@ public final class Widgets extends LauncherInstrumentation.VisibleContainer { protected LauncherInstrumentation.ContainerType getContainerType() { return LauncherInstrumentation.ContainerType.WIDGETS; } + + public Widget getWidget(String label) { + final int margin = ResourceUtils.getNavbarSize( + ResourceUtils.NAVBAR_BOTTOM_GESTURE_SIZE, mLauncher.getResources()) + 1; + final UiObject2 widgetsContainer = verifyActiveContainer(); + widgetsContainer.setGestureMargins(0, 0, 0, margin); + + final Point displaySize = mLauncher.getRealDisplaySize(); + + int i = 0; + final BySelector selector = By. + clazz("com.android.launcher3.widget.WidgetCell"). + hasDescendant(By.text(label)); + + for (; ; ) { + final UiObject2 widget = mLauncher.tryWaitForLauncherObject(selector, 300); + if (widget != null && widget.getVisibleBounds().intersects( + 0, 0, displaySize.x, displaySize.y - margin)) { + return new Widget(mLauncher, widget); + } + if (++i > 40) fail("Too many attempts"); + widgetsContainer.scroll(Direction.DOWN, 1f); + } + } } From 0b70cd4ca4bf56e6afba243e60029f5791868047 Mon Sep 17 00:00:00 2001 From: Winson Chung Date: Mon, 17 Jun 2019 22:34:33 -0700 Subject: [PATCH 38/41] Finish recents animation when starting a new activity - Defer starting the activity when an activity is paused, and finish the current animation to trigger launcher to be resumed Bug: 132811175 Test: Swipe up and launch a new app Change-Id: I78b76800052512eb93f69ccf0523f4d752a82ece --- .../uioverrides/RecentsUiFactory.java | 2 ++ .../uioverrides/RecentsUiFactory.java | 8 +++++ .../android/quickstep/SwipeSharedState.java | 12 ++++--- .../quickstep/TouchInteractionService.java | 32 +++++++++++-------- .../ResetGestureInputConsumer.java | 2 +- .../util/SwipeAnimationTargetSet.java | 4 +++ .../QuickstepAppTransitionManagerImpl.java | 2 +- src/com/android/launcher3/Launcher.java | 27 +++++++++++----- .../launcher3/SecondaryDropTarget.java | 2 +- .../launcher3/uioverrides/UiFactory.java | 2 ++ 10 files changed, 63 insertions(+), 30 deletions(-) diff --git a/go/quickstep/src/com/android/launcher3/uioverrides/RecentsUiFactory.java b/go/quickstep/src/com/android/launcher3/uioverrides/RecentsUiFactory.java index 45822432f5..cbc77d2720 100644 --- a/go/quickstep/src/com/android/launcher3/uioverrides/RecentsUiFactory.java +++ b/go/quickstep/src/com/android/launcher3/uioverrides/RecentsUiFactory.java @@ -89,4 +89,6 @@ public abstract class RecentsUiFactory { public static RotationMode getRotationMode(DeviceProfile dp) { return RotationMode.NORMAL; } + + public static void clearSwipeSharedState(boolean finishAnimation) {} } diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/RecentsUiFactory.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/RecentsUiFactory.java index 8d5ac50942..6ecf1c11bd 100644 --- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/RecentsUiFactory.java +++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/RecentsUiFactory.java @@ -46,6 +46,7 @@ import com.android.launcher3.util.UiThreadHelper; import com.android.launcher3.util.UiThreadHelper.AsyncCommand; import com.android.quickstep.SysUINavigationMode; import com.android.quickstep.SysUINavigationMode.Mode; +import com.android.quickstep.TouchInteractionService; import com.android.quickstep.views.RecentsView; import com.android.systemui.shared.system.WindowManagerWrapper; @@ -183,6 +184,13 @@ public abstract class RecentsUiFactory { return new RecentsViewStateController(launcher); } + /** + * Clears the swipe shared state for the current swipe gesture. + */ + public static void clearSwipeSharedState(boolean finishAnimation) { + TouchInteractionService.getSwipeSharedState().clearAllState(finishAnimation); + } + /** * Recents logic that triggers when launcher state changes or launcher activity stops/resumes. * diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/SwipeSharedState.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/SwipeSharedState.java index 6689ce3d78..c55f656dfd 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/SwipeSharedState.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/SwipeSharedState.java @@ -72,13 +72,15 @@ public class SwipeSharedState implements SwipeAnimationListener { mLastAnimationRunning = false; } - private void clearListenerState() { + private void clearListenerState(boolean finishAnimation) { if (mRecentsAnimationListener != null) { mRecentsAnimationListener.removeListener(this); mRecentsAnimationListener.cancelListener(); if (mLastAnimationRunning && mLastAnimationTarget != null) { Utilities.postAsyncCallback(MAIN_THREAD_EXECUTOR.getHandler(), - mLastAnimationTarget::cancelAnimation); + finishAnimation + ? mLastAnimationTarget::finishAnimation + : mLastAnimationTarget::cancelAnimation); mLastAnimationTarget = null; } } @@ -106,7 +108,7 @@ public class SwipeSharedState implements SwipeAnimationListener { } } - clearListenerState(); + clearListenerState(false /* finishAnimation */); boolean shouldMinimiseSplitScreen = mOverviewComponentObserver == null ? false : mOverviewComponentObserver.getActivityControlHelper().shouldMinimizeSplitScreen(); mRecentsAnimationListener = new RecentsAnimationListenerSet( @@ -138,8 +140,8 @@ public class SwipeSharedState implements SwipeAnimationListener { mLastAnimationTarget = mLastAnimationTarget.cloneWithoutTargets(); } - public void clearAllState() { - clearListenerState(); + public void clearAllState(boolean finishAnimation) { + clearListenerState(finishAnimation); canGestureBeContinued = false; recentsAnimationFinishInterrupted = false; nextRunningTaskId = -1; diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java index 8f08f0de9b..53da0f92dd 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java @@ -225,14 +225,18 @@ public class TouchInteractionService extends Service implements }; private static boolean sConnected = false; + private static final SwipeSharedState sSwipeSharedState = new SwipeSharedState(); public static boolean isConnected() { return sConnected; } - private final SwipeSharedState mSwipeSharedState = new SwipeSharedState(); + public static SwipeSharedState getSwipeSharedState() { + return sSwipeSharedState; + } + private final InputConsumer mResetGestureInputConsumer = - new ResetGestureInputConsumer(mSwipeSharedState); + new ResetGestureInputConsumer(sSwipeSharedState); private ActivityManagerWrapper mAM; private RecentsModel mRecentsModel; @@ -436,7 +440,7 @@ public class TouchInteractionService extends Service implements mInputConsumer = InputConsumerController.getRecentsAnimationInputConsumer(); mIsUserUnlocked = true; - mSwipeSharedState.setOverviewComponentObserver(mOverviewComponentObserver); + sSwipeSharedState.setOverviewComponentObserver(mOverviewComponentObserver); mInputConsumer.registerInputConsumer(); onSystemUiProxySet(); onSystemUiFlagsChanged(); @@ -589,7 +593,7 @@ public class TouchInteractionService extends Service implements private InputConsumer newBaseConsumer(boolean useSharedState, MotionEvent event) { final RunningTaskInfo runningTaskInfo = mAM.getRunningTask(0); if (!useSharedState) { - mSwipeSharedState.clearAllState(); + sSwipeSharedState.clearAllState(false /* finishAnimation */); } if ((mSystemUiStateFlags & SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING_OCCLUDED) != 0) { // This handles apps showing over the lockscreen (e.g. camera) @@ -599,16 +603,16 @@ public class TouchInteractionService extends Service implements final ActivityControlHelper activityControl = mOverviewComponentObserver.getActivityControlHelper(); - if (runningTaskInfo == null && !mSwipeSharedState.goingToLauncher - && !mSwipeSharedState.recentsAnimationFinishInterrupted) { + if (runningTaskInfo == null && !sSwipeSharedState.goingToLauncher + && !sSwipeSharedState.recentsAnimationFinishInterrupted) { return mResetGestureInputConsumer; - } else if (mSwipeSharedState.recentsAnimationFinishInterrupted) { + } else if (sSwipeSharedState.recentsAnimationFinishInterrupted) { // If the finish animation was interrupted, then continue using the other activity input // consumer but with the next task as the running task RunningTaskInfo info = new ActivityManager.RunningTaskInfo(); - info.id = mSwipeSharedState.nextRunningTaskId; + info.id = sSwipeSharedState.nextRunningTaskId; return createOtherActivityInputConsumer(event, info); - } else if (mSwipeSharedState.goingToLauncher || activityControl.isResumed()) { + } else if (sSwipeSharedState.goingToLauncher || activityControl.isResumed()) { return createOverviewInputConsumer(event); } else if (ENABLE_QUICKSTEP_LIVE_TILE.get() && activityControl.isInLiveTileMode()) { return createOverviewInputConsumer(event); @@ -617,7 +621,7 @@ public class TouchInteractionService extends Service implements return mResetGestureInputConsumer; } else if (mMode == Mode.NO_BUTTON && !mOverviewComponentObserver.isHomeAndOverviewSame()) { return new FallbackNoButtonInputConsumer(this, activityControl, - mInputMonitorCompat, mSwipeSharedState, mSwipeTouchRegion, + mInputMonitorCompat, sSwipeSharedState, mSwipeTouchRegion, mOverviewComponentObserver, disableHorizontalSwipe(event), runningTaskInfo); } else { return createOtherActivityInputConsumer(event, runningTaskInfo); @@ -640,13 +644,13 @@ public class TouchInteractionService extends Service implements return new OtherActivityInputConsumer(this, runningTaskInfo, mRecentsModel, mOverviewComponentObserver.getOverviewIntent(), activityControl, shouldDefer, mOverviewCallbacks, mInputConsumer, this::onConsumerInactive, - mSwipeSharedState, mInputMonitorCompat, mSwipeTouchRegion, + sSwipeSharedState, mInputMonitorCompat, mSwipeTouchRegion, disableHorizontalSwipe(event)); } private InputConsumer createDeviceLockedInputConsumer(RunningTaskInfo taskInfo) { if (mMode == Mode.NO_BUTTON && taskInfo != null) { - return new DeviceLockedInputConsumer(this, mSwipeSharedState, mInputMonitorCompat, + return new DeviceLockedInputConsumer(this, sSwipeSharedState, mInputMonitorCompat, mSwipeTouchRegion, taskInfo.taskId); } else { return mResetGestureInputConsumer; @@ -661,7 +665,7 @@ public class TouchInteractionService extends Service implements return mResetGestureInputConsumer; } - if (activity.getRootView().hasWindowFocus() || mSwipeSharedState.goingToLauncher) { + if (activity.getRootView().hasWindowFocus() || sSwipeSharedState.goingToLauncher) { return new OverviewInputConsumer(activity, mInputMonitorCompat, false /* startingInActivityBounds */); } else { @@ -708,7 +712,7 @@ public class TouchInteractionService extends Service implements + mOverviewComponentObserver.getActivityControlHelper().isResumed()); pw.println(" useSharedState=" + mConsumer.useSharedSwipeState()); if (mConsumer.useSharedSwipeState()) { - mSwipeSharedState.dump(" ", pw); + sSwipeSharedState.dump(" ", pw); } pw.println(" mConsumer=" + mConsumer.getName()); pw.println("FeatureFlags:"); diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/ResetGestureInputConsumer.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/ResetGestureInputConsumer.java index 56cba2192a..8eede81b84 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/ResetGestureInputConsumer.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/ResetGestureInputConsumer.java @@ -39,7 +39,7 @@ public class ResetGestureInputConsumer implements InputConsumer { public void onMotionEvent(MotionEvent ev) { if (ev.getAction() == MotionEvent.ACTION_DOWN && mSwipeSharedState.getActiveListener() != null) { - mSwipeSharedState.clearAllState(); + mSwipeSharedState.clearAllState(false /* finishAnimation */); } } } diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/SwipeAnimationTargetSet.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/SwipeAnimationTargetSet.java index df9efa2477..381c27a284 100644 --- a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/SwipeAnimationTargetSet.java +++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/SwipeAnimationTargetSet.java @@ -106,6 +106,10 @@ public class SwipeAnimationTargetSet extends RemoteAnimationTargetSet { finishController(false /* toRecents */, null, false /* sendUserLeaveHint */); } + public void finishAnimation() { + finishController(true /* toRecents */, null, false /* sendUserLeaveHint */); + } + public interface SwipeAnimationListener { void onRecentsAnimationStart(SwipeAnimationTargetSet targetSet); diff --git a/quickstep/src/com/android/launcher3/QuickstepAppTransitionManagerImpl.java b/quickstep/src/com/android/launcher3/QuickstepAppTransitionManagerImpl.java index 8643160950..44324cb2e4 100644 --- a/quickstep/src/com/android/launcher3/QuickstepAppTransitionManagerImpl.java +++ b/quickstep/src/com/android/launcher3/QuickstepAppTransitionManagerImpl.java @@ -765,7 +765,7 @@ public abstract class QuickstepAppTransitionManagerImpl extends LauncherAppTrans LauncherAnimationRunner.AnimationResult result) { if (!mLauncher.hasBeenResumed()) { // If launcher is not resumed, wait until new async-frame after resume - mLauncher.setOnResumeCallback(() -> + mLauncher.addOnResumeCallback(() -> postAsyncCallback(mHandler, () -> onCreateAnimation(targetCompats, result))); return; diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index ad2783e0c6..d9af4da8e8 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -244,7 +244,7 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns, @Thunk boolean mWorkspaceLoading = true; - private OnResumeCallback mOnResumeCallback; + private ArrayList mOnResumeCallbacks = new ArrayList<>(); private ViewOnDrawExecutor mPendingExecutor; @@ -869,6 +869,7 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns, @Override protected void onStop() { super.onStop(); + if (mLauncherCallbacks != null) { mLauncherCallbacks.onStop(); } @@ -951,7 +952,10 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns, mHandler.removeCallbacks(mHandleDeferredResume); Utilities.postAsyncCallback(mHandler, mHandleDeferredResume); - setOnResumeCallback(null); + for (OnResumeCallback cb : mOnResumeCallbacks) { + cb.onLauncherResume(); + } + mOnResumeCallbacks.clear(); if (mLauncherCallbacks != null) { mLauncherCallbacks.onResume(); @@ -1805,6 +1809,16 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns, android.util.Log.d(TestProtocol.NO_START_TAG, "startActivitySafely outer"); } + + if (!hasBeenResumed()) { + // Workaround an issue where the WM launch animation is clobbered when finishing the + // recents animation into launcher. Defer launching the activity until Launcher is + // next resumed. + addOnResumeCallback(() -> startActivitySafely(v, intent, item, sourceContainer)); + UiFactory.clearSwipeSharedState(true /* finishAnimation */); + return true; + } + boolean success = super.startActivitySafely(v, intent, item, sourceContainer); if (success && v instanceof BubbleTextView) { // This is set to the view that launched the activity that navigated the user away @@ -1813,7 +1827,7 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns, // state when we return to launcher. BubbleTextView btv = (BubbleTextView) v; btv.setStayPressed(true); - setOnResumeCallback(btv); + addOnResumeCallback(btv); } return success; } @@ -1861,11 +1875,8 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns, return result; } - public void setOnResumeCallback(OnResumeCallback callback) { - if (mOnResumeCallback != null) { - mOnResumeCallback.onLauncherResume(); - } - mOnResumeCallback = callback; + public void addOnResumeCallback(OnResumeCallback callback) { + mOnResumeCallbacks.add(callback); } /** diff --git a/src/com/android/launcher3/SecondaryDropTarget.java b/src/com/android/launcher3/SecondaryDropTarget.java index 0cf6e44c63..55cb6f2143 100644 --- a/src/com/android/launcher3/SecondaryDropTarget.java +++ b/src/com/android/launcher3/SecondaryDropTarget.java @@ -179,7 +179,7 @@ public class SecondaryDropTarget extends ButtonDropTarget implements OnAlarmList DeferredOnComplete deferred = (DeferredOnComplete) d.dragSource; if (target != null) { deferred.mPackageName = target.getPackageName(); - mLauncher.setOnResumeCallback(deferred); + mLauncher.addOnResumeCallback(deferred); } else { deferred.sendFailure(); } diff --git a/src_ui_overrides/com/android/launcher3/uioverrides/UiFactory.java b/src_ui_overrides/com/android/launcher3/uioverrides/UiFactory.java index 17ff66e92c..5cc64dc9aa 100644 --- a/src_ui_overrides/com/android/launcher3/uioverrides/UiFactory.java +++ b/src_ui_overrides/com/android/launcher3/uioverrides/UiFactory.java @@ -93,4 +93,6 @@ public class UiFactory { public static void resetPendingActivityResults(Launcher launcher, int requestCode) { } + public static void clearSwipeSharedState(boolean finishAnimation) {} + } From 35062f8ed32ea4a868be077fdae37409a700e759 Mon Sep 17 00:00:00 2001 From: Hyunyoung Song Date: Fri, 21 Jun 2019 23:24:02 -0700 Subject: [PATCH 39/41] Enable FLAG_SLIPPERY window flag when swipe down on workspace Bug: 131356741 Test: manual Change-Id: Ibe0e3a03e7406d5d882a1c63265d6e4e0253983e --- .../StatusBarTouchController.java | 68 +++-- .../launcher3/touch/TouchEventTranslator.java | 283 ------------------ 2 files changed, 51 insertions(+), 300 deletions(-) delete mode 100644 src/com/android/launcher3/touch/TouchEventTranslator.java diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/StatusBarTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/StatusBarTouchController.java index fee18204ea..f5ba3725df 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/StatusBarTouchController.java +++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/StatusBarTouchController.java @@ -17,17 +17,22 @@ package com.android.launcher3.uioverrides.touchcontrollers; import static android.view.MotionEvent.ACTION_DOWN; import static android.view.MotionEvent.ACTION_MOVE; +import static android.view.MotionEvent.ACTION_UP; +import static android.view.MotionEvent.ACTION_CANCEL; +import android.graphics.PointF; import android.os.RemoteException; import android.util.Log; +import android.util.SparseArray; import android.view.MotionEvent; import android.view.ViewConfiguration; +import android.view.Window; +import android.view.WindowManager; import com.android.launcher3.AbstractFloatingView; import com.android.launcher3.DeviceProfile; import com.android.launcher3.Launcher; import com.android.launcher3.LauncherState; -import com.android.launcher3.touch.TouchEventTranslator; import com.android.launcher3.util.TouchController; import com.android.quickstep.RecentsModel; import com.android.systemui.shared.recents.ISystemUiProxy; @@ -36,18 +41,29 @@ import java.io.PrintWriter; /** * TouchController for handling touch events that get sent to the StatusBar. Once the - * Once the event delta y passes the touch slop, the events start getting forwarded. + * Once the event delta mDownY passes the touch slop, the events start getting forwarded. * All events are offset by initial Y value of the pointer. */ public class StatusBarTouchController implements TouchController { private static final String TAG = "StatusBarController"; + /** + * Window flag: Enable touches to slide out of a window into neighboring + * windows in mid-gesture instead of being captured for the duration of + * the gesture. + * + * This flag changes the behavior of touch focus for this window only. + * Touches can slide out of the window but they cannot necessarily slide + * back in (unless the other window with touch focus permits it). + */ + private static final int FLAG_SLIPPERY = 0x20000000; + protected final Launcher mLauncher; - protected final TouchEventTranslator mTranslator; private final float mTouchSlop; private ISystemUiProxy mSysUiProxy; private int mLastAction; + private final SparseArray mDownEvents; /* If {@code false}, this controller should not handle the input {@link MotionEvent}.*/ private boolean mCanIntercept; @@ -56,7 +72,7 @@ public class StatusBarTouchController implements TouchController { mLauncher = l; // Guard against TAPs by increasing the touch slop. mTouchSlop = 2 * ViewConfiguration.get(l).getScaledTouchSlop(); - mTranslator = new TouchEventTranslator((MotionEvent ev)-> dispatchTouchEvent(ev)); + mDownEvents = new SparseArray<>(); } @Override @@ -64,7 +80,6 @@ public class StatusBarTouchController implements TouchController { writer.println(prefix + "mCanIntercept:" + mCanIntercept); writer.println(prefix + "mLastAction:" + MotionEvent.actionToString(mLastAction)); writer.println(prefix + "mSysUiProxy available:" + (mSysUiProxy != null)); - } private void dispatchTouchEvent(MotionEvent ev) { @@ -81,26 +96,31 @@ public class StatusBarTouchController implements TouchController { @Override public final boolean onControllerInterceptTouchEvent(MotionEvent ev) { int action = ev.getActionMasked(); + int idx = ev.getActionIndex(); + int pid = ev.getPointerId(idx); if (action == ACTION_DOWN) { mCanIntercept = canInterceptTouch(ev); if (!mCanIntercept) { return false; } - mTranslator.reset(); - mTranslator.setDownParameters(0, ev); + mDownEvents.put(pid, new PointF(ev.getX(), ev.getY())); } else if (ev.getActionMasked() == MotionEvent.ACTION_POINTER_DOWN) { - // Check!! should only set it only when threshold is not entered. - mTranslator.setDownParameters(ev.getActionIndex(), ev); + // Check!! should only set it only when threshold is not entered. + mDownEvents.put(pid, new PointF(ev.getX(idx), ev.getY(idx))); } if (!mCanIntercept) { return false; } if (action == ACTION_MOVE) { - float dy = ev.getY() - mTranslator.getDownY(); - float dx = ev.getX() - mTranslator.getDownX(); - if (dy > mTouchSlop && dy > Math.abs(dx)) { - mTranslator.dispatchDownEvents(ev); - mTranslator.processMotionEvent(ev); + float dy = ev.getY(idx) - mDownEvents.get(pid).y; + float dx = ev.getX(idx) - mDownEvents.get(pid).x; + // Currently input dispatcher will not do touch transfer if there are more than + // one touch pointer. Hence, even if slope passed, only set the slippery flag + // when there is single touch event. (context: InputDispatcher.cpp line 1445) + if (dy > mTouchSlop && dy > Math.abs(dx) && ev.getPointerCount() == 1) { + ev.setAction(ACTION_DOWN); + dispatchTouchEvent(ev); + setWindowSlippery(true); return true; } if (Math.abs(dx) > mTouchSlop) { @@ -110,13 +130,27 @@ public class StatusBarTouchController implements TouchController { return false; } - @Override public final boolean onControllerTouchEvent(MotionEvent ev) { - mTranslator.processMotionEvent(ev); + if (ev.getAction() == ACTION_UP || ev.getAction() == ACTION_CANCEL) { + dispatchTouchEvent(ev); + setWindowSlippery(false); + return true; + } return true; } + private void setWindowSlippery(boolean enable) { + Window w = mLauncher.getWindow(); + WindowManager.LayoutParams wlp = w.getAttributes(); + if (enable) { + wlp.flags |= FLAG_SLIPPERY; + } else { + wlp.flags &= ~FLAG_SLIPPERY; + } + w.setAttributes(wlp); + } + private boolean canInterceptTouch(MotionEvent ev) { if (!mLauncher.isInState(LauncherState.NORMAL) || AbstractFloatingView.getTopOpenViewWithType(mLauncher, @@ -132,4 +166,4 @@ public class StatusBarTouchController implements TouchController { mSysUiProxy = RecentsModel.INSTANCE.get(mLauncher).getSystemUiProxy(); return mSysUiProxy != null; } -} +} \ No newline at end of file diff --git a/src/com/android/launcher3/touch/TouchEventTranslator.java b/src/com/android/launcher3/touch/TouchEventTranslator.java deleted file mode 100644 index 3fcda90848..0000000000 --- a/src/com/android/launcher3/touch/TouchEventTranslator.java +++ /dev/null @@ -1,283 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.android.launcher3.touch; - -import android.graphics.PointF; -import android.util.Log; -import android.util.Pair; -import android.util.SparseArray; -import android.view.MotionEvent; -import android.view.MotionEvent.PointerCoords; -import android.view.MotionEvent.PointerProperties; - -import java.util.function.Consumer; - -/** - * To minimize the size of the MotionEvent, historic events are not copied and passed via the - * listener. - */ -public class TouchEventTranslator { - - private static final String TAG = "TouchEventTranslator"; - private static final boolean DEBUG = false; - - private class DownState { - long timeStamp; - float downX; - float downY; - public DownState(long timeStamp, float downX, float downY) { - this.timeStamp = timeStamp; - this.downX = downX; - this.downY = downY; - } - }; - private final DownState ZERO = new DownState(0, 0f, 0f); - - private final Consumer mListener; - - private final SparseArray mDownEvents; - private final SparseArray mFingers; - - private final SparseArray> mCache; - - public TouchEventTranslator(Consumer listener) { - mDownEvents = new SparseArray<>(); - mFingers = new SparseArray<>(); - mCache = new SparseArray<>(); - - mListener = listener; - } - - public void reset() { - mDownEvents.clear(); - mFingers.clear(); - } - - public float getDownX() { - return mDownEvents.get(0).downX; - } - - public float getDownY() { - return mDownEvents.get(0).downY; - } - - public void setDownParameters(int idx, MotionEvent e) { - DownState ev = new DownState(e.getEventTime(), e.getX(idx), e.getY(idx)); - mDownEvents.append(idx, ev); - } - - public void dispatchDownEvents(MotionEvent ev) { - for(int i = 0; i < ev.getPointerCount() && i < mDownEvents.size(); i++) { - int pid = ev.getPointerId(i); - put(pid, i, ev.getX(i), 0, mDownEvents.get(i).timeStamp, ev); - } - } - - public void processMotionEvent(MotionEvent ev) { - if (DEBUG) { - printSamples(TAG + " processMotionEvent", ev); - } - int index = ev.getActionIndex(); - float x = ev.getX(index); - float y = ev.getY(index) - mDownEvents.get(index, ZERO).downY; - switch (ev.getActionMasked()) { - case MotionEvent.ACTION_POINTER_DOWN: - int pid = ev.getPointerId(index); - if(mFingers.get(pid, null) != null) { - for(int i=0; i < ev.getPointerCount(); i++) { - pid = ev.getPointerId(i); - position(pid, x, y); - } - generateEvent(ev.getAction(), ev); - } else { - put(pid, index, x, y, ev); - } - break; - case MotionEvent.ACTION_MOVE: - for(int i=0; i < ev.getPointerCount(); i++) { - pid = ev.getPointerId(i); - position(pid, x, y); - } - generateEvent(ev.getAction(), ev); - break; - case MotionEvent.ACTION_POINTER_UP: - case MotionEvent.ACTION_UP: - pid = ev.getPointerId(index); - lift(pid, index, x, y, ev); - break; - case MotionEvent.ACTION_CANCEL: - cancel(ev); - break; - default: - Log.v(TAG, "Didn't process "); - printSamples(TAG, ev); - - } - } - - private TouchEventTranslator put(int id, int index, float x, float y, MotionEvent ev) { - return put(id, index, x, y, ev.getEventTime(), ev); - } - - private TouchEventTranslator put(int id, int index, float x, float y, long ms, MotionEvent ev) { - checkFingerExistence(id, false); - boolean isInitialDown = (mFingers.size() == 0); - - mFingers.put(id, new PointF(x, y)); - int n = mFingers.size(); - - if (mCache.get(n) == null) { - PointerProperties[] properties = new PointerProperties[n]; - PointerCoords[] coords = new PointerCoords[n]; - for (int i = 0; i < n; i++) { - properties[i] = new PointerProperties(); - coords[i] = new PointerCoords(); - } - mCache.put(n, new Pair(properties, coords)); - } - - int action; - if (isInitialDown) { - action = MotionEvent.ACTION_DOWN; - } else { - action = MotionEvent.ACTION_POINTER_DOWN; - // Set the id of the changed pointer. - action |= index << MotionEvent.ACTION_POINTER_INDEX_SHIFT; - } - generateEvent(action, ms, ev); - return this; - } - - public TouchEventTranslator position(int id, float x, float y) { - checkFingerExistence(id, true); - mFingers.get(id).set(x, y); - return this; - } - - private TouchEventTranslator lift(int id, int index, MotionEvent ev) { - checkFingerExistence(id, true); - boolean isFinalUp = (mFingers.size() == 1); - int action; - if (isFinalUp) { - action = MotionEvent.ACTION_UP; - } else { - action = MotionEvent.ACTION_POINTER_UP; - // Set the id of the changed pointer. - action |= index << MotionEvent.ACTION_POINTER_INDEX_SHIFT; - } - generateEvent(action, ev); - mFingers.remove(id); - return this; - } - - private TouchEventTranslator lift(int id, int index, float x, float y, MotionEvent ev) { - checkFingerExistence(id, true); - mFingers.get(id).set(x, y); - return lift(id, index, ev); - } - - public TouchEventTranslator cancel(MotionEvent ev) { - generateEvent(MotionEvent.ACTION_CANCEL, ev); - mFingers.clear(); - return this; - } - - private void checkFingerExistence(int id, boolean shouldExist) { - if (shouldExist != (mFingers.get(id, null) != null)) { - throw new IllegalArgumentException( - shouldExist ? "Finger does not exist" : "Finger already exists"); - } - } - - - /** - * Used to debug MotionEvents being sent/received. - */ - public void printSamples(String msg, MotionEvent ev) { - System.out.printf("%s %s", msg, MotionEvent.actionToString(ev.getActionMasked())); - final int pointerCount = ev.getPointerCount(); - System.out.printf("#%d/%d", ev.getActionIndex(), pointerCount); - System.out.printf(" t=%d:", ev.getEventTime()); - for (int p = 0; p < pointerCount; p++) { - System.out.printf(" id=%d: (%f,%f)", - ev.getPointerId(p), ev.getX(p), ev.getY(p)); - } - System.out.println(); - } - - private void generateEvent(int action, MotionEvent ev) { - generateEvent(action, ev.getEventTime(), ev); - } - - private void generateEvent(int action, long ms, MotionEvent ev) { - Pair state = getFingerState(); - MotionEvent event = MotionEvent.obtain( - mDownEvents.get(0).timeStamp, - ms, - action, - state.first.length, - state.first, - state.second, - ev.getMetaState(), - ev.getButtonState() /* buttonState */, - ev.getXPrecision() /* xPrecision */, - ev.getYPrecision() /* yPrecision */, - ev.getDeviceId(), - ev.getEdgeFlags(), - ev.getSource(), - ev.getFlags() /* flags */); - if (DEBUG) { - printSamples(TAG + " generateEvent", event); - } - if (event.getPointerId(event.getActionIndex()) < 0) { - printSamples(TAG + "generateEvent", event); - throw new IllegalStateException(event.getActionIndex() + " not found in MotionEvent"); - } - mListener.accept(event); - event.recycle(); - } - - /** - * Returns the description of the fingers' state expected by MotionEvent. - */ - private Pair getFingerState() { - int nFingers = mFingers.size(); - - Pair result = mCache.get(nFingers); - PointerProperties[] properties = result.first; - PointerCoords[] coordinates = result.second; - - int index = 0; - for (int i = 0; i < mFingers.size(); i++) { - int id = mFingers.keyAt(i); - PointF location = mFingers.get(id); - - PointerProperties property = properties[i]; - property.id = id; - property.toolType = MotionEvent.TOOL_TYPE_FINGER; - properties[index] = property; - - PointerCoords coordinate = coordinates[i]; - coordinate.x = location.x; - coordinate.y = location.y; - coordinate.pressure = 1.0f; - coordinates[index] = coordinate; - - index++; - } - return mCache.get(nFingers); - } -} From f45509ec8e4a00436a18c2cce77c166321b0af78 Mon Sep 17 00:00:00 2001 From: vadimt Date: Wed, 26 Jun 2019 15:16:26 -0700 Subject: [PATCH 40/41] Reenabling tests in OOP Bug: 124524897 Change-Id: I8bdaa0d129c7366616359a3505f3d6b2d397e18f --- tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java | 4 ---- 1 file changed, 4 deletions(-) diff --git a/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java b/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java index 25c461b599..4dab44fadd 100644 --- a/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java +++ b/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java @@ -276,8 +276,6 @@ public class TaplTestsLauncher3 extends AbstractLauncherUiTest { @Test @PortraitLandscape public void testLaunchMenuItem() throws Exception { - if (!TestHelpers.isInLauncherProcess()) return; - final AllApps allApps = mLauncher. getWorkspace(). switchToAllApps(); @@ -321,8 +319,6 @@ public class TaplTestsLauncher3 extends AbstractLauncherUiTest { @Test @PortraitLandscape public void testDragShortcut() throws Throwable { - if (!TestHelpers.isInLauncherProcess()) return; - // 1. Open all apps and wait for load complete. // 2. Find the app and long press it to show shortcuts. // 3. Press icon center until shortcuts appear From 007e7e94b317ab2d9aa03636be5037bc3cee2216 Mon Sep 17 00:00:00 2001 From: Vadim Tryshev Date: Thu, 27 Jun 2019 00:03:14 +0000 Subject: [PATCH 41/41] Revert "Converting even more tests to TAPL" This reverts commit 406d49bdd724a9707a218f6b7ba3d40bdfb87028. Reason for revert: Breaks tests on Crosshatch Change-Id: I95ff541dfe8f6f1d7e1913d209b53abfbebe5336 --- .../launcher3/ui/AbstractLauncherUiTest.java | 31 +++++ .../launcher3/ui/TaplTestsLauncher3.java | 19 ++- .../android/launcher3/ui/TestViewHelpers.java | 114 ++++++++++++++++++ .../ui/widget/AddConfigWidgetTest.java | 16 ++- .../launcher3/ui/widget/AddWidgetTest.java | 32 +++-- .../android/launcher3/tapl/Launchable.java | 6 +- .../tapl/LauncherInstrumentation.java | 4 +- .../com/android/launcher3/tapl/Widget.java | 13 +- .../com/android/launcher3/tapl/Widgets.java | 30 ----- 9 files changed, 202 insertions(+), 63 deletions(-) diff --git a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java index abaad20b2a..4a0ca5c245 100644 --- a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java +++ b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java @@ -20,6 +20,7 @@ import static androidx.test.InstrumentationRegistry.getInstrumentation; import static com.android.launcher3.ui.TaplTestsLauncher3.getAppPackageName; import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; import static java.lang.System.exit; @@ -37,7 +38,10 @@ import android.util.Log; import androidx.test.InstrumentationRegistry; import androidx.test.uiautomator.By; +import androidx.test.uiautomator.BySelector; +import androidx.test.uiautomator.Direction; import androidx.test.uiautomator.UiDevice; +import androidx.test.uiautomator.UiObject2; import androidx.test.uiautomator.Until; import com.android.launcher3.Launcher; @@ -46,6 +50,7 @@ import com.android.launcher3.LauncherModel; import com.android.launcher3.LauncherSettings; import com.android.launcher3.LauncherState; import com.android.launcher3.MainThreadExecutor; +import com.android.launcher3.ResourceUtils; import com.android.launcher3.Utilities; import com.android.launcher3.compat.LauncherAppsCompat; import com.android.launcher3.model.AppLaunchTracker; @@ -81,6 +86,7 @@ public abstract class AbstractLauncherUiTest { public static final long DEFAULT_ACTIVITY_TIMEOUT = TimeUnit.SECONDS.toMillis(10); public static final long DEFAULT_BROADCAST_TIMEOUT_SECS = 5; + public static final long SHORT_UI_TIMEOUT = 300; public static final long DEFAULT_UI_TIMEOUT = 10000; private static final String TAG = "AbstractLauncherUiTest"; @@ -174,6 +180,31 @@ public abstract class AbstractLauncherUiTest { } } + /** + * Scrolls the {@param container} until it finds an object matching {@param condition}. + * + * @return the matching object. + */ + protected UiObject2 scrollAndFind(UiObject2 container, BySelector condition) { + final int margin = ResourceUtils.getNavbarSize( + ResourceUtils.NAVBAR_BOTTOM_GESTURE_SIZE, mLauncher.getResources()) + 1; + container.setGestureMargins(0, 0, 0, margin); + + int i = 0; + for (; ; ) { + // findObject can only execute after spring settles. + mDevice.wait(Until.findObject(condition), SHORT_UI_TIMEOUT); + UiObject2 widget = container.findObject(condition); + if (widget != null && widget.getVisibleBounds().intersects( + 0, 0, mDevice.getDisplayWidth(), + mDevice.getDisplayHeight() - margin)) { + return widget; + } + if (++i > 40) fail("Too many attempts"); + container.scroll(Direction.DOWN, 1f); + } + } + /** * Removes all icons from homescreen and hotseat. */ diff --git a/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java b/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java index 25c461b599..06c56f2c44 100644 --- a/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java +++ b/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java @@ -216,8 +216,7 @@ public class TaplTestsLauncher3 extends AbstractLauncherUiTest { final AppIcon app = allApps.getAppIcon("TestActivity7"); assertNotNull("AppIcon.launch returned null", app.launch(getAppPackageName())); test.executeOnLauncher(launcher -> assertTrue( - "Launcher activity is the top activity; expecting another activity to be the " - + "top " + "Launcher activity is the top activity; expecting another activity to be the top " + "one", test.isInBackground(launcher))); } finally { @@ -307,8 +306,11 @@ public class TaplTestsLauncher3 extends AbstractLauncherUiTest { switchToAllApps(); allApps.freeze(); try { - allApps.getAppIcon(APP_NAME).dragToWorkspace(); - mLauncher.getWorkspace().getWorkspaceAppIcon(APP_NAME).launch(getAppPackageName()); + allApps. + getAppIcon(APP_NAME). + dragToWorkspace(). + getWorkspaceAppIcon(APP_NAME). + launch(getAppPackageName()); } finally { allApps.unfreeze(); } @@ -337,8 +339,13 @@ public class TaplTestsLauncher3 extends AbstractLauncherUiTest { getMenuItem(0); final String shortcutName = menuItem.getText(); - menuItem.dragToWorkspace(); - mLauncher.getWorkspace().getWorkspaceAppIcon(shortcutName).launch(getAppPackageName()); + // 4. Drag the first shortcut to the home screen. + // 5. Verify that the shortcut works on home screen + // (the app opens and has the same text as the shortcut). + menuItem. + dragToWorkspace(). + getWorkspaceAppIcon(shortcutName). + launch(getAppPackageName()); } finally { allApps.unfreeze(); } diff --git a/tests/src/com/android/launcher3/ui/TestViewHelpers.java b/tests/src/com/android/launcher3/ui/TestViewHelpers.java index d0df66485e..d13d319525 100644 --- a/tests/src/com/android/launcher3/ui/TestViewHelpers.java +++ b/tests/src/com/android/launcher3/ui/TestViewHelpers.java @@ -18,15 +18,28 @@ package com.android.launcher3.ui; import static androidx.test.InstrumentationRegistry.getInstrumentation; import static androidx.test.InstrumentationRegistry.getTargetContext; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; + import android.content.ComponentName; +import android.content.Context; +import android.graphics.Point; import android.os.Process; +import android.os.SystemClock; import android.util.Log; +import android.view.KeyEvent; +import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; +import androidx.test.uiautomator.By; +import androidx.test.uiautomator.BySelector; import androidx.test.uiautomator.UiDevice; +import androidx.test.uiautomator.UiObject2; +import androidx.test.uiautomator.Until; import com.android.launcher3.LauncherAppWidgetProviderInfo; +import com.android.launcher3.R; import com.android.launcher3.compat.AppWidgetManagerCompat; import com.android.launcher3.testcomponent.AppWidgetNoConfig; import com.android.launcher3.testcomponent.AppWidgetWithConfig; @@ -37,6 +50,21 @@ import java.util.function.Function; public class TestViewHelpers { private static final String TAG = "TestViewHelpers"; + private static UiDevice getDevice() { + return UiDevice.getInstance(getInstrumentation()); + } + + public static UiObject2 findViewById(int id) { + return getDevice().wait(Until.findObject(getSelectorForId(id)), + AbstractLauncherUiTest.DEFAULT_UI_TIMEOUT); + } + + public static BySelector getSelectorForId(int id) { + final Context targetContext = getTargetContext(); + String name = targetContext.getResources().getResourceEntryName(id); + return By.res(targetContext.getPackageName(), name); + } + /** * Finds a widget provider which can fit on the home screen. * @@ -63,6 +91,92 @@ public class TestViewHelpers { return info; } + /** + * Drags an icon to the center of homescreen. + * + * @param icon object that is either app icon or shortcut icon + */ + public static void dragToWorkspace(UiObject2 icon, boolean expectedToShowShortcuts) { + Point center = icon.getVisibleCenter(); + + // Action Down + final long downTime = SystemClock.uptimeMillis(); + sendPointer(downTime, MotionEvent.ACTION_DOWN, center); + + UiObject2 dragLayer = findViewById(R.id.drag_layer); + + if (expectedToShowShortcuts) { + // Make sure shortcuts show up, and then move a bit to hide them. + assertNotNull(findViewById(R.id.deep_shortcuts_container)); + + Point moveLocation = new Point(center); + int distanceToMove = + getTargetContext().getResources().getDimensionPixelSize( + R.dimen.deep_shortcuts_start_drag_threshold) + 50; + if (moveLocation.y - distanceToMove >= dragLayer.getVisibleBounds().top) { + moveLocation.y -= distanceToMove; + } else { + moveLocation.y += distanceToMove; + } + movePointer(downTime, center, moveLocation); + + assertNull(findViewById(R.id.deep_shortcuts_container)); + } + + // Wait until Remove/Delete target is visible + assertNotNull(findViewById(R.id.delete_target_text)); + + Point moveLocation = dragLayer.getVisibleCenter(); + + // Move to center + movePointer(downTime, center, moveLocation); + sendPointer(downTime, MotionEvent.ACTION_UP, moveLocation); + + // Wait until remove target is gone. + getDevice().wait(Until.gone(getSelectorForId(R.id.delete_target_text)), + AbstractLauncherUiTest.DEFAULT_UI_TIMEOUT); + } + + private static void movePointer(long downTime, Point from, Point to) { + while (!from.equals(to)) { + try { + Thread.sleep(20); + } catch (InterruptedException e) { + e.printStackTrace(); + } + from.x = getNextMoveValue(to.x, from.x); + from.y = getNextMoveValue(to.y, from.y); + sendPointer(downTime, MotionEvent.ACTION_MOVE, from); + } + } + + private static int getNextMoveValue(int targetValue, int oldValue) { + if (targetValue - oldValue > 10) { + return oldValue + 10; + } else if (targetValue - oldValue < -10) { + return oldValue - 10; + } else { + return targetValue; + } + } + + public static void sendPointer(long downTime, int action, Point point) { + MotionEvent event = MotionEvent.obtain(downTime, + SystemClock.uptimeMillis(), action, point.x, point.y, 0); + getInstrumentation().sendPointerSync(event); + event.recycle(); + } + + /** + * Opens widget tray and returns the recycler view. + */ + public static UiObject2 openWidgetsTray() { + final UiDevice device = getDevice(); + device.pressKeyCode(KeyEvent.KEYCODE_W, KeyEvent.META_CTRL_ON); + device.waitForIdle(); + return findViewById(R.id.widgets_list_view); + } + public static View findChildView(ViewGroup parent, Function condition) { for (int i = 0; i < parent.getChildCount(); ++i) { final View child = parent.getChildAt(i); diff --git a/tests/src/com/android/launcher3/ui/widget/AddConfigWidgetTest.java b/tests/src/com/android/launcher3/ui/widget/AddConfigWidgetTest.java index dc72bda9a3..5eb5f19928 100644 --- a/tests/src/com/android/launcher3/ui/widget/AddConfigWidgetTest.java +++ b/tests/src/com/android/launcher3/ui/widget/AddConfigWidgetTest.java @@ -27,18 +27,20 @@ import android.view.View; import androidx.test.filters.LargeTest; import androidx.test.runner.AndroidJUnit4; +import androidx.test.uiautomator.By; +import androidx.test.uiautomator.UiObject2; import com.android.launcher3.ItemInfo; import com.android.launcher3.LauncherAppWidgetInfo; import com.android.launcher3.LauncherAppWidgetProviderInfo; import com.android.launcher3.Workspace; -import com.android.launcher3.tapl.Widgets; import com.android.launcher3.testcomponent.WidgetConfigActivity; import com.android.launcher3.ui.AbstractLauncherUiTest; import com.android.launcher3.ui.TestViewHelpers; import com.android.launcher3.util.Condition; import com.android.launcher3.util.Wait; import com.android.launcher3.util.rule.ShellCommandRule; +import com.android.launcher3.widget.WidgetCell; import org.junit.Before; import org.junit.Ignore; @@ -69,6 +71,7 @@ public class AddConfigWidgetTest extends AbstractLauncherUiTest { } @Test + // Convert test to TAPL b/131116002 public void testWidgetConfig() throws Throwable { runTest(false, true); } @@ -80,6 +83,7 @@ public class AddConfigWidgetTest extends AbstractLauncherUiTest { } @Test + // Convert test to TAPL b/131116002 public void testConfigCancelled() throws Throwable { runTest(false, false); } @@ -100,13 +104,15 @@ public class AddConfigWidgetTest extends AbstractLauncherUiTest { clearHomescreen(); mActivityMonitor.startLauncher(); - final Widgets widgets = mLauncher.getWorkspace().openAllWidgets(); + // Open widget tray and wait for load complete. + final UiObject2 widgetContainer = TestViewHelpers.openWidgetsTray(); + Wait.atMost(null, Condition.minChildCount(widgetContainer, 2), DEFAULT_UI_TIMEOUT); // Drag widget to homescreen WidgetConfigStartupMonitor monitor = new WidgetConfigStartupMonitor(); - widgets. - getWidget(mWidgetInfo.getLabel(mTargetContext.getPackageManager())). - dragToWorkspace(); + UiObject2 widget = scrollAndFind(widgetContainer, By.clazz(WidgetCell.class) + .hasDescendant(By.text(mWidgetInfo.getLabel(mTargetContext.getPackageManager())))); + TestViewHelpers.dragToWorkspace(widget, false); // Widget id for which the config activity was opened mWidgetId = monitor.getWidgetId(); diff --git a/tests/src/com/android/launcher3/ui/widget/AddWidgetTest.java b/tests/src/com/android/launcher3/ui/widget/AddWidgetTest.java index 4529a80d1d..0061568cce 100644 --- a/tests/src/com/android/launcher3/ui/widget/AddWidgetTest.java +++ b/tests/src/com/android/launcher3/ui/widget/AddWidgetTest.java @@ -19,12 +19,20 @@ import static org.junit.Assert.assertTrue; import androidx.test.filters.LargeTest; import androidx.test.runner.AndroidJUnit4; +import androidx.test.uiautomator.By; +import androidx.test.uiautomator.UiObject2; +import android.view.View; +import com.android.launcher3.ItemInfo; import com.android.launcher3.LauncherAppWidgetInfo; import com.android.launcher3.LauncherAppWidgetProviderInfo; +import com.android.launcher3.Workspace.ItemOperator; import com.android.launcher3.ui.AbstractLauncherUiTest; import com.android.launcher3.ui.TestViewHelpers; +import com.android.launcher3.util.Condition; +import com.android.launcher3.util.Wait; import com.android.launcher3.util.rule.ShellCommandRule; +import com.android.launcher3.widget.WidgetCell; import org.junit.Ignore; import org.junit.Rule; @@ -53,6 +61,7 @@ public class AddWidgetTest extends AbstractLauncherUiTest { performTest(); } + // Convert to TAPL b/131116002 private void performTest() throws Throwable { clearHomescreen(); mActivityMonitor.startLauncher(); @@ -60,15 +69,22 @@ public class AddWidgetTest extends AbstractLauncherUiTest { final LauncherAppWidgetProviderInfo widgetInfo = TestViewHelpers.findWidgetProvider(this, false /* hasConfigureScreen */); - mLauncher. - getWorkspace(). - openAllWidgets(). - getWidget(widgetInfo.getLabel(mTargetContext.getPackageManager())). - dragToWorkspace(); + // Open widget tray and wait for load complete. + final UiObject2 widgetContainer = TestViewHelpers.openWidgetsTray(); + Wait.atMost(null, Condition.minChildCount(widgetContainer, 2), DEFAULT_UI_TIMEOUT); - assertTrue(mActivityMonitor.itemExists( - (info, view) -> info instanceof LauncherAppWidgetInfo && + // Drag widget to homescreen + UiObject2 widget = scrollAndFind(widgetContainer, By.clazz(WidgetCell.class) + .hasDescendant(By.text(widgetInfo.getLabel(mTargetContext.getPackageManager())))); + TestViewHelpers.dragToWorkspace(widget, false); + + assertTrue(mActivityMonitor.itemExists(new ItemOperator() { + @Override + public boolean evaluate(ItemInfo info, View view) { + return info instanceof LauncherAppWidgetInfo && ((LauncherAppWidgetInfo) info).providerName.getClassName().equals( - widgetInfo.provider.getClassName())).call()); + widgetInfo.provider.getClassName()); + } + }).call()); } } diff --git a/tests/tapl/com/android/launcher3/tapl/Launchable.java b/tests/tapl/com/android/launcher3/tapl/Launchable.java index 04b8019450..d4bdafa764 100644 --- a/tests/tapl/com/android/launcher3/tapl/Launchable.java +++ b/tests/tapl/com/android/launcher3/tapl/Launchable.java @@ -68,7 +68,7 @@ abstract class Launchable { /** * Drags an object to the center of homescreen. */ - public void dragToWorkspace() { + public Workspace dragToWorkspace() { final Point launchableCenter = getObject().getVisibleCenter(); final Point displaySize = mLauncher.getRealDisplaySize(); final int width = displaySize.x / 2; @@ -80,6 +80,10 @@ abstract class Launchable { launchableCenter.x - width / 2 : launchableCenter.x + width / 2, displaySize.y / 2), getLongPressIndicator()); + try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer( + "dragged launchable to workspace")) { + return new Workspace(mLauncher); + } } protected abstract String getLongPressIndicator(); diff --git a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java index 1641d702d3..a7e6336199 100644 --- a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java +++ b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java @@ -614,12 +614,12 @@ public final class LauncherInstrumentation { @NonNull UiObject2 waitForLauncherObject(BySelector selector) { - return waitForObjectBySelector(By.copy(selector).pkg(getLauncherPackageName())); + return waitForObjectBySelector(selector.pkg(getLauncherPackageName())); } @NonNull UiObject2 tryWaitForLauncherObject(BySelector selector, long timeout) { - return tryWaitForObjectBySelector(By.copy(selector).pkg(getLauncherPackageName()), timeout); + return tryWaitForObjectBySelector(selector.pkg(getLauncherPackageName()), timeout); } @NonNull diff --git a/tests/tapl/com/android/launcher3/tapl/Widget.java b/tests/tapl/com/android/launcher3/tapl/Widget.java index 1b6d8c4a6e..128789dbc1 100644 --- a/tests/tapl/com/android/launcher3/tapl/Widget.java +++ b/tests/tapl/com/android/launcher3/tapl/Widget.java @@ -18,16 +18,7 @@ package com.android.launcher3.tapl; import androidx.test.uiautomator.UiObject2; -/** - * Widget in workspace or a widget list. - */ -public final class Widget extends Launchable { - Widget(LauncherInstrumentation launcher, UiObject2 icon) { - super(launcher, icon); - } - - @Override - protected String getLongPressIndicator() { - return "drop_target_bar"; +public class Widget { + Widget(LauncherInstrumentation launcher, UiObject2 widget) { } } diff --git a/tests/tapl/com/android/launcher3/tapl/Widgets.java b/tests/tapl/com/android/launcher3/tapl/Widgets.java index cbc3a815f8..94003be919 100644 --- a/tests/tapl/com/android/launcher3/tapl/Widgets.java +++ b/tests/tapl/com/android/launcher3/tapl/Widgets.java @@ -16,12 +16,6 @@ package com.android.launcher3.tapl; -import static org.junit.Assert.fail; - -import android.graphics.Point; - -import androidx.test.uiautomator.By; -import androidx.test.uiautomator.BySelector; import androidx.test.uiautomator.Direction; import androidx.test.uiautomator.UiObject2; @@ -81,28 +75,4 @@ public final class Widgets extends LauncherInstrumentation.VisibleContainer { protected LauncherInstrumentation.ContainerType getContainerType() { return LauncherInstrumentation.ContainerType.WIDGETS; } - - public Widget getWidget(String label) { - final int margin = ResourceUtils.getNavbarSize( - ResourceUtils.NAVBAR_BOTTOM_GESTURE_SIZE, mLauncher.getResources()) + 1; - final UiObject2 widgetsContainer = verifyActiveContainer(); - widgetsContainer.setGestureMargins(0, 0, 0, margin); - - final Point displaySize = mLauncher.getRealDisplaySize(); - - int i = 0; - final BySelector selector = By. - clazz("com.android.launcher3.widget.WidgetCell"). - hasDescendant(By.text(label)); - - for (; ; ) { - final UiObject2 widget = mLauncher.tryWaitForLauncherObject(selector, 300); - if (widget != null && widget.getVisibleBounds().intersects( - 0, 0, displaySize.x, displaySize.y - margin)) { - return new Widget(mLauncher, widget); - } - if (++i > 40) fail("Too many attempts"); - widgetsContainer.scroll(Direction.DOWN, 1f); - } - } }