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

Commit e4739ba2 authored by Alan Viverette's avatar Alan Viverette Committed by Android (Google) Code Review
Browse files

Merge "Support for list selector ripple during arrow movement & drag"

parents 5fc37e79 de399397
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();
    }