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

Commit 830e4b7c authored by Andy Wickham's avatar Andy Wickham
Browse files

Add long swipe from app to overview gesture (with flag).

High level:
 - As you swipe up from an app (OtherActivityInputConsumer),
   a state transition animation to All Apps is created in
   AnimatorControllerWithResistance. The animation is played
   alongside the Recents resistance animation (i.e. past the
   settling point of Overview, which is at mCurrentShift 1).
 - The actual state transition to All Apps only happens if you
   release your finger in the "all apps region." This is set to
   mCurrentShift 2, so double the distance that Overview rests.
 - A haptic plays whenever you enter or exit this region, and
   the all apps animation is set to 0 until the region is
   active. This is so it's clear that something different is
   happening.
 - The panel that was previously used for tablets is now used
   for phones during this transition. It comes in at full
   opacity when you enter the region, and the contents (apps
   and search suggestions) fade in as you continue swiping.
 - The only gesture that is recognized in the all apps region
   is a fling downwards, which will return you to the previous
   app. Otherwise a left/right/up fling or slow release will
   finish the all apps transition.
 - The threshold is ignored if the flag is disabled (default)
   or if FallbackActivityInterface is active.

Flag:
The threshold is ignored if ENABLE_ALL_APPS_FROM_OVERVIEW is
disabled (default).

Bug: 259619990
Bug: 275132633
Test: Manual with and without the flag enabled
Change-Id: Ie311b77252416d97677b2c56fad61dfd392b6fe8
parent 773c5c17
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -63,5 +63,6 @@ message GestureStateProto {
        RECENTS = 2;
        NEW_TASK = 3;
        LAST_TASK = 4;
        ALL_APPS = 5;
    }
}
+65 −9
Original line number Diff line number Diff line
@@ -39,6 +39,7 @@ import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
import static com.android.launcher3.util.SystemUiController.UI_STATE_FULLSCREEN_TASK;
import static com.android.launcher3.util.VibratorWrapper.OVERVIEW_HAPTIC;
import static com.android.launcher3.util.window.RefreshRateTracker.getSingleFrameMs;
import static com.android.quickstep.GestureState.GestureEndTarget.ALL_APPS;
import static com.android.quickstep.GestureState.GestureEndTarget.HOME;
import static com.android.quickstep.GestureState.GestureEndTarget.LAST_TASK;
import static com.android.quickstep.GestureState.GestureEndTarget.NEW_TASK;
@@ -161,6 +162,9 @@ public abstract class AbsSwipeUpHandler<T extends StatefulActivity<S>,

    private static final ArrayList<String> STATE_NAMES = new ArrayList<>();

    /** Shift distance to transition to All Apps if ENABLE_ALL_APPS_FROM_OVERVIEW. */
    public static final float ALL_APPS_SHIFT_THRESHOLD = 2f;

    protected final BaseActivityInterface<S, T> mActivityInterface;
    protected final InputConsumerProxy mInputConsumerProxy;
    protected final ActivityInitListener mActivityInitListener;
@@ -247,6 +251,8 @@ public abstract class AbsSwipeUpHandler<T extends StatefulActivity<S>,
            getNextStateFlag("STATE_CURRENT_TASK_FINISHED");
    private static final int STATE_FINISH_WITH_NO_END =
            getNextStateFlag("STATE_FINISH_WITH_NO_END");
    private static final int STATE_SETTLED_ON_ALL_APPS =
            getNextStateFlag("STATE_SETTLED_ON_ALL_APPS");

    private static final int LAUNCHER_UI_STATES =
            STATE_LAUNCHER_PRESENT | STATE_LAUNCHER_DRAWN | STATE_LAUNCHER_STARTED |
@@ -299,6 +305,7 @@ public abstract class AbsSwipeUpHandler<T extends StatefulActivity<S>,
    private boolean mGestureStarted;
    private boolean mLogDirectionUpOrLeft = true;
    private boolean mIsLikelyToStartNewTask;
    private boolean mIsInAllAppsRegion;

    private final long mTouchTimeMs;
    private long mLauncherFrameDrawnTime;
@@ -432,6 +439,9 @@ public abstract class AbsSwipeUpHandler<T extends StatefulActivity<S>,
                this::finishCurrentTransitionToHome);
        mStateCallback.runOnceAtState(STATE_SCALED_CONTROLLER_HOME | STATE_CURRENT_TASK_FINISHED,
                this::reset);
        mStateCallback.runOnceAtState(STATE_SETTLED_ON_ALL_APPS | STATE_SCREENSHOT_CAPTURED
                        | STATE_GESTURE_COMPLETED,
                this::finishCurrentTransitionToAllApps);

        mStateCallback.runOnceAtState(STATE_LAUNCHER_PRESENT | STATE_APP_CONTROLLER_RECEIVED
                        | STATE_LAUNCHER_DRAWN | STATE_SCALED_CONTROLLER_RECENTS
@@ -681,8 +691,10 @@ public abstract class AbsSwipeUpHandler<T extends StatefulActivity<S>,
                maybeUpdateRecentsAttachedState(true/* animate */, true/* moveRunningTask */);
                Optional.ofNullable(mActivityInterface.getTaskbarController())
                        .ifPresent(TaskbarUIController::startTranslationSpring);
                if (!mIsInAllAppsRegion) {
                    performHapticFeedback();
                }
            }

            @Override
            public void onMotionPauseChanged(boolean isPaused) {
@@ -695,7 +707,7 @@ public abstract class AbsSwipeUpHandler<T extends StatefulActivity<S>,
        maybeUpdateRecentsAttachedState(true /* animate */);
    }

    private void maybeUpdateRecentsAttachedState(boolean animate) {
    protected void maybeUpdateRecentsAttachedState(boolean animate) {
        maybeUpdateRecentsAttachedState(animate, false /* moveRunningTask */);
    }

@@ -716,7 +728,9 @@ public abstract class AbsSwipeUpHandler<T extends StatefulActivity<S>,
                ? mRecentsAnimationTargets.findTask(mGestureState.getRunningTaskId())
                : null;
        final boolean recentsAttachedToAppWindow;
        if (mGestureState.getEndTarget() != null) {
        if (mIsInAllAppsRegion) {
            recentsAttachedToAppWindow = false;
        } else if (mGestureState.getEndTarget() != null) {
            recentsAttachedToAppWindow = mGestureState.getEndTarget().recentsAttachedToAppWindow;
        } else if (mContinuingLastGesture
                && mRecentsView.getRunningTaskIndex() != mRecentsView.getNextPage()) {
@@ -772,6 +786,26 @@ public abstract class AbsSwipeUpHandler<T extends StatefulActivity<S>,
        }
    }

    /**
     * Update whether user is currently dragging in a region that will trigger all apps.
     */
    private void setIsInAllAppsRegion(boolean isInAllAppsRegion) {
        if (mIsInAllAppsRegion == isInAllAppsRegion
                || !mActivityInterface.allowAllAppsFromOverview()) {
            return;
        }
        mIsInAllAppsRegion = isInAllAppsRegion;

        // Newly entering or exiting the zone - do haptic and animate recent tasks.
        VibratorWrapper.INSTANCE.get(mContext).vibrate(OVERVIEW_HAPTIC);
        maybeUpdateRecentsAttachedState(true);

        // Draw active task below Launcher so that All Apps can appear over it.
        runActionOnRemoteHandles(remoteTargetHandle ->
                remoteTargetHandle.getTaskViewSimulator().setDrawsBelowRecents(isInAllAppsRegion));
    }


    private void buildAnimationController() {
        if (!canCreateNewOrUpdateExistingLauncherTransitionController()) {
            return;
@@ -792,10 +826,15 @@ public abstract class AbsSwipeUpHandler<T extends StatefulActivity<S>,
    @Override
    public WindowInsets onApplyWindowInsets(View view, WindowInsets windowInsets) {
        WindowInsets result = view.onApplyWindowInsets(windowInsets);
        // Don't rebuild animation when we are animating the IME, because it will cause a loop
        // where the insets change -> animation changes (updating ime) -> insets change -> ...
        if (windowInsets.isVisible(WindowInsets.Type.ime())) {
            return result;
        }
        buildAnimationController();
        // Reapply the current shift to ensure it takes new insets into account, e.g. when long
        // pressing to stash taskbar without moving the finger.
        updateFinalShift();
        onCurrentShiftUpdated();
        return result;
    }

@@ -822,7 +861,8 @@ public abstract class AbsSwipeUpHandler<T extends StatefulActivity<S>,
     */
    @UiThread
    @Override
    public void updateFinalShift() {
    public void onCurrentShiftUpdated() {
        setIsInAllAppsRegion(mCurrentShift.value >= ALL_APPS_SHIFT_THRESHOLD);
        updateSysUiFlags(mCurrentShift.value);
        applyScrollAndTransform();

@@ -1085,6 +1125,9 @@ public abstract class AbsSwipeUpHandler<T extends StatefulActivity<S>,
        }

        switch (endTarget) {
            case ALL_APPS:
                mStateCallback.setState(STATE_SETTLED_ON_ALL_APPS | STATE_CAPTURE_SCREENSHOT);
                break;
            case HOME:
                mStateCallback.setState(STATE_SCALED_CONTROLLER_HOME | STATE_CAPTURE_SCREENSHOT);
                // Notify the SysUI to use fade-in animation when entering PiP
@@ -1173,6 +1216,9 @@ public abstract class AbsSwipeUpHandler<T extends StatefulActivity<S>,
        final boolean willGoToNewTask =
                isScrollingToNewTask() && Math.abs(velocity.x) > Math.abs(endVelocity);
        final boolean isSwipeUp = endVelocity < 0;
        if (mIsInAllAppsRegion) {
            return isSwipeUp ? ALL_APPS : LAST_TASK;
        }
        if (!isSwipeUp) {
            final boolean isCenteredOnNewTask =
                    mRecentsView.getDestinationPage() != mRecentsView.getRunningTaskIndex();
@@ -1188,7 +1234,9 @@ public abstract class AbsSwipeUpHandler<T extends StatefulActivity<S>,
        // Fully gestural mode.
        final boolean isFlingX = Math.abs(velocity.x) > mContext.getResources()
                .getDimension(R.dimen.quickstep_fling_threshold_speed);
        if (isScrollingToNewTask && isFlingX) {
        if (mIsInAllAppsRegion) {
            return ALL_APPS;
        } else if (isScrollingToNewTask && isFlingX) {
            // Flinging towards new task takes precedence over mIsMotionPaused (which only
            // checks y-velocity).
            return NEW_TASK;
@@ -1236,7 +1284,8 @@ public abstract class AbsSwipeUpHandler<T extends StatefulActivity<S>,
        mGestureState.setEndTarget(endTarget, false /* isAtomic */);
        mAnimationFactory.setEndTarget(endTarget);

        float endShift = endTarget.isLauncher ? 1 : 0;
        float endShift = endTarget == ALL_APPS ? mDragLengthFactor
                : endTarget.isLauncher ? 1 : 0;
        final float startShift;
        if (!isFling) {
            long expectedDuration = Math.abs(Math.round((endShift - currentShift)
@@ -1793,6 +1842,12 @@ public abstract class AbsSwipeUpHandler<T extends StatefulActivity<S>,
        reset();
    }

    @UiThread
    private void finishCurrentTransitionToAllApps() {
        finishCurrentTransitionToHome();
        reset();
    }

    private void reset() {
        mStateCallback.setStateOnUiThread(STATE_HANDLER_INVALIDATED);
        if (mActivity != null) {
@@ -1926,7 +1981,8 @@ public abstract class AbsSwipeUpHandler<T extends StatefulActivity<S>,
    private boolean updateThumbnail(int runningTaskId, boolean refreshView) {
        boolean finishTransitionPosted = false;
        final TaskView taskView;
        if (mGestureState.getEndTarget() == HOME || mGestureState.getEndTarget() == NEW_TASK) {
        if (mGestureState.getEndTarget() == HOME || mGestureState.getEndTarget() == NEW_TASK
                || mGestureState.getEndTarget() == ALL_APPS) {
            // Capture the screenshot before finishing the transition to home or quickswitching to
            // ensure it's taken in the correct orientation, but no need to update the thumbnail.
            taskView = null;
@@ -2072,7 +2128,7 @@ public abstract class AbsSwipeUpHandler<T extends StatefulActivity<S>,

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

+3 −0
Original line number Diff line number Diff line
@@ -187,6 +187,9 @@ public abstract class BaseActivityInterface<STATE_TYPE extends BaseState<STATE_T

    public abstract boolean allowMinimizeSplitScreen();

    /** @return whether to allow going to All Apps from Overview. */
    public abstract boolean allowAllAppsFromOverview();

    public boolean deferStartingActivity(RecentsAnimationDeviceState deviceState, MotionEvent ev) {
        return deviceState.isInDeferredGestureRegion(ev) || deviceState.isImeRenderingNavButtons()
                || isTrackpadMultiFingerSwipe(ev);
+6 −0
Original line number Diff line number Diff line
@@ -138,6 +138,11 @@ public final class FallbackActivityInterface extends
        return false;
    }

    @Override
    public boolean allowAllAppsFromOverview() {
        return false;
    }

    @Override
    public boolean deferStartingActivity(RecentsAnimationDeviceState deviceState, MotionEvent ev) {
        // In non-gesture mode, user might be clicking on the home button which would directly
@@ -196,6 +201,7 @@ public final class FallbackActivityInterface extends
            case LAST_TASK:
                return BACKGROUND_APP;
            case HOME:
            case ALL_APPS:
            default:
                return HOME;
        }
+8 −1
Original line number Diff line number Diff line
@@ -18,11 +18,13 @@ package com.android.quickstep;
import static com.android.launcher3.MotionEventsUtils.isTrackpadFourFingerSwipe;
import static com.android.launcher3.MotionEventsUtils.isTrackpadMultiFingerSwipe;
import static com.android.launcher3.MotionEventsUtils.isTrackpadThreeFingerSwipe;
import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_ALLAPPS;
import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_BACKGROUND;
import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_HOME;
import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_OVERVIEW;
import static com.android.quickstep.MultiStateCallback.DEBUG_STATES;
import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.SET_END_TARGET;
import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.SET_END_TARGET_ALL_APPS;
import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.SET_END_TARGET_HOME;
import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.SET_END_TARGET_NEW_TASK;

@@ -68,7 +70,9 @@ public class GestureState implements RecentsAnimationCallbacks.RecentsAnimationL
                GestureStateProto.GestureEndTarget.NEW_TASK),

        LAST_TASK(false, LAUNCHER_STATE_BACKGROUND, true,
                GestureStateProto.GestureEndTarget.LAST_TASK);
                GestureStateProto.GestureEndTarget.LAST_TASK),

        ALL_APPS(true, LAUNCHER_STATE_ALLAPPS, false, GestureStateProto.GestureEndTarget.ALL_APPS);

        GestureEndTarget(boolean isLauncher, int containerType, boolean recentsAttachedToAppWindow,
                GestureStateProto.GestureEndTarget protoEndTarget) {
@@ -385,6 +389,9 @@ public class GestureState implements RecentsAnimationCallbacks.RecentsAnimationL
            case NEW_TASK:
                ActiveGestureLog.INSTANCE.trackEvent(SET_END_TARGET_NEW_TASK);
                break;
            case ALL_APPS:
                ActiveGestureLog.INSTANCE.trackEvent(SET_END_TARGET_ALL_APPS);
                break;
            case LAST_TASK:
            case RECENTS:
            default:
Loading