Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit c39db732 authored by Sunny Goyal's avatar Sunny Goyal
Browse files

New spring overscroll in pagedView

> Using EdgeEffect in PagedView for overscroll
> Keeping RecentsView overscroll similar to S, by created a custom EdgeEffect

Bug: 183966408
Test: Manual
Change-Id: Ia547cf14ea315468b12a4ff5f389ac2d2beceafa
parent b99105b9
Loading
Loading
Loading
Loading
+11 −5
Original line number Diff line number Diff line
@@ -71,6 +71,7 @@ import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnApplyWindowInsetsListener;
import android.view.ViewTreeObserver.OnDrawListener;
import android.view.ViewTreeObserver.OnScrollChangedListener;
import android.view.WindowInsets;
import android.view.animation.Interpolator;
import android.widget.Toast;
@@ -139,6 +140,8 @@ public abstract class AbsSwipeUpHandler<T extends StatefulActivity<S>,
    private final Handler mHandler = new Handler();
    // Callbacks to be made once the recents animation starts
    private final ArrayList<Runnable> mRecentsAnimationStartCallbacks = new ArrayList<>();
    private final OnScrollChangedListener mOnRecentsScrollListener = this::onRecentsViewScroll;

    protected RecentsAnimationController mRecentsAnimationController;
    protected RecentsAnimationTargets mRecentsAnimationTargets;
    protected T mActivity;
@@ -1368,6 +1371,7 @@ public abstract class AbsSwipeUpHandler<T extends StatefulActivity<S>,
    private void invalidateHandlerWithLauncher() {
        endLauncherTransitionController();

        mRecentsView.removeOnScrollChangedListener(mOnRecentsScrollListener);
        mRecentsView.onGestureAnimationEnd();
        resetLauncherListeners();
    }
@@ -1560,17 +1564,19 @@ public abstract class AbsSwipeUpHandler<T extends StatefulActivity<S>,
                    mRecentsAnimationTargets.addReleaseCheck(applier));
        });

        mRecentsView.setOnScrollChangeListener((v, scrollX, scrollY, oldScrollX, oldScrollY) -> {
            if (moveWindowWithRecentsScroll()) {
                updateFinalShift();
            }
        });
        mRecentsView.addOnScrollChangedListener(mOnRecentsScrollListener);
        runOnRecentsAnimationStart(() ->
                mRecentsView.setRecentsAnimationTargets(mRecentsAnimationController,
                        mRecentsAnimationTargets));
        mRecentsViewScrollLinked = true;
    }

    private void onRecentsViewScroll() {
        if (moveWindowWithRecentsScroll()) {
            updateFinalShift();
        }
    }

    protected void startNewTask(Consumer<Boolean> resultCallback) {
        // Launch the task user scrolled to (mRecentsView.getNextPage()).
        if (!mCanceled) {
+133 −25
Original line number Diff line number Diff line
@@ -42,6 +42,7 @@ import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCH
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASK_DISMISS_SWIPE_UP;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASK_LAUNCH_SWIPE_DOWN;
import static com.android.launcher3.statehandlers.DepthController.DEPTH;
import static com.android.launcher3.touch.PagedOrientationHandler.CANVAS_TRANSLATE;
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
import static com.android.launcher3.util.SystemUiController.UI_STATE_OVERVIEW;
@@ -88,11 +89,13 @@ import android.view.MotionEvent;
import android.view.View;
import android.view.ViewDebug;
import android.view.ViewGroup;
import android.view.ViewTreeObserver.OnScrollChangedListener;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityNodeInfo;
import android.view.animation.Interpolator;
import android.widget.FrameLayout;
import android.widget.ListView;
import android.widget.OverScroller;

import androidx.annotation.Nullable;
import androidx.annotation.UiThread;
@@ -116,14 +119,15 @@ import com.android.launcher3.statehandlers.DepthController;
import com.android.launcher3.statemanager.BaseState;
import com.android.launcher3.statemanager.StatefulActivity;
import com.android.launcher3.testing.TestProtocol;
import com.android.launcher3.touch.OverScroll;
import com.android.launcher3.touch.PagedOrientationHandler;
import com.android.launcher3.util.DynamicResource;
import com.android.launcher3.util.IntSet;
import com.android.launcher3.util.MultiValueAlpha;
import com.android.launcher3.util.OverScroller;
import com.android.launcher3.util.ResourceBasedOverride.Overrides;
import com.android.launcher3.util.SplitConfigurationOptions.SplitPositionOption;
import com.android.launcher3.util.Themes;
import com.android.launcher3.util.TranslateEdgeEffect;
import com.android.launcher3.util.ViewPool;
import com.android.quickstep.AnimatedFloat;
import com.android.quickstep.BaseActivityInterface;
@@ -157,6 +161,7 @@ import com.android.systemui.shared.system.TaskStackChangeListeners;
import com.android.wm.shell.pip.IPipAnimationListener;

import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;

/**
@@ -296,6 +301,9 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T
                }
            };

    // OverScroll constants
    private static final int OVERSCROLL_PAGE_SNAP_ANIMATION_DURATION = 270;

    protected final RecentsOrientedState mOrientationState;
    protected final BaseActivityInterface<STATE_TYPE, ACTIVITY_TYPE> mSizeStrategy;
    protected RecentsAnimationController mRecentsAnimationController;
@@ -314,6 +322,8 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T
    protected final Rect mTempRect = new Rect();
    protected final RectF mTempRectF = new RectF();
    private final PointF mTempPointF = new PointF();
    private final float[] mTempFloat = new float[1];
    private final List<OnScrollChangedListener> mScrollListeners = new ArrayList<>();
    private float mFullscreenScale;

    private static final int DISMISS_TASK_DURATION = 300;
@@ -360,6 +370,9 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T

    IntSet mTopIdSet = new IntSet();

    private int mOverScrollShift = 0;


    /**
     * TODO: Call reloadIdNeeded in onTaskStackChanged.
     */
@@ -583,6 +596,72 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T
        return mIsRtl;
    }

    @Override
    protected void initEdgeEffect() {
        mEdgeGlowLeft = new TranslateEdgeEffect(getContext());
        mEdgeGlowRight = new TranslateEdgeEffect(getContext());
    }

    @Override
    protected void drawEdgeEffect(Canvas canvas) {
        // Do not draw edge effect
    }

    @Override
    protected void dispatchDraw(Canvas canvas) {
        // Draw overscroll
        if (mAllowOverScroll && (!mEdgeGlowRight.isFinished() || !mEdgeGlowLeft.isFinished())) {
            final int restoreCount = canvas.save();
            final int width = getWidth();
            final int height = getHeight();
            int primarySize = mOrientationHandler.getPrimaryValue(width, height);
            int secondarySize = mOrientationHandler.getSecondaryValue(width, height);

            float effectiveShift = 0;
            if (!mEdgeGlowLeft.isFinished()) {
                mEdgeGlowLeft.setSize(secondarySize, primarySize);
                if (((TranslateEdgeEffect) mEdgeGlowLeft).getTranslationShift(mTempFloat)) {
                    effectiveShift = mTempFloat[0];
                    postInvalidateOnAnimation();
                }
            }
            if (!mEdgeGlowRight.isFinished()) {
                mEdgeGlowRight.setSize(secondarySize, primarySize);
                if (((TranslateEdgeEffect) mEdgeGlowRight).getTranslationShift(mTempFloat)) {
                    effectiveShift -= mTempFloat[0];
                    postInvalidateOnAnimation();
                }
            }

            int scroll = OverScroll.dampedScroll(effectiveShift * primarySize, primarySize);
            mOrientationHandler.set(canvas, CANVAS_TRANSLATE, scroll);

            if (mOverScrollShift != scroll) {
                mOverScrollShift = scroll;
                dispatchScrollChanged();
            }

            super.dispatchDraw(canvas);
            canvas.restoreToCount(restoreCount);
        } else {
            if (mOverScrollShift != 0) {
                mOverScrollShift = 0;
                dispatchScrollChanged();
            }
            super.dispatchDraw(canvas);
        }
        if (LIVE_TILE.get() && mEnableDrawingLiveTile && mLiveTileParams.getTargetSet() != null) {
            redrawLiveTile();
        }
    }

    /**
     * Returns the view shift due to overscroll
     */
    public int getOverScrollShift() {
        return mOverScrollShift;
    }

    @Override
    public Task onTaskThumbnailChanged(int taskId, ThumbnailData thumbnailData) {
        if (mHandleTaskStackChanges) {
@@ -936,8 +1015,30 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T
    }

    @Override
    protected boolean snapToPageInFreeScroll() {
        return !showAsGrid();
    protected void onNotSnappingToPageInFreeScroll() {
        int finalPos = mScroller.getFinalX();
        if (!showAsGrid() && finalPos > mMinScroll && finalPos < mMaxScroll) {
            int firstPageScroll = getScrollForPage(!mIsRtl ? 0 : getPageCount() - 1);
            int lastPageScroll = getScrollForPage(!mIsRtl ? getPageCount() - 1 : 0);

            // If scrolling ends in the half of the added space that is closer to
            // the end, settle to the end. Otherwise snap to the nearest page.
            // If flinging past one of the ends, don't change the velocity as it
            // will get stopped at the end anyway.
            int pageSnapped = finalPos < (firstPageScroll + mMinScroll) / 2
                    ? mMinScroll
                    : finalPos > (lastPageScroll + mMaxScroll) / 2
                            ? mMaxScroll
                            : getScrollForPage(mNextPage);

            mScroller.setFinalX(pageSnapped);
            // Ensure the scroll/snap doesn't happen too fast;
            int extraScrollDuration = OVERSCROLL_PAGE_SNAP_ANIMATION_DURATION
                    - mScroller.getDuration();
            if (extraScrollDuration > 0) {
                mScroller.extendDuration(extraScrollDuration);
            }
        }
    }

    @Override
@@ -1267,12 +1368,6 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T

        // Update the high res thumbnail loader state
        mModel.getThumbnailCache().getHighResLoadingState().setFlingingFast(isFlingingFast);

        mLiveTileTaskViewSimulator.setScroll(getScrollOffset());
        if (LIVE_TILE.get() && mEnableDrawingLiveTile
                && mLiveTileParams.getTargetSet() != null) {
            redrawLiveTile();
        }
        return scrolling;
    }

@@ -1571,7 +1666,6 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T
            updateOrientationHandler();
        }

        setOnScrollChangeListener(null);
        setEnableFreeScroll(true);
        setEnableDrawingLiveTile(true);
        if (!LIVE_TILE.get()) {
@@ -3207,13 +3301,6 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T
        return mClearAllButton;
    }

    @Override
    protected boolean onOverscroll(int amount) {
        // overscroll should only be accepted on -1 direction (for clear all button)
        if ((amount > 0 && !mIsRtl) || (amount < 0 && mIsRtl)) return false;
        return super.onOverscroll(amount);
    }

    /**
     * @return How many pixels the running task is offset on the currently laid out dominant axis.
     */
@@ -3228,14 +3315,8 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T
        if (pageIndex == -1) {
            return 0;
        }
        // Unbound the scroll (due to overscroll) if the adjacent tasks are offset away from it.
        // This allows the page to move freely, given there's no visual indication why it shouldn't.
        int boundedScroll = mOrientationHandler.getPrimaryScroll(this);
        int unboundedScroll = getUnboundedScroll();
        float unboundedProgress = mAdjacentPageOffset;
        int scroll = Math.round(unboundedScroll * unboundedProgress
                + boundedScroll * (1 - unboundedProgress));
        return getScrollForPage(pageIndex) - scroll;
        return getScrollForPage(pageIndex) - mOrientationHandler.getPrimaryScroll(this)
                + getOverScrollShift();
    }

    /**
@@ -3421,6 +3502,33 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T
        void onEmptyMessageUpdated(boolean isEmpty);
    }

    /**
     * Adds a listener for scroll changes
     */
    public void addOnScrollChangedListener(OnScrollChangedListener listener) {
        mScrollListeners.add(listener);
    }

    /**
     * Removes a previously added scroll change listener
     */
    public void removeOnScrollChangedListener(OnScrollChangedListener listener) {
        mScrollListeners.remove(listener);
    }

    @Override
    protected void onScrollChanged(int l, int t, int oldl, int oldt) {
        super.onScrollChanged(l, t, oldl, oldt);
        dispatchScrollChanged();
    }

    private void dispatchScrollChanged() {
        mLiveTileTaskViewSimulator.setScroll(getScrollOffset());
        for (int i = mScrollListeners.size() - 1; i >= 0; i--) {
            mScrollListeners.get(i).onScrollChanged();
        }
    }

    private static class PinnedStackAnimationListener<T extends BaseActivity> extends
            IPipAnimationListener.Stub {
        private T mActivity;
+9 −7
Original line number Diff line number Diff line
@@ -121,7 +121,7 @@ public class TaskMenuView extends AbstractFloatingView implements OnScrollChange
        };
    }

    private void setPosition(float x, float y) {
    private void setPosition(float x, float y, int overscrollShift) {
        PagedOrientationHandler pagedOrientationHandler = mTaskView.getPagedOrientationHandler();
        int taskTopMargin = mActivity.getDeviceProfile().overviewTaskThumbnailTopMarginPx;
        float adjustedY = y + taskTopMargin;
@@ -136,8 +136,9 @@ public class TaskMenuView extends AbstractFloatingView implements OnScrollChange
            setPivotY(0);
        }
        setRotation(pagedOrientationHandler.getDegreesRotated());
        setX(pagedOrientationHandler.getTaskMenuX(x, mTaskView.getThumbnail()));
        setY(pagedOrientationHandler.getTaskMenuY(adjustedY, mTaskView.getThumbnail()));
        setX(pagedOrientationHandler.getTaskMenuX(x, mTaskView.getThumbnail(), overscrollShift));
        setY(pagedOrientationHandler.getTaskMenuY(
                adjustedY, mTaskView.getThumbnail(), overscrollShift));
    }

    public void onRotationChanged() {
@@ -169,14 +170,15 @@ public class TaskMenuView extends AbstractFloatingView implements OnScrollChange
            return false;
        }
        post(this::animateOpen);
        mActivity.getRootView().getViewTreeObserver().addOnScrollChangedListener(this);
        ((RecentsView) mActivity.getOverviewPanel()).addOnScrollChangedListener(this);
        return true;
    }

    @Override
    public void onScrollChanged() {
        RecentsView rv = mTaskView.getRecentsView();
        setPosition(mTaskView.getX() - rv.getScrollX(), mTaskView.getY() - rv.getScrollY());
        setPosition(mTaskView.getX() - rv.getScrollX(), mTaskView.getY() - rv.getScrollY(),
                rv.getOverScrollShift());
    }

    /** @return true if successfully able to populate task view menu, false otherwise */
@@ -236,7 +238,7 @@ public class TaskMenuView extends AbstractFloatingView implements OnScrollChange
            .mOrientationState.isRecentsActivityRotationAllowed();
        mOptionLayout.setOrientation(orientationHandler
                .getTaskMenuLayoutOrientation(canActivityRotate, mOptionLayout));
        setPosition(sTempRect.left - insets.left, sTempRect.top - insets.top);
        setPosition(sTempRect.left - insets.left, sTempRect.top - insets.top, 0);
    }

    private void animateOpen() {
@@ -282,7 +284,7 @@ public class TaskMenuView extends AbstractFloatingView implements OnScrollChange
    private void closeComplete() {
        mIsOpen = false;
        mActivity.getDragLayer().removeView(this);
        mActivity.getRootView().getViewTreeObserver().removeOnScrollChangedListener(this);
        ((RecentsView) mActivity.getOverviewPanel()).removeOnScrollChangedListener(this);
    }

    private RoundedRectRevealOutlineProvider createOpenCloseOutlineProvider() {
+153 −250

File changed.

Preview size limit exceeded, changes collapsed.

+13 −73
Original line number Diff line number Diff line
@@ -24,7 +24,6 @@ import static com.android.launcher3.LauncherState.FLAG_WORKSPACE_ICONS_CAN_BE_DR
import static com.android.launcher3.LauncherState.FLAG_WORKSPACE_INACCESSIBLE;
import static com.android.launcher3.LauncherState.HINT_STATE;
import static com.android.launcher3.LauncherState.NORMAL;
import static com.android.launcher3.LauncherState.OVERVIEW;
import static com.android.launcher3.LauncherState.SPRING_LOADED;
import static com.android.launcher3.config.FeatureFlags.ADAPTIVE_ICON_WINDOW_ANIM;
import static com.android.launcher3.dragndrop.DragLayer.ALPHA_INDEX_OVERLAY;
@@ -96,10 +95,12 @@ import com.android.launcher3.statemanager.StateManager;
import com.android.launcher3.statemanager.StateManager.StateHandler;
import com.android.launcher3.states.StateAnimationConfig;
import com.android.launcher3.touch.WorkspaceTouchListener;
import com.android.launcher3.util.EdgeEffectCompat;
import com.android.launcher3.util.Executors;
import com.android.launcher3.util.IntArray;
import com.android.launcher3.util.IntSparseArrayMap;
import com.android.launcher3.util.ItemInfoMatcher;
import com.android.launcher3.util.OverlayEdgeEffect;
import com.android.launcher3.util.PackageUserKey;
import com.android.launcher3.util.Thunk;
import com.android.launcher3.util.WallpaperOffsetInterpolator;
@@ -245,10 +246,7 @@ public class Workspace extends PagedView<WorkspacePageIndicator>
    private float mTransitionProgress;

    // State related to Launcher Overlay
    LauncherOverlay mLauncherOverlay;
    boolean mScrollInteractionBegan;
    boolean mStartedSendingScrollEvents;
    float mLastOverlayScroll = 0;
    private OverlayEdgeEffect mOverlayEdgeEffect;
    boolean mOverlayShown = false;
    private Runnable mOnOverlayHiddenCallback;

@@ -945,47 +943,25 @@ public class Workspace extends PagedView<WorkspacePageIndicator>
        }
    }

    protected void onScrollInteractionBegin() {
        super.onScrollInteractionBegin();
        mScrollInteractionBegan = true;
    }

    protected void onScrollInteractionEnd() {
        super.onScrollInteractionEnd();
        mScrollInteractionBegan = false;
        if (mStartedSendingScrollEvents) {
            mStartedSendingScrollEvents = false;
            mLauncherOverlay.onScrollInteractionEnd();
        }
    }

    public void setLauncherOverlay(LauncherOverlay overlay) {
        mLauncherOverlay = overlay;
        // A new overlay has been set. Reset event tracking
        mStartedSendingScrollEvents = false;
        mOverlayEdgeEffect = overlay == null ? null : new OverlayEdgeEffect(getContext(), overlay);
        EdgeEffectCompat newEffect = overlay == null
                ? new EdgeEffectCompat(getContext()) : mOverlayEdgeEffect;
        if (mIsRtl) {
            mEdgeGlowRight = newEffect;
        } else {
            mEdgeGlowLeft = newEffect;
        }
        onOverlayScrollChanged(0);
    }

    public boolean hasOverlay() {
        return mLauncherOverlay != null;
    }

    private boolean isScrollingOverlay() {
        return mLauncherOverlay != null &&
                ((mIsRtl && getUnboundedScroll() > mMaxScroll)
                        || (!mIsRtl && getUnboundedScroll() < mMinScroll));
        return mOverlayEdgeEffect != null;
    }

    @Override
    protected void snapToDestination() {
        // If we're overscrolling the overlay, we make sure to immediately reset the PagedView
        // to it's baseline position instead of letting the overscroll settle. The overlay handles
        // it's own settling, and every gesture to the overlay should be self-contained and start
        // from 0, so we zero it out here.
        if (isScrollingOverlay()) {
            // We reset mWasInOverscroll so that PagedView doesn't zero out the overscroll
            // interaction when we call snapToPageImmediately.
            mWasInOverscroll = false;
        if (mOverlayEdgeEffect != null && !mOverlayEdgeEffect.isFinished()) {
            snapToPageImmediately(0);
        } else {
            super.snapToDestination();
@@ -1017,38 +993,6 @@ public class Workspace extends PagedView<WorkspacePageIndicator>
        }
    }

    @Override
    protected void overScroll(int amount) {
        boolean shouldScrollOverlay = mLauncherOverlay != null && !mScroller.isSpringing() &&
                ((amount <= 0 && !mIsRtl) || (amount >= 0 && mIsRtl));

        boolean shouldZeroOverlay = mLauncherOverlay != null && mLastOverlayScroll != 0 &&
                ((amount >= 0 && !mIsRtl) || (amount <= 0 && mIsRtl));

        if (shouldScrollOverlay) {
            if (!mStartedSendingScrollEvents && mScrollInteractionBegan) {
                mStartedSendingScrollEvents = true;
                mLauncherOverlay.onScrollInteractionBegin();
            }

            mLastOverlayScroll = Math.abs(((float) amount) / getMeasuredWidth());
            mLauncherOverlay.onScrollChange(mLastOverlayScroll, mIsRtl);
        } else {
            dampedOverScroll(amount);
        }

        if (shouldZeroOverlay) {
            mLauncherOverlay.onScrollChange(0, mIsRtl);
        }
    }

    @Override
    protected boolean onOverscroll(int amount) {
        // Enforce overscroll on -1 direction
        if ((amount > 0 && !mIsRtl) || (amount < 0 && mIsRtl)) return false;
        return super.onOverscroll(amount);
    }

    @Override
    protected boolean shouldFlingForVelocity(int velocityX) {
        // When the overlay is moving, the fling or settle transition is controlled by the overlay.
@@ -1377,10 +1321,6 @@ public class Workspace extends PagedView<WorkspacePageIndicator>
        mOutlineProvider = outlineProvider;
    }

    public void snapToPageFromOverView(int whichPage) {
        snapToPage(whichPage, OVERVIEW.getTransitionDuration(mLauncher), Interpolators.ZOOM_IN);
    }

    private void onStartStateTransition(LauncherState state) {
        mIsSwitchingState = true;
        mTransitionProgress = 0;
Loading