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

Commit 87cd5e71 authored by Jorim Jaggi's avatar Jorim Jaggi
Browse files

Improve quick settings fling/expand animation.

Change-Id: I507970ad2d22712a140f2107b62e689c2f3d7d6e
parent 6dbae589
Loading
Loading
Loading
Loading
+129 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2014 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.systemui.statusbar;

import android.animation.ValueAnimator;
import android.content.Context;
import android.view.animation.AnimationUtils;
import android.view.animation.Interpolator;
import android.view.animation.PathInterpolator;

/**
 * Utility class to calculate general fling animation when the finger is released.
 */
public class FlingAnimationUtils {

    private static final float LINEAR_OUT_SLOW_IN_Y2 = 0.35f;
    private static final float MAX_LENGTH_SECONDS = 0.4f;
    private static final float MIN_VELOCITY_DP_PER_SECOND = 250;

    /**
     * Crazy math. http://en.wikipedia.org/wiki/B%C3%A9zier_curve
     */
    private static final float LINEAR_OUT_SLOW_IN_START_GRADIENT = 1/LINEAR_OUT_SLOW_IN_Y2;

    private Interpolator mLinearOutSlowIn;
    private Interpolator mFastOutSlowIn;
    private float mMinVelocityPxPerSecond;

    public FlingAnimationUtils(Context ctx) {
        mLinearOutSlowIn = new PathInterpolator(0, 0, LINEAR_OUT_SLOW_IN_Y2, 1);
        mFastOutSlowIn
                = AnimationUtils.loadInterpolator(ctx, android.R.interpolator.fast_out_slow_in);
        mMinVelocityPxPerSecond
                = MIN_VELOCITY_DP_PER_SECOND * ctx.getResources().getDisplayMetrics().density;
    }

    /**
     * Applies the interpolator and length to the animator, such that the fling animation is
     * consistent with the finger motion.
     *
     * @param animator the animator to apply
     * @param currValue the current value
     * @param endValue the end value of the animator
     * @param velocity the current velocity of the motion
     */
    public void apply(ValueAnimator animator, float currValue, float endValue, float velocity) {
        float diff = Math.abs(endValue - currValue);
        float velAbs = Math.abs(velocity);
        float durationSeconds = LINEAR_OUT_SLOW_IN_START_GRADIENT * diff / velAbs;
        if (durationSeconds <= MAX_LENGTH_SECONDS) {
            animator.setInterpolator(mLinearOutSlowIn);
        } else if (velAbs >= mMinVelocityPxPerSecond) {

            // Cross fade between fast-out-slow-in and linear interpolator with current velocity.
            durationSeconds = MAX_LENGTH_SECONDS;
            VelocityInterpolator velocityInterpolator
                    = new VelocityInterpolator(durationSeconds, velAbs, diff);
            InterpolatorInterpolator superInterpolator = new InterpolatorInterpolator(
                    velocityInterpolator, mLinearOutSlowIn, mLinearOutSlowIn);
            animator.setInterpolator(superInterpolator);
        } else {

            // Just use a normal interpolator which doesn't take the velocity into account.
            durationSeconds = MAX_LENGTH_SECONDS;
            animator.setInterpolator(mFastOutSlowIn);
        }
        animator.setDuration((long) (durationSeconds * 1000));
    }

    /**
     * An interpolator which interpolates two interpolators with an interpolator.
     */
    private static final class InterpolatorInterpolator implements Interpolator {

        private Interpolator mInterpolator1;
        private Interpolator mInterpolator2;
        private Interpolator mCrossfader;

        InterpolatorInterpolator(Interpolator interpolator1, Interpolator interpolator2,
                Interpolator crossfader) {
            mInterpolator1 = interpolator1;
            mInterpolator2 = interpolator2;
            mCrossfader = crossfader;
        }

        @Override
        public float getInterpolation(float input) {
            float t = mCrossfader.getInterpolation(input);
            return (1 - t) * mInterpolator1.getInterpolation(input)
                    + t * mInterpolator2.getInterpolation(input);
        }
    }

    /**
     * An interpolator which interpolates with a fixed velocity.
     */
    private static final class VelocityInterpolator implements Interpolator {

        private float mDurationSeconds;
        private float mVelocity;
        private float mDiff;

        private VelocityInterpolator(float durationSeconds, float velocity, float diff) {
            mDurationSeconds = durationSeconds;
            mVelocity = velocity;
            mDiff = diff;
        }

        @Override
        public float getInterpolation(float input) {
            float time = input * mDurationSeconds;
            return time * mVelocity / mDiff;
        }
    }
}
+4 −9
Original line number Diff line number Diff line
@@ -32,6 +32,7 @@ import android.widget.LinearLayout;

import com.android.systemui.R;
import com.android.systemui.statusbar.ExpandableView;
import com.android.systemui.statusbar.FlingAnimationUtils;
import com.android.systemui.statusbar.GestureRecorder;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.stack.NotificationStackScrollLayout;
@@ -53,8 +54,6 @@ public class NotificationPanelView extends PanelView implements
    private int mNotificationTopPadding;
    private boolean mAnimateNextTopPaddingChange;

    private Interpolator mExpansionInterpolator;

    private int mTrackingPointer;
    private VelocityTracker mVelocityTracker;
    private boolean mTracking;
@@ -78,6 +77,7 @@ public class NotificationPanelView extends PanelView implements
    private int mStackScrollerIntrinsicPadding;
    private boolean mQsExpansionEnabled = true;
    private ValueAnimator mQsExpansionAnimator;
    private FlingAnimationUtils mFlingAnimationUtils;

    public NotificationPanelView(Context context, AttributeSet attrs) {
        super(context, attrs);
@@ -115,8 +115,7 @@ public class NotificationPanelView extends PanelView implements
        mNotificationTopPadding = getResources().getDimensionPixelSize(
                R.dimen.notifications_top_padding);
        mMinStackHeight = getResources().getDimensionPixelSize(R.dimen.collapsed_stack_height);
        mExpansionInterpolator = AnimationUtils.loadInterpolator(
                getContext(), android.R.interpolator.fast_out_slow_in);
        mFlingAnimationUtils = new FlingAnimationUtils(getContext());
    }

    @Override
@@ -435,13 +434,9 @@ public class NotificationPanelView extends PanelView implements
        }
    }
    private void flingSettings(float vel, boolean expand) {

        // TODO: Actually use velocity.

        float target = expand ? mQsMaxExpansionHeight : mQsMinExpansionHeight;
        ValueAnimator animator = ValueAnimator.ofFloat(mQsExpansionHeight, target);
        animator.setDuration(EXPANSION_ANIMATION_LENGTH);
        animator.setInterpolator(mExpansionInterpolator);
        mFlingAnimationUtils.apply(animator, mQsExpansionHeight, target, vel);
        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {