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

Commit 0618d689 authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge changes from topic "state-supplier" into ub-launcher3-master

* changes:
  Translate recents slightly while dragging after pausing
  Two-zone model: swipe up from nav bar vs above it
  Change LauncherState to Supplier<LauncherState> in tests
parents 5f8a1ab7 fd239caf
Loading
Loading
Loading
Loading
+6 −1
Original line number Diff line number Diff line
@@ -37,6 +37,7 @@ import com.android.launcher3.popup.SystemShortcut;
import com.android.launcher3.uioverrides.touchcontrollers.FlingAndHoldTouchController;
import com.android.launcher3.uioverrides.touchcontrollers.LandscapeEdgeSwipeController;
import com.android.launcher3.uioverrides.touchcontrollers.NavBarToHomeTouchController;
import com.android.launcher3.uioverrides.touchcontrollers.NoButtonNavbarToOverviewTouchController;
import com.android.launcher3.uioverrides.touchcontrollers.NoButtonQuickSwitchTouchController;
import com.android.launcher3.uioverrides.touchcontrollers.OverviewToAllAppsTouchController;
import com.android.launcher3.uioverrides.touchcontrollers.PortraitStatesTouchController;
@@ -224,7 +225,11 @@ public class QuickstepLauncher extends BaseQuickstepLauncher {
        if (mode == NO_BUTTON) {
            list.add(new NoButtonQuickSwitchTouchController(this));
            list.add(new NavBarToHomeTouchController(this));
            if (FeatureFlags.ENABLE_OVERVIEW_ACTIONS.get()) {
                list.add(new NoButtonNavbarToOverviewTouchController(this));
            } else {
                list.add(new FlingAndHoldTouchController(this));
            }
        } else {
            if (getDeviceProfile().isVerticalBarLayout()) {
                list.add(new OverviewToAllAppsTouchController(this));
+20 −6
Original line number Diff line number Diff line
@@ -30,11 +30,13 @@ 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.config.FeatureFlags.ENABLE_OVERVIEW_ACTIONS;
import static com.android.launcher3.logging.LoggerUtils.newContainerTarget;
import static com.android.launcher3.states.RotationHelper.REQUEST_ROTATE;

import android.graphics.Rect;
import android.view.View;
import android.view.animation.Interpolator;

import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.DeviceProfile;
@@ -44,7 +46,6 @@ 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.config.FeatureFlags;
import com.android.launcher3.userevent.nano.LauncherLogProto.Action;
import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
import com.android.quickstep.SysUINavigationMode;
@@ -115,6 +116,15 @@ public class OverviewState extends LauncherState {
        return new ScaleAndTranslation(1f, 0f, 0f);
    }

    @Override
    public ScaleAndTranslation getQsbScaleAndTranslation(Launcher launcher) {
        if (this == OVERVIEW && ENABLE_OVERVIEW_ACTIONS.get()) {
            // Treat the QSB as part of the hotseat so they move together.
            return getHotseatScaleAndTranslation(launcher);
        }
        return super.getQsbScaleAndTranslation(launcher);
    }

    @Override
    public void onStateEnabled(Launcher launcher) {
        AbstractFloatingView.closeAllOpenViews(launcher);
@@ -141,7 +151,7 @@ public class OverviewState extends LauncherState {
        if (launcher.getDeviceProfile().isVerticalBarLayout()) {
            return VERTICAL_SWIPE_INDICATOR | RECENTS_CLEAR_ALL_BUTTON;
        } else {
            if (FeatureFlags.ENABLE_OVERVIEW_ACTIONS.get()) {
            if (ENABLE_OVERVIEW_ACTIONS.get()) {
                return VERTICAL_SWIPE_INDICATOR | RECENTS_CLEAR_ALL_BUTTON;
            }

@@ -195,9 +205,10 @@ public class OverviewState extends LauncherState {
    @Override
    public void prepareForAtomicAnimation(Launcher launcher, LauncherState fromState,
            AnimatorSetBuilder builder) {
        if (fromState == NORMAL && this == OVERVIEW) {
        if ((fromState == NORMAL || fromState == HINT_STATE) && this == OVERVIEW) {
            if (SysUINavigationMode.getMode(launcher) == SysUINavigationMode.Mode.NO_BUTTON) {
                builder.setInterpolator(ANIM_WORKSPACE_SCALE, ACCEL);
                builder.setInterpolator(ANIM_WORKSPACE_SCALE,
                        fromState == NORMAL ? ACCEL : OVERSHOOT_1_2);
                builder.setInterpolator(ANIM_WORKSPACE_TRANSLATE, ACCEL);
            } else {
                builder.setInterpolator(ANIM_WORKSPACE_SCALE, OVERSHOOT_1_2);
@@ -210,8 +221,11 @@ public class OverviewState extends LauncherState {
            }
            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_TRANSLATE_Y, OVERSHOOT_1_7);
            Interpolator translationInterpolator = ENABLE_OVERVIEW_ACTIONS.get()
                    ? OVERSHOOT_1_2
                    : OVERSHOOT_1_7;
            builder.setInterpolator(ANIM_OVERVIEW_TRANSLATE_X, translationInterpolator);
            builder.setInterpolator(ANIM_OVERVIEW_TRANSLATE_Y, translationInterpolator);
            builder.setInterpolator(ANIM_OVERVIEW_FADE, OVERSHOOT_1_2);
        }
    }
+46 −39
Original line number Diff line number Diff line
@@ -60,7 +60,7 @@ public class FlingAndHoldTouchController extends PortraitStatesTouchController {
    private static final long PEEK_OUT_ANIM_DURATION = 100;
    private static final float MAX_DISPLACEMENT_PERCENT = 0.75f;

    private final MotionPauseDetector mMotionPauseDetector;
    protected final MotionPauseDetector mMotionPauseDetector;
    private final float mMotionPauseMinDisplacement;
    private final float mMotionPauseMaxDisplacement;

@@ -85,7 +85,11 @@ public class FlingAndHoldTouchController extends PortraitStatesTouchController {
        super.onDragStart(start);

        if (handlingOverviewAnim()) {
            mMotionPauseDetector.setOnMotionPauseListener(isPaused -> {
            mMotionPauseDetector.setOnMotionPauseListener(this::onMotionPauseChanged);
        }
    }

    protected void onMotionPauseChanged(boolean isPaused) {
        RecentsView recentsView = mLauncher.getOverviewPanel();
        recentsView.setOverviewStateEnabled(isPaused);
        if (mPeekAnim != null) {
@@ -107,15 +111,13 @@ public class FlingAndHoldTouchController extends PortraitStatesTouchController {

        mLauncher.getDragLayer().getScrim().animateToSysuiMultiplier(isPaused ? 0 : 1,
                peekDuration, 0);
            });
        }
    }

    /**
     * @return Whether we are handling the overview animation, rather than
     * having it as part of the existing animation to the target state.
     */
    private boolean handlingOverviewAnim() {
    protected boolean handlingOverviewAnim() {
        int stateFlags = SystemUiProxy.INSTANCE.get(mLauncher).getLastSystemUiStateFlags();
        return mStartState == NORMAL && (stateFlags & SYSUI_STATE_OVERVIEW_DISABLED) == 0;
    }
@@ -162,7 +164,8 @@ public class FlingAndHoldTouchController extends PortraitStatesTouchController {
    @Override
    public boolean onDrag(float displacement, MotionEvent event) {
        float upDisplacement = -displacement;
        mMotionPauseDetector.setDisallowPause(upDisplacement < mMotionPauseMinDisplacement
        mMotionPauseDetector.setDisallowPause(!handlingOverviewAnim()
                || upDisplacement < mMotionPauseMinDisplacement
                || upDisplacement > mMotionPauseMaxDisplacement);
        mMotionPauseDetector.addPosition(displacement, event.getEventTime());
        return super.onDrag(displacement, event);
@@ -171,6 +174,19 @@ public class FlingAndHoldTouchController extends PortraitStatesTouchController {
    @Override
    public void onDragEnd(float velocity) {
        if (mMotionPauseDetector.isPaused() && handlingOverviewAnim()) {
            goToOverviewOnDragEnd(velocity);
        } else {
            super.onDragEnd(velocity);
        }

        View searchView = mLauncher.getAppsView().getSearchView();
        if (searchView instanceof FeedbackHandler) {
            ((FeedbackHandler) searchView).resetFeedback();
        }
        mMotionPauseDetector.clear();
    }

    protected void goToOverviewOnDragEnd(float velocity) {
        if (mPeekAnim != null) {
            mPeekAnim.cancel();
        }
@@ -184,15 +200,6 @@ public class FlingAndHoldTouchController extends PortraitStatesTouchController {
            }
        });
        overviewAnim.start();
        } else {
            super.onDragEnd(velocity);
        }

        View searchView = mLauncher.getAppsView().getSearchView();
        if (searchView instanceof FeedbackHandler) {
            ((FeedbackHandler) searchView).resetFeedback();
        }
        mMotionPauseDetector.clear();
    }

    @Override
+208 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2020 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.uioverrides.touchcontrollers;

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.LauncherStateManager.ATOMIC_OVERVIEW_PEEK_COMPONENT;
import static com.android.launcher3.Utilities.EDGE_NAV_BAR;
import static com.android.launcher3.anim.Interpolators.ACCEL_DEACCEL;
import static com.android.launcher3.util.VibratorWrapper.OVERVIEW_HAPTIC;

import android.animation.Animator;
import android.animation.AnimatorSet;
import android.animation.ValueAnimator;
import android.graphics.PointF;
import android.view.MotionEvent;

import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherState;
import com.android.launcher3.LauncherStateManager;
import com.android.launcher3.Utilities;
import com.android.launcher3.anim.AnimationSuccessListener;
import com.android.launcher3.anim.AnimatorSetBuilder;
import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch;
import com.android.launcher3.util.VibratorWrapper;
import com.android.quickstep.util.StaggeredWorkspaceAnim;
import com.android.quickstep.views.RecentsView;

/**
 * Touch controller which handles swipe and hold from the nav bar to go to Overview. Swiping above
 * the nav bar falls back to go to All Apps. Swiping from the nav bar without holding goes to the
 * first home screen instead of to Overview.
 */
public class NoButtonNavbarToOverviewTouchController extends FlingAndHoldTouchController {


    // How much of the movement to use for translating overview after swipe and hold.
    private static final float OVERVIEW_MOVEMENT_FACTOR = 0.25f;
    private static final long TRANSLATION_ANIM_MIN_DURATION_MS = 80;
    private static final float TRANSLATION_ANIM_VELOCITY_DP_PER_MS = 0.8f;

    private final RecentsView mRecentsView;

    private boolean mDidTouchStartInNavBar;
    private boolean mReachedOverview;
    // The last recorded displacement before we reached overview.
    private PointF mStartDisplacement = new PointF();

    public NoButtonNavbarToOverviewTouchController(Launcher l) {
        super(l);
        mRecentsView = l.getOverviewPanel();
    }

    @Override
    protected boolean canInterceptTouch(MotionEvent ev) {
        mDidTouchStartInNavBar = (ev.getEdgeFlags() & EDGE_NAV_BAR) != 0;
        return super.canInterceptTouch(ev);
    }

    @Override
    protected LauncherState getTargetState(LauncherState fromState, boolean isDragTowardPositive) {
        if (fromState == NORMAL && mDidTouchStartInNavBar) {
            return HINT_STATE;
        } else if (fromState == OVERVIEW && isDragTowardPositive) {
            // Don't allow swiping up to all apps.
            return OVERVIEW;
        }
        return super.getTargetState(fromState, isDragTowardPositive);
    }

    @Override
    protected float initCurrentAnimation(int animComponents) {
        float progressMultiplier = super.initCurrentAnimation(animComponents);
        if (mToState == HINT_STATE) {
            // Track the drag across the entire height of the screen.
            progressMultiplier = -1 / getShiftRange();
        }
        return progressMultiplier;
    }

    @Override
    public void onDragStart(boolean start) {
        super.onDragStart(start);

        mReachedOverview = false;
    }

    @Override
    protected void updateSwipeCompleteAnimation(ValueAnimator animator, long expectedDuration,
            LauncherState targetState, float velocity, boolean isFling) {
        super.updateSwipeCompleteAnimation(animator, expectedDuration, targetState, velocity,
                isFling);
        if (targetState == HINT_STATE) {
            // Normally we compute the duration based on the velocity and distance to the given
            // state, but since the hint state tracks the entire screen without a clear endpoint, we
            // need to manually set the duration to a reasonable value.
            animator.setDuration(HINT_STATE.transitionDuration);
        }
    }

    @Override
    protected void onMotionPauseChanged(boolean isPaused) {
        if (mCurrentAnimation == null) {
            return;
        }
        mCurrentAnimation.dispatchOnCancelWithoutCancelRunnable(() -> {
            mLauncher.getStateManager().goToState(OVERVIEW, true, () -> {
                mReachedOverview = true;
                maybeSwipeInteractionToOverviewComplete();
            });
        });
        VibratorWrapper.INSTANCE.get(mLauncher).vibrate(OVERVIEW_HAPTIC);
    }

    private void maybeSwipeInteractionToOverviewComplete() {
        if (mReachedOverview && mDetector.isSettlingState()) {
            onSwipeInteractionCompleted(OVERVIEW, Touch.SWIPE);
        }
    }

    @Override
    protected boolean handlingOverviewAnim() {
        return mDidTouchStartInNavBar && super.handlingOverviewAnim();
    }

    @Override
    public boolean onDrag(float yDisplacement, float xDisplacement, MotionEvent event) {
        if (mMotionPauseDetector.isPaused()) {
            if (!mReachedOverview) {
                mStartDisplacement.set(xDisplacement, yDisplacement);
            } else {
                mRecentsView.setTranslationX((xDisplacement - mStartDisplacement.x)
                        * OVERVIEW_MOVEMENT_FACTOR);
                mRecentsView.setTranslationY((yDisplacement - mStartDisplacement.y)
                        * OVERVIEW_MOVEMENT_FACTOR);
            }
            // Stay in Overview.
            return true;
        }
        return super.onDrag(yDisplacement, xDisplacement, event);
    }

    @Override
    protected void goToOverviewOnDragEnd(float velocity) {
        float velocityDp = dpiFromPx(velocity);
        boolean isFling = Math.abs(velocityDp) > 1;
        LauncherStateManager stateManager = mLauncher.getStateManager();
        if (isFling) {
            // When flinging, go back to home instead of overview.
            if (velocity > 0) {
                stateManager.goToState(NORMAL, true,
                        () -> onSwipeInteractionCompleted(NORMAL, Touch.FLING));
            } else {
                StaggeredWorkspaceAnim staggeredWorkspaceAnim = new StaggeredWorkspaceAnim(
                        mLauncher, velocity, false /* animateOverviewScrim */);
                staggeredWorkspaceAnim.start();

                // StaggeredWorkspaceAnim doesn't animate overview, so we handle it here.
                stateManager.cancelAnimation();
                AnimatorSetBuilder builder = new AnimatorSetBuilder();
                long duration = OVERVIEW.transitionDuration;
                AnimatorSet anim = stateManager.createAtomicAnimation(
                        stateManager.getState(), NORMAL, builder,
                        ATOMIC_OVERVIEW_PEEK_COMPONENT, duration);
                anim.addListener(new AnimationSuccessListener() {
                    @Override
                    public void onAnimationSuccess(Animator animator) {
                        onSwipeInteractionCompleted(NORMAL, Touch.SWIPE);
                    }
                });
                anim.start();
            }
        } else {
            if (mReachedOverview) {
                float distanceDp = dpiFromPx(Math.max(
                        Math.abs(mRecentsView.getTranslationX()),
                        Math.abs(mRecentsView.getTranslationY())));
                long duration = (long) Math.max(TRANSLATION_ANIM_MIN_DURATION_MS,
                        distanceDp / TRANSLATION_ANIM_VELOCITY_DP_PER_MS);
                mRecentsView.animate()
                        .translationX(0)
                        .translationY(0)
                        .setInterpolator(ACCEL_DEACCEL)
                        .setDuration(duration)
                        .withEndAction(this::maybeSwipeInteractionToOverviewComplete);
            }
        }
    }

    private float dpiFromPx(float pixels) {
        return Utilities.dpiFromPx(pixels, mLauncher.getResources().getDisplayMetrics());
    }
}
+7 −1
Original line number Diff line number Diff line
@@ -62,6 +62,7 @@ import com.android.launcher3.Utilities;
import com.android.launcher3.allapps.AllAppsTransitionController;
import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.anim.AnimatorSetBuilder;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.graphics.OverviewScrim;
import com.android.launcher3.touch.BaseSwipeDetector;
import com.android.launcher3.touch.BothAxesSwipeDetector;
@@ -181,6 +182,12 @@ public class NoButtonQuickSwitchTouchController implements TouchController,

    @Override
    public void onMotionPauseChanged(boolean isPaused) {
        VibratorWrapper.INSTANCE.get(mLauncher).vibrate(OVERVIEW_HAPTIC);

        if (FeatureFlags.ENABLE_OVERVIEW_ACTIONS.get()) {
            return;
        }

        ShelfAnimState shelfState = isPaused ? PEEK : HIDE;
        if (shelfState == PEEK) {
            // Some shelf elements (e.g. qsb) were hidden, but we need them visible when peeking.
@@ -197,7 +204,6 @@ public class NoButtonQuickSwitchTouchController implements TouchController,
        }
        mShelfPeekAnim.setShelfState(shelfState, ShelfPeekAnim.INTERPOLATOR,
                ShelfPeekAnim.DURATION);
        VibratorWrapper.INSTANCE.get(mLauncher).vibrate(OVERVIEW_HAPTIC);
    }

    private void setupAnimators() {
Loading