From bb44c03b1c45c18483121ace5d5b986419f78b6e Mon Sep 17 00:00:00 2001 From: Schneider Victor-tulias Date: Thu, 7 Apr 2022 15:25:00 -0700 Subject: [PATCH 1/2] Fix janky overview animation. Switching to overview shortly after switching to 3-button mode caused a janky animation if the PagedView wasn't properly initialized yet. Moved the animation to a callback. Fixes: 203632659 Fixes: 223719200 Test: manual Change-Id: I8a345036c6b7322ae3fa50a23bcb7522f57c8a90 --- .../android/quickstep/AbsSwipeUpHandler.java | 17 ++++++- .../android/quickstep/views/RecentsView.java | 15 +++--- src/com/android/launcher3/PagedView.java | 49 ++++++++++++++----- 3 files changed, 62 insertions(+), 19 deletions(-) diff --git a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java index 5acce899f9..13389c0c09 100644 --- a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java +++ b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java @@ -22,6 +22,7 @@ import static android.widget.Toast.LENGTH_SHORT; import static com.android.launcher3.BaseActivity.INVISIBLE_BY_STATE_HANDLER; import static com.android.launcher3.BaseActivity.STATE_HANDLER_INVISIBILITY_FLAGS; +import static com.android.launcher3.PagedView.INVALID_PAGE; import static com.android.launcher3.anim.Interpolators.ACCEL_DEACCEL; import static com.android.launcher3.anim.Interpolators.DEACCEL; import static com.android.launcher3.anim.Interpolators.OVERSHOOT_1_2; @@ -66,6 +67,7 @@ import android.graphics.RectF; import android.os.Build; import android.os.IBinder; import android.os.SystemClock; +import android.util.Log; import android.view.MotionEvent; import android.view.View; import android.view.View.OnApplyWindowInsetsListener; @@ -926,7 +928,13 @@ public abstract class AbsSwipeUpHandler, mLogDirectionUpOrLeft = velocity.x < 0; } mDownPos = downPos; - handleNormalGestureEnd(endVelocity, isFling, velocity, false /* isCancel */); + Runnable handleNormalGestureEndCallback = () -> + handleNormalGestureEnd(endVelocity, isFling, velocity, /* isCancel= */ false); + if (mRecentsView != null) { + mRecentsView.runOnPageScrollsInitialized(handleNormalGestureEndCallback); + } else { + handleNormalGestureEndCallback.run(); + } } private void endRunningWindowAnim(boolean cancel) { @@ -1130,6 +1138,13 @@ public abstract class AbsSwipeUpHandler, } else if (endTarget == RECENTS) { if (mRecentsView != null) { int nearestPage = mRecentsView.getDestinationPage(); + if (nearestPage == INVALID_PAGE) { + // Allow the snap to invalid page to catch future error cases. + Log.e(TAG, + "RecentsView destination page is invalid", + new IllegalStateException()); + } + boolean isScrolling = false; if (mRecentsView.getNextPage() != nearestPage) { // We shouldn't really scroll to the next page when swiping up to recents. diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java index 224c1f6c75..83c78c3b0c 100644 --- a/quickstep/src/com/android/quickstep/views/RecentsView.java +++ b/quickstep/src/com/android/quickstep/views/RecentsView.java @@ -946,7 +946,7 @@ public abstract class RecentsView extends ViewGrou private boolean mAllowEasyFling; protected PagedOrientationHandler mOrientationHandler = PagedOrientationHandler.PORTRAIT; - protected int[] mPageScrolls; + private final ArrayList mOnPageScrollsInitializedCallbacks = new ArrayList<>(); + + // We should always check pageScrollsInitialized() is true when using mPageScrolls. + @Nullable protected int[] mPageScrolls = null; private boolean mIsBeingDragged; // The amount of movement to begin scrolling @@ -684,14 +687,37 @@ public abstract class PagedView extends ViewGrou setMeasuredDimension(widthSize, heightSize); } + /** Returns true iff this PagedView's scroll amounts are initialized to each page index. */ + protected boolean pageScrollsInitialized() { + return mPageScrolls != null && mPageScrolls.length == getChildCount(); + } + + /** + * Queues the given callback to be run once {@code mPageScrolls} has been initialized. + */ + public void runOnPageScrollsInitialized(Runnable callback) { + mOnPageScrollsInitializedCallbacks.add(callback); + if (pageScrollsInitialized()) { + onPageScrollsInitialized(); + } + } + + private void onPageScrollsInitialized() { + for (Runnable callback : mOnPageScrollsInitializedCallbacks) { + callback.run(); + } + mOnPageScrollsInitializedCallbacks.clear(); + } + @SuppressLint("DrawAllocation") @Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { mIsLayoutValid = true; final int childCount = getChildCount(); + int[] pageScrolls = mPageScrolls; boolean pageScrollChanged = false; - if (mPageScrolls == null || childCount != mPageScrolls.length) { - mPageScrolls = new int[childCount]; + if (!pageScrollsInitialized()) { + pageScrolls = new int[childCount]; pageScrollChanged = true; } @@ -701,10 +727,8 @@ public abstract class PagedView extends ViewGrou if (DEBUG) Log.d(TAG, "PagedView.onLayout()"); - boolean isScrollChanged = getPageScrolls(mPageScrolls, true, SIMPLE_SCROLL_LOGIC); - if (isScrollChanged) { - pageScrollChanged = true; - } + pageScrollChanged |= getPageScrolls(pageScrolls, true, SIMPLE_SCROLL_LOGIC); + mPageScrolls = pageScrolls; final LayoutTransition transition = getLayoutTransition(); // If the transition is running defer updating max scroll, as some empty pages could @@ -738,6 +762,7 @@ public abstract class PagedView extends ViewGrou if (mScroller.isFinished() && pageScrollChanged) { setCurrentPage(getNextPage()); } + onPageScrollsInitialized(); } /** @@ -849,8 +874,10 @@ public abstract class PagedView extends ViewGrou @Override public void onViewRemoved(View child) { super.onViewRemoved(child); - mCurrentPage = validateNewPage(mCurrentPage); - mCurrentScrollOverPage = mCurrentPage; + runOnPageScrollsInitialized(() -> { + mCurrentPage = validateNewPage(mCurrentPage); + mCurrentScrollOverPage = mCurrentPage; + }); dispatchPageCountChanged(); } @@ -1153,7 +1180,7 @@ public abstract class PagedView extends ViewGrou } public int getScrollForPage(int index) { - if (mPageScrolls == null || index >= mPageScrolls.length || index < 0) { + if (!pageScrollsInitialized() || index >= mPageScrolls.length || index < 0) { return 0; } else { return mPageScrolls[index]; @@ -1163,7 +1190,7 @@ public abstract class PagedView extends ViewGrou // While layout transitions are occurring, a child's position may stray from its baseline // position. This method returns the magnitude of this stray at any given time. public int getLayoutTransitionOffsetForPage(int index) { - if (mPageScrolls == null || index >= mPageScrolls.length || index < 0) { + if (!pageScrollsInitialized() || index >= mPageScrolls.length || index < 0) { return 0; } else { View child = getChildAt(index); From 9f922d3598ad506da90dce8ad294bae023812dbe Mon Sep 17 00:00:00 2001 From: Schneider Victor-tulias Date: Wed, 27 Apr 2022 16:39:29 -0700 Subject: [PATCH 2/2] Fix recents animation flicker The recents animation can sometimes start before launcher has started when started with 3-button mode. This causes the animation to start before the recents UI has been initialized by Launcher. Added a callback to properly synchronize launcher start and the recents animation. Fixes: 229360539 Test: started the recents animation and checked logs (order of operations), when the recents animation or launcher started first Change-Id: I5938bbba778a2886b4a1e4ee4844eaf28c6fac0e --- .../quickstep/BaseActivityInterface.java | 20 ++++++++++++++++++- .../quickstep/FallbackActivityInterface.java | 2 +- .../quickstep/LauncherActivityInterface.java | 2 +- .../quickstep/OverviewCommandHelper.java | 3 ++- src/com/android/launcher3/Launcher.java | 7 +++++-- 5 files changed, 28 insertions(+), 6 deletions(-) diff --git a/quickstep/src/com/android/quickstep/BaseActivityInterface.java b/quickstep/src/com/android/quickstep/BaseActivityInterface.java index 48127c04e2..6745246b89 100644 --- a/quickstep/src/com/android/quickstep/BaseActivityInterface.java +++ b/quickstep/src/com/android/quickstep/BaseActivityInterface.java @@ -84,6 +84,8 @@ public abstract class BaseActivityInterface callback) { notifyRecentsOfOrientation(deviceState.getRotationTouchHelper()); DefaultAnimationFactory factory = new DefaultAnimationFactory(callback); - factory.initUI(); + factory.initBackgroundStateUI(); return factory; } diff --git a/quickstep/src/com/android/quickstep/LauncherActivityInterface.java b/quickstep/src/com/android/quickstep/LauncherActivityInterface.java index 10a3a2ed2c..c13b95f2ab 100644 --- a/quickstep/src/com/android/quickstep/LauncherActivityInterface.java +++ b/quickstep/src/com/android/quickstep/LauncherActivityInterface.java @@ -135,7 +135,7 @@ public final class LauncherActivityInterface extends } }; - BaseQuickstepLauncher launcher = factory.initUI(); + BaseQuickstepLauncher launcher = factory.initBackgroundStateUI(); // Since all apps is not visible, we can safely reset the scroll position. // This ensures then the next swipe up to all-apps starts from scroll 0. launcher.getAppsView().reset(false /* animate */); diff --git a/quickstep/src/com/android/quickstep/OverviewCommandHelper.java b/quickstep/src/com/android/quickstep/OverviewCommandHelper.java index 42f9eb607a..dffdc5a641 100644 --- a/quickstep/src/com/android/quickstep/OverviewCommandHelper.java +++ b/quickstep/src/com/android/quickstep/OverviewCommandHelper.java @@ -217,7 +217,8 @@ public class OverviewCommandHelper { @Override public void onRecentsAnimationStart(RecentsAnimationController controller, RecentsAnimationTargets targets) { - interactionHandler.onGestureEnded(0, new PointF(), new PointF()); + activityInterface.runOnInitBackgroundStateUI(() -> + interactionHandler.onGestureEnded(0, new PointF(), new PointF())); cmd.removeListener(this); } diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index 1c62ded7ab..d55134e3dd 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -1280,13 +1280,16 @@ public class Launcher extends StatefulActivity * @param info The data structure describing the shortcut. */ View createShortcut(WorkspaceItemInfo info) { - return createShortcut((ViewGroup) mWorkspace.getChildAt(mWorkspace.getCurrentPage()), info); + // This can be called before PagedView#pageScrollsInitialized returns true, so use the + // first page, which we always assume to be present. + return createShortcut((ViewGroup) mWorkspace.getChildAt(0), info); } /** * Creates a view representing a shortcut inflated from the specified resource. * - * @param parent The group the shortcut belongs to. + * @param parent The group the shortcut belongs to. This is not necessarily the group where + * the shortcut should be added. * @param info The data structure describing the shortcut. * @return A View inflated from layoutResId. */