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

Commit de399397 authored by Alan Viverette's avatar Alan Viverette
Browse files

Support for list selector ripple during arrow movement & drag

BUG: 14231774
BUG: 14230395
Change-Id: I23efbc88c3f05b3f323e47bcb34f55ff70ad6828
parent edfb6b6b
Loading
Loading
Loading
Loading
+24 −0
Original line number Diff line number Diff line
@@ -110,6 +110,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
     * @see #setTranscriptMode(int)
     */
    public static final int TRANSCRIPT_MODE_DISABLED = 0;

    /**
     * The list will automatically scroll to the bottom when a data set change
     * notification is received and only if the last item is already visible
@@ -118,6 +119,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
     * @see #setTranscriptMode(int)
     */
    public static final int TRANSCRIPT_MODE_NORMAL = 1;

    /**
     * The list will automatically scroll to the bottom, no matter what items
     * are currently visible.
@@ -2489,8 +2491,30 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
        }
    }

    /**
     * Positions the selector in a way that mimics keyboard focus. If the
     * selector drawable supports hotspots, this manages the focus hotspot.
     */
    void positionSelectorLikeFocus(int position, View sel) {
        positionSelector(position, sel);

        final Drawable selector = mSelector;
        if (selector != null && selector.supportsHotspots() && position != INVALID_POSITION) {
            final Rect bounds = mSelectorRect;
            final float x = bounds.exactCenterX();
            final float y = bounds.exactCenterY();
            selector.setHotspot(R.attr.state_focused, x, y);
        }
    }

    void positionSelector(int position, View sel) {
        if (position != INVALID_POSITION) {
            if (mSelectorPosition != position) {
                final Drawable selector = mSelector;
                if (selector != null && selector.supportsHotspots()) {
                    selector.clearHotspots();
                }
            }
            mSelectorPosition = position;
        }

+1 −1
Original line number Diff line number Diff line
@@ -1565,7 +1565,7 @@ public class ListPopupWindow {

            // Ensure that keyboard focus starts from the last touched position.
            setSelectedPositionInt(position);
            positionSelector(position, child);
            positionSelectorLikeFocus(position, child);

            // Refresh the drawable state to reflect the new pressed state,
            // which will also update the selector state.
+1 −1
Original line number Diff line number Diff line
@@ -2564,7 +2564,7 @@ public class ListView extends AbsListView {

        if (needToRedraw) {
            if (selectedView != null) {
                positionSelector(selectedPos, selectedView);
                positionSelectorLikeFocus(selectedPos, selectedView);
                mSelectedTop = selectedView.getTop();
            }
            if (!awakenScrollBars()) {
+79 −63
Original line number Diff line number Diff line
@@ -18,6 +18,7 @@ package android.graphics.drawable;

import android.animation.Animator;
import android.animation.Animator.AnimatorListener;
import android.animation.AnimatorListenerAdapter;
import android.animation.ObjectAnimator;
import android.animation.TimeInterpolator;
import android.graphics.Canvas;
@@ -85,8 +86,9 @@ class Ripple {
    /** Configured maximum ripple radius. */
    private final int mMaxInsideRadius;

    private ObjectAnimator mEnter;
    private ObjectAnimator mExit;
    private ObjectAnimator mOuter;
    private ObjectAnimator mInner;
    private ObjectAnimator mAlpha;

    /** Maximum ripple radius. */
    private int mMaxRadius;
@@ -267,28 +269,46 @@ class Ripple {
    public void exit() {
        mExitFinished = false;

        final ObjectAnimator exit = ObjectAnimator.ofFloat(this, "innerRadius", 0, mMaxRadius);
        exit.setAutoCancel(true);
        exit.setDuration(EXIT_DURATION);
        exit.setInterpolator(INTERPOLATOR);
        exit.addListener(mAnimationListener);
        final ObjectAnimator inner = ObjectAnimator.ofFloat(this, "innerRadius", 0, mMaxRadius);
        inner.setAutoCancel(true);
        inner.setDuration(EXIT_DURATION);
        inner.setInterpolator(INTERPOLATOR);
        inner.addListener(mAnimationListener);

        if (mEnter != null && mEnter.isStarted()) {
        if (mOuter != null && mOuter.isStarted()) {
            // If we haven't been running the enter animation for long enough,
            // delay the exit animator.
            final int elapsed = (int) (mEnter.getAnimatedFraction() * mEnter.getDuration());
            final int elapsed = (int) (mOuter.getAnimatedFraction() * mOuter.getDuration());
            final int delay = Math.max(0, EXIT_MIN_DELAY - elapsed);
            exit.setStartDelay(delay);
            inner.setStartDelay(delay);
        }

        exit.start();
        inner.start();

        final ObjectAnimator fade = ObjectAnimator.ofFloat(this, "alphaMultiplier", 0);
        fade.setAutoCancel(true);
        fade.setDuration(EXIT_DURATION);
        fade.start();
        final ObjectAnimator alpha = ObjectAnimator.ofFloat(this, "alphaMultiplier", 0);
        alpha.setAutoCancel(true);
        alpha.setDuration(EXIT_DURATION);
        alpha.start();

        mExit = exit;
        mInner = inner;
        mAlpha = alpha;
    }

    /**
     * Cancel all animations.
     */
    public void cancel() {
        if (mInner != null) {
            mInner.end();
        }

        if (mOuter != null) {
            mOuter.cancel();
        }

        if (mAlpha != null) {
            mAlpha.end();
        }
    }

    private void invalidateSelf() {
@@ -299,19 +319,17 @@ class Ripple {
     * Starts the enter animation.
     */
    private void enter() {
        final ObjectAnimator enter = ObjectAnimator.ofFloat(this, "outerRadius", mMaxRadius);
        enter.setAutoCancel(true);
        enter.setDuration(ENTER_DURATION);
        enter.setInterpolator(INTERPOLATOR);
        enter.start();

        final ObjectAnimator fade = ObjectAnimator.ofFloat(this, "alphaMultiplier", 1);
        fade.setAutoCancel(true);
        fade.setDuration(FADE_DURATION);
        fade.start();

        // TODO: Starting with a delay will still cancel the fade in.
        if (false && mPulseEnabled) {
        final ObjectAnimator outer = ObjectAnimator.ofFloat(this, "outerRadius", mMaxRadius);
        outer.setAutoCancel(true);
        outer.setDuration(ENTER_DURATION);
        outer.setInterpolator(INTERPOLATOR);
        outer.start();

        final ObjectAnimator alpha = ObjectAnimator.ofFloat(this, "alphaMultiplier", 1);
        if (mPulseEnabled) {
            alpha.addListener(new AnimatorListenerAdapter() {
                @Override
                public void onAnimationEnd(Animator animation) {
                    final ObjectAnimator pulse = ObjectAnimator.ofFloat(
                            this, "alphaMultiplier", 1, PULSE_MIN_ALPHA);
                    pulse.setAutoCancel(true);
@@ -320,26 +338,36 @@ class Ripple {
                    pulse.setRepeatMode(ObjectAnimator.REVERSE);
                    pulse.setStartDelay(PULSE_DELAY);
                    pulse.start();

                    mAlpha = pulse;
                }
            });
        }
        alpha.setAutoCancel(true);
        alpha.setDuration(FADE_DURATION);
        alpha.start();

        mEnter = enter;
        mOuter = outer;
        mAlpha = alpha;
    }

    /**
     * Starts the outside transition animation.
     */
    private void outside() {
        final float targetRadius = mMaxOutsideRadius;
        final ObjectAnimator outside = ObjectAnimator.ofFloat(this, "outerRadius", targetRadius);
        outside.setAutoCancel(true);
        outside.setDuration(OUTSIDE_DURATION);
        outside.setInterpolator(INTERPOLATOR);
        outside.start();

        final ObjectAnimator fade = ObjectAnimator.ofFloat(this, "alphaMultiplier", 1);
        fade.setAutoCancel(true);
        fade.setDuration(FADE_DURATION);
        fade.start();
        final ObjectAnimator outer = ObjectAnimator.ofFloat(this, "outerRadius", mMaxOutsideRadius);
        outer.setAutoCancel(true);
        outer.setDuration(OUTSIDE_DURATION);
        outer.setInterpolator(INTERPOLATOR);
        outer.start();

        final ObjectAnimator alpha = ObjectAnimator.ofFloat(this, "alphaMultiplier", 1);
        alpha.setAutoCancel(true);
        alpha.setDuration(FADE_DURATION);
        alpha.start();

        mOuter = outer;
        mAlpha = alpha;
    }

    /**
@@ -358,27 +386,15 @@ class Ripple {
        }
    }

    private final AnimatorListener mAnimationListener = new AnimatorListener() {
        @Override
        public void onAnimationStart(Animator animation) {
        }

        @Override
        public void onAnimationRepeat(Animator animation) {
        }

    private final AnimatorListener mAnimationListener = new AnimatorListenerAdapter() {
        @Override
        public void onAnimationEnd(Animator animation) {
            if (animation == mExit) {
            if (animation == mInner) {
                mExitFinished = true;
                mOuterRadius = 0;
                mInnerRadius = 0;
                mAlphaMultiplier = 1;
            }
        }

        @Override
        public void onAnimationCancel(Animator animation) {
        }
    };
}
+10 −3
Original line number Diff line number Diff line
@@ -380,11 +380,18 @@ public class TouchFeedbackDrawable extends LayerDrawable {

    @Override
    public void clearHotspots() {
        if (mRipples == null) {
            return;
        if (mRipples != null) {
            mRipples.clear();
        }

        mRipples.clear();
        final int count = mAnimatingRipplesCount;
        final Ripple[] ripples = mAnimatingRipples;
        for (int i = 0; i < count; i++) {
            ripples[i].cancel();
            ripples[i] = null;
        }

        mAnimatingRipplesCount = 0;
        invalidateSelf();
    }