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

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

Merge "Swipe interaction changes on home screen" into ub-launcher3-master

parents e2b9a2a0 7a80b59e
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -89,7 +89,7 @@ public class AllAppsState extends LauncherState {
    }

    @Override
    public float getOverviewTranslationX(Launcher launcher) {
    public float getOverviewTranslationFactor(Launcher launcher) {
        return 0;
    }

+1 −1
Original line number Diff line number Diff line
@@ -58,7 +58,7 @@ public class OverviewState extends LauncherState {
    }

    @Override
    public float getOverviewTranslationX(Launcher launcher) {
    public float getOverviewTranslationFactor(Launcher launcher) {
        return 0;
    }

+166 −3
Original line number Diff line number Diff line
@@ -18,16 +18,24 @@ package com.android.launcher3.uioverrides;
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.anim.AnimatorSetBuilder.ANIM_OVERVIEW_TRANSLATION;
import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_VERTICAL_PROGRESS;
import static com.android.launcher3.anim.Interpolators.FAST_OUT_SLOW_IN;
import static com.android.launcher3.anim.Interpolators.LINEAR;

import android.animation.TimeInterpolator;
import android.animation.ValueAnimator;
import android.view.MotionEvent;
import android.view.animation.Interpolator;

import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherState;
import com.android.launcher3.anim.AnimatorSetBuilder;
import com.android.launcher3.touch.AbstractStateChangeTouchController;
import com.android.launcher3.touch.SwipeDetector;
import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
import com.android.launcher3.touch.AbstractStateChangeTouchController;
import com.android.quickstep.TouchInteractionService;
import com.android.quickstep.util.SysuiEventLogger;

@@ -36,6 +44,47 @@ import com.android.quickstep.util.SysuiEventLogger;
 */
public class PortraitStatesTouchController extends AbstractStateChangeTouchController {

    private static final float TOTAL_DISTANCE_MULTIPLIER = 2f;
    private static final float LINEAR_SCALE_LIMIT = 1 / TOTAL_DISTANCE_MULTIPLIER;

    // Much be greater than LINEAR_SCALE_LIMIT;
    private static final float MAXIMUM_DISTANCE_FACTOR = 0.9f;

    // Maximum amount to overshoot.
    private static final float MAX_OVERSHOOT = 0.3f;

    private static final double PI_BY_2 = Math.PI / 2;

    private InterpolatorWrapper mAllAppsInterpolatorWrapper = new InterpolatorWrapper();

    // If > 0, the animation progress is clamped at that value as long as user is dragging.
    private float mClampProgressUpdate = -1;

    // If true, we will finish the current animation instantly on second touch.
    private boolean mFinishFastOnSecondTouch;

    private final Interpolator mAllAppsDampedInterpolator = new Interpolator() {

        private final double mAngleMultiplier = Math.PI /
                (2 * (MAXIMUM_DISTANCE_FACTOR - LINEAR_SCALE_LIMIT));

        @Override
        public float getInterpolation(float v) {
            if (v <= LINEAR_SCALE_LIMIT) {
                return v * TOTAL_DISTANCE_MULTIPLIER;
            }
            float overshoot = (v - LINEAR_SCALE_LIMIT);
            return (float) (1 + MAX_OVERSHOOT * Math.sin(overshoot * mAngleMultiplier));
        }
    };

    private final Interpolator mOverviewBoundInterpolator = (v) -> {
            if (v >= MAXIMUM_DISTANCE_FACTOR) {
                return 1;
            }
            return FAST_OUT_SLOW_IN.getInterpolation(v / MAXIMUM_DISTANCE_FACTOR);
    };

    public PortraitStatesTouchController(Launcher l) {
        super(l, SwipeDetector.VERTICAL);
    }
@@ -43,6 +92,11 @@ public class PortraitStatesTouchController extends AbstractStateChangeTouchContr
    @Override
    protected boolean canInterceptTouch(MotionEvent ev) {
        if (mCurrentAnimation != null) {
            if (mFinishFastOnSecondTouch) {
                // TODO: Animate to finish instead.
                mCurrentAnimation.getAnimationPlayer().end();
            }

            // If we are already animating from a previous state, we can intercept.
            return true;
        }
@@ -100,17 +154,49 @@ public class PortraitStatesTouchController extends AbstractStateChangeTouchContr
        }
    }

    private AnimatorSetBuilder getNormalToOverviewAnimation() {
        mAllAppsInterpolatorWrapper.baseInterpolator = mAllAppsDampedInterpolator;

        AnimatorSetBuilder builder = new AnimatorSetBuilder();
        builder.setInterpolator(ANIM_VERTICAL_PROGRESS, mAllAppsInterpolatorWrapper);

        builder.setInterpolator(ANIM_OVERVIEW_TRANSLATION, mOverviewBoundInterpolator);
        return builder;
    }

    @Override
    protected void updateProgress(float fraction) {
        if (mClampProgressUpdate > 0) {
            mCurrentAnimation.setPlayFraction(Math.min(fraction, mClampProgressUpdate));
        } else {
            super.updateProgress(fraction);
        }
    }

    @Override
    protected float initCurrentAnimation() {
        float range = getShiftRange();
        long maxAccuracy = (long) (2 * range);
        mCurrentAnimation = mLauncher.getStateManager()
                .createAnimationToNewWorkspace(mToState, maxAccuracy);

        float startVerticalShift = mFromState.getVerticalProgress(mLauncher) * range;
        float endVerticalShift = mToState.getVerticalProgress(mLauncher) * range;

        float totalShift = endVerticalShift - startVerticalShift;

        final AnimatorSetBuilder builder;

        if (mFromState == NORMAL && mToState == OVERVIEW && totalShift != 0) {
            builder = getNormalToOverviewAnimation();
            totalShift = totalShift * TOTAL_DISTANCE_MULTIPLIER;
            mClampProgressUpdate = MAXIMUM_DISTANCE_FACTOR;
        } else {
            builder = new AnimatorSetBuilder();
            mClampProgressUpdate = -1;
        }

        mCurrentAnimation = mLauncher.getStateManager()
                .createAnimationToNewWorkspace(mToState, builder, maxAccuracy);

        if (totalShift == 0) {
            totalShift = Math.signum(mFromState.ordinal - mToState.ordinal)
                    * OverviewState.getDefaultSwipeHeight(mLauncher);
@@ -118,6 +204,73 @@ public class PortraitStatesTouchController extends AbstractStateChangeTouchContr
        return 1 / totalShift;
    }

    @Override
    protected void updateSwipeCompleteAnimation(ValueAnimator animator, long expectedDuration,
            LauncherState targetState, float velocity, boolean isFling) {
        if (mFromState == NORMAL && mToState == OVERVIEW && targetState == OVERVIEW) {
            mFinishFastOnSecondTouch = true;

            // Update all apps interpolator
            float currentFraction = mCurrentAnimation.getProgressFraction();
            float absVelocity = Math.abs(velocity);
            float currentValue = mAllAppsDampedInterpolator.getInterpolation(currentFraction);

            if (isFling && absVelocity > 1 && currentFraction < LINEAR_SCALE_LIMIT) {

                // TODO: Clean up these magic calculations
                // Linearly interpolate the max value based on the velocity.
                float maxValue = Math.max(absVelocity > 4 ? 1 + MAX_OVERSHOOT :
                        1 + (absVelocity - 1) * MAX_OVERSHOOT / 3,
                        currentValue);
                double angleToPeak = PI_BY_2 - Math.asin(currentValue / maxValue);

                if (expectedDuration != 0 && angleToPeak != 0) {

                    float distanceLeft = 1 - currentFraction;
                    mAllAppsInterpolatorWrapper.baseInterpolator = (f) -> {
                        float scaledF = (f - currentFraction) / distanceLeft;

                        if (scaledF < 0.5f) {
                            double angle = PI_BY_2 - angleToPeak + scaledF * angleToPeak / 0.5f;
                            return (float) (maxValue * Math.sin(angle));
                        }

                        scaledF = ((scaledF - .5f) / .5f);
                        double angle = PI_BY_2 + 3 * scaledF * PI_BY_2;
                        float amplitude = (1 - scaledF) * (1 - scaledF) * (maxValue - 1);
                        return 1 + (float) (amplitude * Math.sin(angle));
                    };

                    animator.setDuration(expectedDuration).setInterpolator(LINEAR);
                    return;
                }
            }

            if (currentFraction < LINEAR_SCALE_LIMIT) {
                mAllAppsInterpolatorWrapper.baseInterpolator = LINEAR;
                super.updateSwipeCompleteAnimation(animator, expectedDuration, targetState,
                        velocity, isFling);
                return;
            }
            float extraValue = mAllAppsDampedInterpolator.getInterpolation(currentFraction) - 1;
            float distanceLeft = 1 - currentFraction;

            animator.setFloatValues(currentFraction, 1);
            mAllAppsInterpolatorWrapper.baseInterpolator = (f) -> {
                float scaledF = (f - currentFraction) / distanceLeft;

                double angle = scaledF * 1.5 * Math.PI;
                float amplitude = (1 - scaledF) * (1 - scaledF) * extraValue;
                return 1 + (float) (amplitude * Math.sin(angle));
            };
            animator.setDuration(200).setInterpolator(LINEAR);
            return;
        }
        mFinishFastOnSecondTouch = false;
        super.updateSwipeCompleteAnimation(animator, expectedDuration, targetState,
                velocity, isFling);
    }

    @Override
    protected void onSwipeInteractionCompleted(LauncherState targetState, int logAction) {
        super.onSwipeInteractionCompleted(targetState, logAction);
@@ -125,4 +278,14 @@ public class PortraitStatesTouchController extends AbstractStateChangeTouchContr
            SysuiEventLogger.writeDummyRecentsTransition(0);
        }
    }

    private static class InterpolatorWrapper implements Interpolator {

        public TimeInterpolator baseInterpolator = LINEAR;

        @Override
        public float getInterpolation(float v) {
            return baseInterpolator.getInterpolation(v);
        }
    }
}
+24 −81
Original line number Diff line number Diff line
@@ -16,32 +16,30 @@
package com.android.launcher3.uioverrides;

import static com.android.launcher3.LauncherState.NORMAL;
import static com.android.launcher3.anim.AlphaUpdateListener.updateVisibility;
import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_OVERVIEW_TRANSLATION;
import static com.android.launcher3.anim.Interpolators.LINEAR;
import static com.android.launcher3.compat.AccessibilityManagerCompat.isAccessibilityEnabled;
import static com.android.quickstep.views.LauncherRecentsView.TRANSLATION_FACTOR;

import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ObjectAnimator;
import android.view.View;
import android.animation.ValueAnimator;
import android.annotation.TargetApi;
import android.os.Build;

import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherState;
import com.android.launcher3.LauncherStateManager.AnimationConfig;
import com.android.launcher3.LauncherStateManager.StateHandler;
import com.android.launcher3.PagedView;
import com.android.launcher3.anim.AnimationSuccessListener;
import com.android.launcher3.anim.AnimatorSetBuilder;
import com.android.launcher3.anim.Interpolators;
import com.android.quickstep.AnimatedFloat;
import com.android.quickstep.views.RecentsView;
import com.android.launcher3.anim.PropertySetter;
import com.android.quickstep.views.LauncherRecentsView;

@TargetApi(Build.VERSION_CODES.O)
public class RecentsViewStateController implements StateHandler {

    private final Launcher mLauncher;
    private final RecentsView mRecentsView;

    private final AnimatedFloat mTransitionProgress = new AnimatedFloat(this::onTransitionProgress);
    // The fraction representing the visibility of the RecentsView. This allows delaying the
    // overall transition while the RecentsView is being shown or hidden.
    private final AnimatedFloat mVisibilityMultiplier = new AnimatedFloat(this::onVisibilityProgress);
    private final LauncherRecentsView mRecentsView;

    public RecentsViewStateController(Launcher launcher) {
        mLauncher = launcher;
@@ -50,14 +48,12 @@ public class RecentsViewStateController implements StateHandler {

    @Override
    public void setState(LauncherState state) {
        setVisibility(state.overviewUi);
        setTransitionProgress(state.overviewUi ? 1 : 0);
        mRecentsView.setAlpha(state.overviewUi ? 1 : 0);
        updateVisibility(mRecentsView, isAccessibilityEnabled(mLauncher));
        mRecentsView.setTranslationFactor(state.getOverviewTranslationFactor(mLauncher));
        if (state.overviewUi) {
            mRecentsView.resetTaskVisuals();
        }
        float overviewTranslationX = state.getOverviewTranslationX(mLauncher);
        int direction = mRecentsView.isRtl() ? -1 : 1;
        mRecentsView.setTranslationX(overviewTranslationX * direction);
    }

    @Override
@@ -76,73 +72,20 @@ public class RecentsViewStateController implements StateHandler {
            builder.setStartDelay(snapDuration / 4);
        }

        ObjectAnimator progressAnim =
                mTransitionProgress.animateToValue(toState.overviewUi ? 1 : 0);
        progressAnim.setDuration(config.duration);
        progressAnim.setInterpolator(Interpolators.LINEAR);
        builder.play(progressAnim);

        ObjectAnimator visibilityAnim = animateVisibility(toState.overviewUi);
        visibilityAnim.setDuration(config.duration);
        visibilityAnim.setInterpolator(Interpolators.LINEAR);
        builder.play(visibilityAnim);
        PropertySetter setter = config.getProperSetter(builder);
        setter.setFloat(mRecentsView, TRANSLATION_FACTOR,
                toState.getOverviewTranslationFactor(mLauncher),
                builder.getInterpolator(ANIM_OVERVIEW_TRANSLATION, LINEAR));
        setter.setViewAlpha(mRecentsView, toState.overviewUi ? 1 : 0, LINEAR);

        int direction = mRecentsView.isRtl() ? -1 : 1;
        float fromTranslationX = fromState.getOverviewTranslationX(mLauncher) * direction;
        float toTranslationX = toState.getOverviewTranslationX(mLauncher) * direction;
        ObjectAnimator translationXAnim = ObjectAnimator.ofFloat(mRecentsView, View.TRANSLATION_X,
                fromTranslationX, toTranslationX);
        translationXAnim.setDuration(config.duration);
        translationXAnim.setInterpolator(Interpolators.ACCEL);
        if (toState.overviewUi) {
            translationXAnim.addUpdateListener(valueAnimator -> {
            ValueAnimator updateAnim = ValueAnimator.ofFloat(0, 1);
            updateAnim.addUpdateListener(valueAnimator -> {
                // While animating into recents, update the visible task data as needed
                mRecentsView.loadVisibleTaskData();
            });
            updateAnim.setDuration(config.duration);
            builder.play(updateAnim);
        }
        builder.play(translationXAnim);
    }

    public void setVisibility(boolean isVisible) {
        mVisibilityMultiplier.cancelAnimation();
        mRecentsView.setVisibility(isVisible ? View.VISIBLE : View.GONE);
        mVisibilityMultiplier.updateValue(isVisible ? 1 : 0);
    }

    public ObjectAnimator animateVisibility(boolean isVisible) {
        ObjectAnimator anim = mVisibilityMultiplier.animateToValue(isVisible ? 1 : 0);
        if (isVisible) {
            anim.addListener(new AnimatorListenerAdapter() {
                @Override
                public void onAnimationStart(Animator animation) {
                    mRecentsView.setVisibility(View.VISIBLE);
                }
            });
        } else {
            anim.addListener(new AnimationSuccessListener() {
                @Override
                public void onAnimationSuccess(Animator animator) {
                    mRecentsView.setVisibility(View.GONE);
                }
            });
        }
        return anim;
    }

    public void setTransitionProgress(float progress) {
        mTransitionProgress.cancelAnimation();
        mTransitionProgress.updateValue(progress);
    }

    private void onTransitionProgress() {
        applyProgress();
    }

    private void onVisibilityProgress() {
        applyProgress();
    }

    private void applyProgress() {
        mRecentsView.setAlpha(mTransitionProgress.value * mVisibilityMultiplier.value);
    }
}
+37 −0
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@ package com.android.quickstep.views;

import static com.android.launcher3.LauncherState.NORMAL;

import android.annotation.TargetApi;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapShader;
@@ -30,7 +31,9 @@ import android.graphics.Shader;
import android.graphics.Shader.TileMode;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.util.AttributeSet;
import android.util.FloatProperty;
import android.widget.FrameLayout;

import com.android.launcher3.DeviceProfile;
@@ -41,14 +44,31 @@ import com.android.launcher3.R;
/**
 * {@link RecentsView} used in Launcher activity
 */
@TargetApi(Build.VERSION_CODES.O)
public class LauncherRecentsView extends RecentsView<Launcher> implements Insettable {

    public static final FloatProperty<LauncherRecentsView> TRANSLATION_FACTOR =
            new FloatProperty<LauncherRecentsView>("translationFactor") {

                @Override
                public void setValue(LauncherRecentsView view, float v) {
                    view.setTranslationFactor(v);
                }

                @Override
                public Float get(LauncherRecentsView view) {
                    return view.mTranslationFactor;
                }
            };

    private Bitmap mScrim;
    private Paint mFadePaint;
    private Shader mFadeShader;
    private Matrix mFadeMatrix;
    private boolean mScrimOnLeft;

    private float mTranslationFactor;

    public LauncherRecentsView(Context context) {
        this(context, null);
    }
@@ -131,4 +151,21 @@ public class LauncherRecentsView extends RecentsView<Launcher> implements Insett
    protected void onAllTasksRemoved() {
        mActivity.getStateManager().goToState(NORMAL);
    }

    @Override
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
        super.onLayout(changed, left, top, right, bottom);

        int width = right - left;
        setTranslationX(mTranslationFactor * (mIsRtl ? -width : width));
    }

    public void setTranslationFactor(float translationFactor) {
        mTranslationFactor = translationFactor;
        setTranslationX(translationFactor * (mIsRtl ? -getWidth() : getWidth()));
    }

    public float getTranslationFactor() {
        return mTranslationFactor;
    }
}
Loading