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

Commit 5e29395e authored by Tony Wickham's avatar Tony Wickham Committed by Android (Google) Code Review
Browse files

Merge "Swipe up on nav bar to go home from -1 and widgets" into ub-launcher3-qt-dev

parents deb7cd54 9791bd15
Loading
Loading
Loading
Loading
+133 −49
Original line number Diff line number Diff line
@@ -15,74 +15,131 @@
 */
package com.android.launcher3.uioverrides.touchcontrollers;

import static android.view.View.TRANSLATION_X;

import static com.android.launcher3.AbstractFloatingView.TYPE_ALL;
import static com.android.launcher3.AbstractFloatingView.TYPE_HIDE_BACK_BUTTON;
import static com.android.launcher3.LauncherState.ALL_APPS;
import static com.android.launcher3.LauncherState.NORMAL;
import static com.android.launcher3.LauncherState.OVERVIEW;
import static com.android.launcher3.allapps.AllAppsTransitionController.ALL_APPS_PROGRESS;
import static com.android.launcher3.anim.Interpolators.DEACCEL_3;
import static com.android.launcher3.touch.AbstractStateChangeTouchController.SUCCESS_TRANSITION_PROGRESS;

import android.animation.Animator;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.view.MotionEvent;
import android.view.View;
import android.view.animation.Interpolator;

import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherState;
import com.android.launcher3.LauncherStateManager.AnimationConfig;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.allapps.AllAppsTransitionController;
import com.android.launcher3.anim.AnimationSuccessListener;
import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.anim.AnimatorSetBuilder;
import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.touch.AbstractStateChangeTouchController;
import com.android.launcher3.touch.SwipeDetector;
import com.android.launcher3.userevent.nano.LauncherLogProto;
import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Command;
import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch;
import com.android.launcher3.util.TouchController;
import com.android.quickstep.views.RecentsView;

/**
 * Handles swiping up on the nav bar to go home from overview or all apps.
 * Handles swiping up on the nav bar to go home from launcher, e.g. overview or all apps.
 */
public class NavBarToHomeTouchController extends AbstractStateChangeTouchController {
public class NavBarToHomeTouchController implements TouchController, SwipeDetector.Listener {

    private static final Interpolator PULLBACK_INTERPOLATOR = DEACCEL_3;

    private final Launcher mLauncher;
    private final SwipeDetector mSwipeDetector;
    private final float mPullbackDistance;

    private boolean mNoIntercept;
    private LauncherState mStartState;
    private LauncherState mEndState = NORMAL;
    private AnimatorPlaybackController mCurrentAnimation;

    public NavBarToHomeTouchController(Launcher launcher) {
        super(launcher, SwipeDetector.VERTICAL);
        mLauncher = launcher;
        mSwipeDetector = new SwipeDetector(mLauncher, this, SwipeDetector.VERTICAL);
        mPullbackDistance = mLauncher.getResources().getDimension(R.dimen.home_pullback_distance);
    }

    @Override
    protected boolean canInterceptTouch(MotionEvent ev) {
    public final boolean onControllerInterceptTouchEvent(MotionEvent ev) {
        if (ev.getAction() == MotionEvent.ACTION_DOWN) {
            mStartState = mLauncher.getStateManager().getState();
            mNoIntercept = !canInterceptTouch(ev);
            if (mNoIntercept) {
                return false;
            }
            mSwipeDetector.setDetectableScrollConditions(SwipeDetector.DIRECTION_POSITIVE, false);
        }

        if (mNoIntercept) {
            return false;
        }

        onControllerTouchEvent(ev);
        return mSwipeDetector.isDraggingOrSettling();
    }

    private boolean canInterceptTouch(MotionEvent ev) {
        boolean cameFromNavBar = (ev.getEdgeFlags() & Utilities.EDGE_NAV_BAR) != 0;
        return cameFromNavBar && (mLauncher.isInState(OVERVIEW) || mLauncher.isInState(ALL_APPS));
        if (!cameFromNavBar) {
            return false;
        }
        if (mStartState == OVERVIEW || mStartState == ALL_APPS) {
            return true;
        }
        if (!mLauncher.hasWindowFocus()) {
            return true;
        }
        if (AbstractFloatingView.getTopOpenView(mLauncher) != null) {
            return true;
        }
        return false;
    }

    @Override
    protected LauncherState getTargetState(LauncherState fromState, boolean isDragTowardPositive) {
        return isDragTowardPositive ? NORMAL : fromState;
    public final boolean onControllerTouchEvent(MotionEvent ev) {
        return mSwipeDetector.onTouchEvent(ev);
    }

    private float getShiftRange() {
        return mLauncher.getDeviceProfile().heightPx;
    }

    @Override
    protected float initCurrentAnimation(int animComponents) {
    public void onDragStart(boolean start) {
        initCurrentAnimation();
    }

    private void initCurrentAnimation() {
        long accuracy = (long) (getShiftRange() * 2);
        final AnimatorSet anim;
        if (mFromState == OVERVIEW) {
            anim = new AnimatorSet();
        final AnimatorSet anim = new AnimatorSet();
        if (mStartState == OVERVIEW) {
            RecentsView recentsView = mLauncher.getOverviewPanel();
            float pullbackDistance = recentsView.getPaddingStart() / 2;
            float pullbackDist = mPullbackDistance;
            if (!recentsView.isRtl()) {
                pullbackDistance = -pullbackDistance;
                pullbackDist = -pullbackDist;
            }
            anim.play(ObjectAnimator.ofFloat(recentsView, View.TRANSLATION_X, pullbackDistance));
            anim.setInterpolator(PULLBACK_INTERPOLATOR);
        } else { // if (mFromState == ALL_APPS)
            Animator pullback = ObjectAnimator.ofFloat(recentsView, TRANSLATION_X, pullbackDist);
            pullback.setInterpolator(PULLBACK_INTERPOLATOR);
            anim.play(pullback);
        } else if (mStartState == ALL_APPS) {
            AnimatorSetBuilder builder = new AnimatorSetBuilder();
            AllAppsTransitionController allAppsController = mLauncher.getAllAppsController();
            final float pullbackDistance = mLauncher.getDeviceProfile().allAppsIconSizePx / 2;
            Animator allAppsProgress = ObjectAnimator.ofFloat(allAppsController, ALL_APPS_PROGRESS,
                    -pullbackDistance / allAppsController.getShiftRange());
                    -mPullbackDistance / allAppsController.getShiftRange());
            allAppsProgress.setInterpolator(PULLBACK_INTERPOLATOR);
            builder.play(allAppsProgress);
            // Slightly fade out all apps content to further distinguish from scrolling.
@@ -90,52 +147,79 @@ public class NavBarToHomeTouchController extends AbstractStateChangeTouchControl
                    .mapToProgress(PULLBACK_INTERPOLATOR, 0, 0.5f));
            AnimationConfig config = new AnimationConfig();
            config.duration = accuracy;
            allAppsController.setAlphas(mToState.getVisibleElements(mLauncher), config, builder);
            anim = builder.build();
            allAppsController.setAlphas(mEndState.getVisibleElements(mLauncher), config, builder);
            anim.play(builder.build());
        }
        AbstractFloatingView topView = AbstractFloatingView.getTopOpenView(mLauncher);
        if (topView != null) {
            Animator hintCloseAnim = topView.createHintCloseAnim(mPullbackDistance);
            if (hintCloseAnim != null) {
                hintCloseAnim.setInterpolator(PULLBACK_INTERPOLATOR);
                anim.play(hintCloseAnim);
            }
        }
        anim.setDuration(accuracy);
        mCurrentAnimation = AnimatorPlaybackController.wrap(anim, accuracy, this::clearState);
        return -1 / getShiftRange();
    }

    private void clearState() {
        mCurrentAnimation = null;
        mSwipeDetector.finishedScrolling();
        mSwipeDetector.setDetectableScrollConditions(0, false);
    }

    @Override
    public void onDragStart(boolean start) {
        super.onDragStart(start);
        mStartContainerType = LauncherLogProto.ContainerType.NAVBAR;
    public boolean onDrag(float displacement) {
        // Only allow swipe up.
        displacement = Math.min(0, displacement);
        float progress = Utilities.getProgress(displacement, 0, getShiftRange());
        mCurrentAnimation.setPlayFraction(progress);
        return true;
    }

    @Override
    public void onDragEnd(float velocity, boolean fling) {
        final int logAction = fling ? Touch.FLING : Touch.SWIPE;
        float interpolatedProgress = PULLBACK_INTERPOLATOR.getInterpolation(
                mCurrentAnimation.getProgressFraction());
        if (interpolatedProgress >= SUCCESS_TRANSITION_PROGRESS || velocity < 0 && fling) {
            mLauncher.getStateManager().goToState(mToState, true,
                    () -> onSwipeInteractionCompleted(mToState, logAction));
        float progress = mCurrentAnimation.getProgressFraction();
        float interpolatedProgress = PULLBACK_INTERPOLATOR.getInterpolation(progress);
        boolean success = interpolatedProgress >= SUCCESS_TRANSITION_PROGRESS
                || (velocity < 0 && fling);
        if (success) {
            mLauncher.getStateManager().goToState(mEndState, true,
                    () -> onSwipeInteractionCompleted(mEndState));
            if (mStartState != mEndState) {
                logStateChange(mStartState.containerType, logAction);
            }
            AbstractFloatingView topOpenView = AbstractFloatingView.getTopOpenView(mLauncher);
            if (topOpenView != null) {
                AbstractFloatingView.closeAllOpenViews(mLauncher);
                logStateChange(topOpenView.getLogContainerType(), logAction);
            }
        } else {
            // Quickly return to the state we came from (we didn't move far).
            AnimatorPlaybackController anim = mLauncher.getStateManager()
                    .createAnimationToNewWorkspace(mFromState, 80);
            anim.setEndAction(() -> onSwipeInteractionCompleted(mFromState, logAction));
            anim.start();
            ValueAnimator anim = mCurrentAnimation.getAnimationPlayer();
            anim.setFloatValues(progress, 0);
            anim.addListener(new AnimationSuccessListener() {
                @Override
                public void onAnimationSuccess(Animator animator) {
                    onSwipeInteractionCompleted(mStartState);
                }
        mCurrentAnimation.dispatchOnCancel();
            });
            anim.setDuration(80).start();
        }

    @Override
    protected int getDirectionForLog() {
        return LauncherLogProto.Action.Direction.UP;
    }

    @Override
    protected boolean goingBetweenNormalAndOverview(LauncherState fromState,
            LauncherState toState) {
        // We don't want to create an atomic animation to/from overview.
        return false;
    private void onSwipeInteractionCompleted(LauncherState targetState) {
        clearState();
        mLauncher.getStateManager().goToState(targetState, false /* animated */);
    }

    @Override
    protected int getLogContainerTypeForNormalState() {
        return LauncherLogProto.ContainerType.NAVBAR;
    private void logStateChange(int startContainerType, int logAction) {
        mLauncher.getUserEventDispatcher().logStateChangeAction(logAction,
                LauncherLogProto.Action.Direction.UP,
                LauncherLogProto.ContainerType.NAVBAR,
                startContainerType,
                mEndState.containerType,
                mLauncher.getWorkspace().getCurrentPage());
    }
}
+3 −0
Original line number Diff line number Diff line
@@ -65,4 +65,7 @@
    <!-- Assistant Gestures -->
    <dimen name="gestures_assistant_size">28dp</dimen>
    <dimen name="gestures_assistant_drag_threshold">70dp</dimen>

    <!-- Distance to move elements when swiping up to go home from launcher -->
    <dimen name="home_pullback_distance">28dp</dimen>
</resources>
+18 −2
Original line number Diff line number Diff line
@@ -19,9 +19,11 @@ package com.android.launcher3;
import static android.view.accessibility.AccessibilityEvent.TYPE_VIEW_FOCUSED;
import static android.view.accessibility.AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED;
import static android.view.accessibility.AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED;

import static com.android.launcher3.compat.AccessibilityManagerCompat.isAccessibilityEnabled;
import static com.android.launcher3.compat.AccessibilityManagerCompat.sendCustomAccessibilityEvent;

import android.animation.Animator;
import android.annotation.SuppressLint;
import android.content.Context;
import android.util.AttributeSet;
@@ -30,7 +32,11 @@ import android.view.MotionEvent;
import android.view.View;
import android.widget.LinearLayout;

import androidx.annotation.IntDef;
import androidx.annotation.Nullable;

import com.android.launcher3.userevent.nano.LauncherLogProto.Action;
import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
import com.android.launcher3.util.TouchController;
import com.android.launcher3.views.ActivityContext;
import com.android.launcher3.views.BaseDragLayer;
@@ -38,8 +44,6 @@ import com.android.launcher3.views.BaseDragLayer;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

import androidx.annotation.IntDef;

/**
 * Base class for a View which shows a floating UI on top of the launcher UI.
 */
@@ -124,8 +128,20 @@ public abstract class AbstractFloatingView extends LinearLayout implements Touch

    protected abstract void handleClose(boolean animate);

    /**
     * Creates a user-controlled animation to hint that the view will be closed if completed.
     * @param distanceToMove The max distance that elements should move from their starting point.
     */
    public @Nullable Animator createHintCloseAnim(float distanceToMove) {
        return null;
    }

    public abstract void logActionCommand(int command);

    public int getLogContainerType() {
        return ContainerType.DEFAULT_CONTAINERTYPE;
    }

    public final boolean isOpen() {
        return mIsOpen;
    }
+6 −1
Original line number Diff line number Diff line
@@ -1406,7 +1406,12 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo
    @Override
    public void logActionCommand(int command) {
        mLauncher.getUserEventDispatcher().logActionCommand(
                command, getFolderIcon(), ContainerType.FOLDER);
                command, getFolderIcon(), getLogContainerType());
    }

    @Override
    public int getLogContainerType() {
        return ContainerType.FOLDER;
    }

    @Override
+6 −1
Original line number Diff line number Diff line
@@ -157,7 +157,12 @@ public class PopupContainerWithArrow extends ArrowPopup implements DragSource,
    @Override
    public void logActionCommand(int command) {
        mLauncher.getUserEventDispatcher().logActionCommand(
                command, mOriginalIcon, ContainerType.DEEPSHORTCUTS);
                command, mOriginalIcon, getLogContainerType());
    }

    @Override
    public int getLogContainerType() {
        return ContainerType.DEEPSHORTCUTS;
    }

    public OnClickListener getItemClickListener() {
Loading