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

Commit c36ee6f4 authored by Joshua Tsuji's avatar Joshua Tsuji
Browse files

Stability and logic fixes to PhysicsAnimationLayout to address bugs that leave...

Stability and logic fixes to PhysicsAnimationLayout to address bugs that leave Bubbles in a bad state.

- Ignore attempts to start animations from controllers that aren't the active controller.
- Use -Float.MAX_VALUE for the start velocity default, not 0, so negative velocities aren't discarded.
- Update the SpringForce's final position directly rather than using animateToFinalPosition (the latter is susceptible to race conditions if animateToFinalPosition is called again before the next animation frame, a bug in DynamicAnimation).

Test: atest SystemUITests
Fixes: 131625234
Change-Id: Ie48da88db6c241896bba17bb800a2f91ab61651e
parent 0fa7a54e
Loading
Loading
Loading
Loading
+4 −4
Original line number Original line Diff line number Diff line
@@ -332,7 +332,7 @@ public class BubbleStackView extends FrameLayout {
        mBubbleContainer = new PhysicsAnimationLayout(context);
        mBubbleContainer = new PhysicsAnimationLayout(context);
        mBubbleContainer.setMaxRenderedChildren(
        mBubbleContainer.setMaxRenderedChildren(
                getResources().getInteger(R.integer.bubbles_max_rendered));
                getResources().getInteger(R.integer.bubbles_max_rendered));
        mBubbleContainer.setController(mStackAnimationController);
        mBubbleContainer.setActiveController(mStackAnimationController);
        mBubbleContainer.setElevation(elevation);
        mBubbleContainer.setElevation(elevation);
        mBubbleContainer.setClipChildren(false);
        mBubbleContainer.setClipChildren(false);
        addView(mBubbleContainer, new FrameLayout.LayoutParams(MATCH_PARENT, MATCH_PARENT));
        addView(mBubbleContainer, new FrameLayout.LayoutParams(MATCH_PARENT, MATCH_PARENT));
@@ -909,7 +909,7 @@ public class BubbleStackView extends FrameLayout {
            };
            };


            if (shouldExpand) {
            if (shouldExpand) {
                mBubbleContainer.setController(mExpandedAnimationController);
                mBubbleContainer.setActiveController(mExpandedAnimationController);
                mExpandedAnimationController.expandFromStack(
                mExpandedAnimationController.expandFromStack(
                        mStackAnimationController.getStackPositionAlongNearestHorizontalEdge()
                        mStackAnimationController.getStackPositionAlongNearestHorizontalEdge()
                        /* collapseTo */,
                        /* collapseTo */,
@@ -921,7 +921,7 @@ public class BubbleStackView extends FrameLayout {
                mBubbleContainer.cancelAllAnimations();
                mBubbleContainer.cancelAllAnimations();
                mExpandedAnimationController.collapseBackToStack(
                mExpandedAnimationController.collapseBackToStack(
                        () -> {
                        () -> {
                            mBubbleContainer.setController(mStackAnimationController);
                            mBubbleContainer.setActiveController(mStackAnimationController);
                            updateAfter.run();
                            updateAfter.run();
                        });
                        });
            }
            }
@@ -1014,7 +1014,7 @@ public class BubbleStackView extends FrameLayout {
        }
        }


        mStackAnimationController.cancelStackPositionAnimations();
        mStackAnimationController.cancelStackPositionAnimations();
        mBubbleContainer.setController(mStackAnimationController);
        mBubbleContainer.setActiveController(mStackAnimationController);
        hideFlyoutImmediate();
        hideFlyoutImmediate();


        mDraggingInDismissTarget = false;
        mDraggingInDismissTarget = false;
+18 −14
Original line number Original line Diff line number Diff line
@@ -103,20 +103,6 @@ public class ExpandedAnimationController
    private float mBubbleDraggingOutVelX;
    private float mBubbleDraggingOutVelX;
    private float mBubbleDraggingOutVelY;
    private float mBubbleDraggingOutVelY;


    @Override
    protected void setLayout(PhysicsAnimationLayout layout) {
        super.setLayout(layout);

        final Resources res = layout.getResources();
        mStackOffsetPx = res.getDimensionPixelSize(R.dimen.bubble_stack_offset);
        mBubblePaddingPx = res.getDimensionPixelSize(R.dimen.bubble_padding);
        mBubbleSizePx = res.getDimensionPixelSize(R.dimen.individual_bubble_size);
        mStatusBarHeight =
                res.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_height);
        mPipDismissHeight = res.getDimensionPixelSize(R.dimen.pip_dismiss_gradient_height);
        mBubblesMaxRendered = res.getInteger(R.integer.bubbles_max_rendered);
    }

    /**
    /**
     * Animates expanding the bubbles into a row along the top of the screen.
     * Animates expanding the bubbles into a row along the top of the screen.
     */
     */
@@ -296,6 +282,24 @@ public class ExpandedAnimationController
                : 0);
                : 0);
    }
    }


    @Override
    void onActiveControllerForLayout(PhysicsAnimationLayout layout) {
        final Resources res = layout.getResources();
        mStackOffsetPx = res.getDimensionPixelSize(R.dimen.bubble_stack_offset);
        mBubblePaddingPx = res.getDimensionPixelSize(R.dimen.bubble_padding);
        mBubbleSizePx = res.getDimensionPixelSize(R.dimen.individual_bubble_size);
        mStatusBarHeight =
                res.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_height);
        mPipDismissHeight = res.getDimensionPixelSize(R.dimen.pip_dismiss_gradient_height);
        mBubblesMaxRendered = res.getInteger(R.integer.bubbles_max_rendered);

        // Ensure that all child views are at 1x scale, and visible, in case they were animating
        // in.
        mLayout.setVisibility(View.VISIBLE);
        animationsForChildrenFromIndex(0 /* startIndex */, (index, animation) ->
                animation.scaleX(1f).scaleY(1f).alpha(1f)).startAll();
    }

    @Override
    @Override
    Set<DynamicAnimation.ViewProperty> getAnimatedProperties() {
    Set<DynamicAnimation.ViewProperty> getAnimatedProperties() {
        return Sets.newHashSet(
        return Sets.newHashSet(
+93 −23
Original line number Original line Diff line number Diff line
@@ -17,10 +17,12 @@
package com.android.systemui.bubbles.animation;
package com.android.systemui.bubbles.animation;


import android.content.Context;
import android.content.Context;
import android.util.Log;
import android.view.View;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import android.widget.FrameLayout;


import androidx.annotation.Nullable;
import androidx.dynamicanimation.animation.DynamicAnimation;
import androidx.dynamicanimation.animation.DynamicAnimation;
import androidx.dynamicanimation.animation.SpringAnimation;
import androidx.dynamicanimation.animation.SpringAnimation;
import androidx.dynamicanimation.animation.SpringForce;
import androidx.dynamicanimation.animation.SpringForce;
@@ -137,12 +139,30 @@ public class PhysicsAnimationLayout extends FrameLayout {
         */
         */
        abstract void onChildRemoved(View child, int index, Runnable finishRemoval);
        abstract void onChildRemoved(View child, int index, Runnable finishRemoval);


        /**
         * Called when the controller is set as the active animation controller for the given
         * layout. Once active, the controller can start animations using the animator instances
         * returned by {@link #animationForChild}.
         *
         * While all animations started by the previous controller will be cancelled, the new
         * controller should not make any assumptions about the state of the layout or its children.
         * Their translation, alpha, scale, etc. values may have been changed by the previous
         * controller and should be reset here if relevant.
         */
        abstract void onActiveControllerForLayout(PhysicsAnimationLayout layout);

        protected PhysicsAnimationLayout mLayout;
        protected PhysicsAnimationLayout mLayout;


        PhysicsAnimationController() { }
        PhysicsAnimationController() { }


        /** Whether this controller is the currently active controller for its associated layout. */
        protected boolean isActiveController() {
            return this == mLayout.mController;
        }

        protected void setLayout(PhysicsAnimationLayout layout) {
        protected void setLayout(PhysicsAnimationLayout layout) {
            this.mLayout = layout;
            this.mLayout = layout;
            onActiveControllerForLayout(layout);
        }
        }


        protected PhysicsAnimationLayout getLayout() {
        protected PhysicsAnimationLayout getLayout() {
@@ -170,6 +190,9 @@ public class PhysicsAnimationLayout extends FrameLayout {
                child.setTag(R.id.physics_animator_tag, animator);
                child.setTag(R.id.physics_animator_tag, animator);
            }
            }


            animator.clearAnimator();
            animator.setAssociatedController(this);

            return animator;
            return animator;
        }
        }


@@ -235,7 +258,7 @@ public class PhysicsAnimationLayout extends FrameLayout {
            new HashMap<>();
            new HashMap<>();


    /** The currently active animation controller. */
    /** The currently active animation controller. */
    private PhysicsAnimationController mController;
    @Nullable protected PhysicsAnimationController mController;


    /**
    /**
     * The maximum number of children to render and animate at a time. See
     * The maximum number of children to render and animate at a time. See
@@ -260,7 +283,7 @@ public class PhysicsAnimationLayout extends FrameLayout {
     * Sets the animation controller and constructs or reconfigures the layout's physics animations
     * Sets the animation controller and constructs or reconfigures the layout's physics animations
     * to meet the controller's specifications.
     * to meet the controller's specifications.
     */
     */
    public void setController(PhysicsAnimationController controller) {
    public void setActiveController(PhysicsAnimationController controller) {
        cancelAllAnimations();
        cancelAllAnimations();
        mEndActionForProperty.clear();
        mEndActionForProperty.clear();


@@ -315,7 +338,7 @@ public class PhysicsAnimationLayout extends FrameLayout {
        super.addView(child, index, params);
        super.addView(child, index, params);


        // Set up animations for the new view, if the controller is set. If it isn't set, we'll be
        // Set up animations for the new view, if the controller is set. If it isn't set, we'll be
        // setting up animations for all children when setController is called.
        // setting up animations for all children when setActiveController is called.
        if (mController != null) {
        if (mController != null) {
            for (DynamicAnimation.ViewProperty property : mController.getAnimatedProperties()) {
            for (DynamicAnimation.ViewProperty property : mController.getAnimatedProperties()) {
                setUpAnimationForChild(property, child, index);
                setUpAnimationForChild(property, child, index);
@@ -427,6 +450,10 @@ public class PhysicsAnimationLayout extends FrameLayout {
        }
        }
    }
    }


    protected boolean isActiveController(PhysicsAnimationController controller) {
        return mController == controller;
    }

    /** Whether the first child would be left of center if translated to the given x value. */
    /** Whether the first child would be left of center if translated to the given x value. */
    protected boolean isFirstChildXLeftOfCenter(float x) {
    protected boolean isFirstChildXLeftOfCenter(float x) {
        if (getChildCount() > 0) {
        if (getChildCount() > 0) {
@@ -592,7 +619,7 @@ public class PhysicsAnimationLayout extends FrameLayout {
        private View mView;
        private View mView;


        /** Start velocity to use for all property animations. */
        /** Start velocity to use for all property animations. */
        private float mDefaultStartVelocity = 0f;
        private float mDefaultStartVelocity = -Float.MAX_VALUE;


        /** Start delay to use when start is called. */
        /** Start delay to use when start is called. */
        private long mStartDelay = 0;
        private long mStartDelay = 0;
@@ -625,6 +652,15 @@ public class PhysicsAnimationLayout extends FrameLayout {
         */
         */
        private Map<DynamicAnimation.ViewProperty, Float> mAnimatedProperties = new HashMap<>();
        private Map<DynamicAnimation.ViewProperty, Float> mAnimatedProperties = new HashMap<>();


        /**
         * All of the initial property values that have been set. These values will be instantly set
         * when {@link #start} is called, just before the animation begins.
         */
        private Map<DynamicAnimation.ViewProperty, Float> mInitialPropertyValues = new HashMap<>();

        /** The animation controller that last retrieved this animator instance. */
        private PhysicsAnimationController mAssociatedController;

        protected PhysicsPropertyAnimator(View view) {
        protected PhysicsPropertyAnimator(View view) {
            this.mView = view;
            this.mView = view;
        }
        }
@@ -644,7 +680,7 @@ public class PhysicsAnimationLayout extends FrameLayout {


        /** Set the view's alpha value to 'from', then animate it to the given value. */
        /** Set the view's alpha value to 'from', then animate it to the given value. */
        public PhysicsPropertyAnimator alpha(float from, float to, Runnable... endActions) {
        public PhysicsPropertyAnimator alpha(float from, float to, Runnable... endActions) {
            mView.setAlpha(from);
            mInitialPropertyValues.put(DynamicAnimation.ALPHA, from);
            return alpha(to, endActions);
            return alpha(to, endActions);
        }
        }


@@ -656,7 +692,7 @@ public class PhysicsAnimationLayout extends FrameLayout {
        /** Set the view's translationX value to 'from', then animate it to the given value. */
        /** Set the view's translationX value to 'from', then animate it to the given value. */
        public PhysicsPropertyAnimator translationX(
        public PhysicsPropertyAnimator translationX(
                float from, float to, Runnable... endActions) {
                float from, float to, Runnable... endActions) {
            mView.setTranslationX(from);
            mInitialPropertyValues.put(DynamicAnimation.TRANSLATION_X, from);
            return translationX(to, endActions);
            return translationX(to, endActions);
        }
        }


@@ -668,7 +704,7 @@ public class PhysicsAnimationLayout extends FrameLayout {
        /** Set the view's translationY value to 'from', then animate it to the given value. */
        /** Set the view's translationY value to 'from', then animate it to the given value. */
        public PhysicsPropertyAnimator translationY(
        public PhysicsPropertyAnimator translationY(
                float from, float to, Runnable... endActions) {
                float from, float to, Runnable... endActions) {
            mView.setTranslationY(from);
            mInitialPropertyValues.put(DynamicAnimation.TRANSLATION_Y, from);
            return translationY(to, endActions);
            return translationY(to, endActions);
        }
        }


@@ -690,7 +726,7 @@ public class PhysicsAnimationLayout extends FrameLayout {


        /** Set the view's scaleX value to 'from', then animate it to the given value. */
        /** Set the view's scaleX value to 'from', then animate it to the given value. */
        public PhysicsPropertyAnimator scaleX(float from, float to, Runnable... endActions) {
        public PhysicsPropertyAnimator scaleX(float from, float to, Runnable... endActions) {
            mView.setScaleX(from);
            mInitialPropertyValues.put(DynamicAnimation.SCALE_X, from);
            return scaleX(to, endActions);
            return scaleX(to, endActions);
        }
        }


@@ -701,7 +737,7 @@ public class PhysicsAnimationLayout extends FrameLayout {


        /** Set the view's scaleY value to 'from', then animate it to the given value. */
        /** Set the view's scaleY value to 'from', then animate it to the given value. */
        public PhysicsPropertyAnimator scaleY(float from, float to, Runnable... endActions) {
        public PhysicsPropertyAnimator scaleY(float from, float to, Runnable... endActions) {
            mView.setScaleY(from);
            mInitialPropertyValues.put(DynamicAnimation.SCALE_Y, from);
            return scaleY(to, endActions);
            return scaleY(to, endActions);
        }
        }


@@ -750,6 +786,13 @@ public class PhysicsAnimationLayout extends FrameLayout {
         * animated property on every child (including chained animations) have ended.
         * animated property on every child (including chained animations) have ended.
         */
         */
        public void start(Runnable... after) {
        public void start(Runnable... after) {
            if (!isActiveController(mAssociatedController)) {
                Log.w(TAG, "Only the active animation controller is allowed to start animations. "
                        + "Use PhysicsAnimationLayout#setActiveController to set the active "
                        + "animation controller.");
                return;
            }

            final Set<DynamicAnimation.ViewProperty> properties = getAnimatedProperties();
            final Set<DynamicAnimation.ViewProperty> properties = getAnimatedProperties();


            // If there are end actions, set an end listener on the layout for all the properties
            // If there are end actions, set an end listener on the layout for all the properties
@@ -791,6 +834,10 @@ public class PhysicsAnimationLayout extends FrameLayout {


            // Actually start the animations.
            // Actually start the animations.
            for (DynamicAnimation.ViewProperty property : properties) {
            for (DynamicAnimation.ViewProperty property : properties) {
                if (mInitialPropertyValues.containsKey(property)) {
                    property.setValue(mView, mInitialPropertyValues.get(property));
                }

                final SpringForce defaultSpringForce = mController.getSpringForce(property, mView);
                final SpringForce defaultSpringForce = mController.getSpringForce(property, mView);
                animateValueForChild(
                animateValueForChild(
                        property,
                        property,
@@ -803,14 +850,7 @@ public class PhysicsAnimationLayout extends FrameLayout {
                        mEndActionsForProperty.get(property));
                        mEndActionsForProperty.get(property));
            }
            }


            // Clear out the animator.
            clearAnimator();
            mAnimatedProperties.clear();
            mPositionStartVelocities.clear();
            mDefaultStartVelocity = 0;
            mStartDelay = 0;
            mStiffness = -1;
            mDampingRatio = -1;
            mEndActionsForProperty.clear();
        }
        }


        /** Returns the set of properties that will animate once {@link #start} is called. */
        /** Returns the set of properties that will animate once {@link #start} is called. */
@@ -847,20 +887,50 @@ public class PhysicsAnimationLayout extends FrameLayout {
                    });
                    });
                }
                }


                animation.getSpring().setStiffness(stiffness);
                final SpringForce animationSpring = animation.getSpring();
                animation.getSpring().setDampingRatio(dampingRatio);

                if (animationSpring == null) {
                    return;
                }


                if (startVel > 0) {
                final Runnable configureAndStartAnimation = () -> {
                    animationSpring.setStiffness(stiffness);
                    animationSpring.setDampingRatio(dampingRatio);

                    if (startVel > -Float.MAX_VALUE) {
                        animation.setStartVelocity(startVel);
                        animation.setStartVelocity(startVel);
                    }
                    }


                    animationSpring.setFinalPosition(value);
                    animation.start();
                };

                if (startDelay > 0) {
                if (startDelay > 0) {
                    postDelayed(() -> animation.animateToFinalPosition(value), startDelay);
                    postDelayed(configureAndStartAnimation, startDelay);
                } else {
                } else {
                    animation.animateToFinalPosition(value);
                    configureAndStartAnimation.run();
                }
                }
            }
            }
        }
        }

        private void clearAnimator() {
            mInitialPropertyValues.clear();
            mAnimatedProperties.clear();
            mPositionStartVelocities.clear();
            mDefaultStartVelocity = -Float.MAX_VALUE;
            mStartDelay = 0;
            mStiffness = -1;
            mDampingRatio = -1;
            mEndActionsForProperty.clear();
        }

        /**
         * Sets the controller that last retrieved this animator instance, so that we can prevent
         * {@link #start} from actually starting animations if called by a non-active controller.
         */
        private void setAssociatedController(PhysicsAnimationController controller) {
            mAssociatedController = controller;
        }
    }
    }


    @Override
    @Override
+38 −28
Original line number Original line Diff line number Diff line
@@ -154,21 +154,6 @@ public class StackAnimationController extends
    /** Height of the status bar. */
    /** Height of the status bar. */
    private float mStatusBarHeight;
    private float mStatusBarHeight;


    @Override
    protected void setLayout(PhysicsAnimationLayout layout) {
        super.setLayout(layout);

        Resources res = layout.getResources();
        mStackOffset = res.getDimensionPixelSize(R.dimen.bubble_stack_offset);
        mIndividualBubbleSize = res.getDimensionPixelSize(R.dimen.individual_bubble_size);
        mBubblePadding = res.getDimensionPixelSize(R.dimen.bubble_padding);
        mBubbleOffscreen = res.getDimensionPixelSize(R.dimen.bubble_stack_offscreen);
        mStackStartingVerticalOffset =
                res.getDimensionPixelSize(R.dimen.bubble_stack_starting_offset_y);
        mStatusBarHeight =
                res.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_height);
    }

    /**
    /**
     * Instantly move the first bubble to the given point, and animate the rest of the stack behind
     * Instantly move the first bubble to the given point, and animate the rest of the stack behind
     * it with the 'following' effect.
     * it with the 'following' effect.
@@ -656,19 +641,35 @@ public class StackAnimationController extends


        if (mLayout.getChildCount() > 0) {
        if (mLayout.getChildCount() > 0) {
            animationForChildAtIndex(0).translationX(mStackPosition.x).start();
            animationForChildAtIndex(0).translationX(mStackPosition.x).start();
        } else {
            // Set the start position back to the default since we're out of bubbles. New bubbles
            // will then animate in from the start position.
            mStackPosition = getDefaultStartPosition();
        }
        }
    }
    }


    @Override
    void onActiveControllerForLayout(PhysicsAnimationLayout layout) {
        Resources res = layout.getResources();
        mStackOffset = res.getDimensionPixelSize(R.dimen.bubble_stack_offset);
        mIndividualBubbleSize = res.getDimensionPixelSize(R.dimen.individual_bubble_size);
        mBubblePadding = res.getDimensionPixelSize(R.dimen.bubble_padding);
        mBubbleOffscreen = res.getDimensionPixelSize(R.dimen.bubble_stack_offscreen);
        mStackStartingVerticalOffset =
                res.getDimensionPixelSize(R.dimen.bubble_stack_starting_offset_y);
        mStatusBarHeight =
                res.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_height);
    }

    /** Moves the stack, without any animation, to the starting position. */
    /** Moves the stack, without any animation, to the starting position. */
    private void moveStackToStartPosition() {
    private void moveStackToStartPosition() {
        // Post to ensure that the layout's width and height have been calculated.
        // Post to ensure that the layout's width and height have been calculated.
        mLayout.setVisibility(View.INVISIBLE);
        mLayout.setVisibility(View.INVISIBLE);
        mLayout.post(() -> {
        mLayout.post(() -> {
            mStackMovedToStartPosition = true;
            setStackPosition(mRestingStackPosition == null
            setStackPosition(
                    mRestingStackPosition == null
                    ? getDefaultStartPosition()
                    ? getDefaultStartPosition()
                    : mRestingStackPosition);
                    : mRestingStackPosition);
            mStackMovedToStartPosition = true;
            mLayout.setVisibility(View.VISIBLE);
            mLayout.setVisibility(View.VISIBLE);


            // Animate in the top bubble now that we're visible.
            // Animate in the top bubble now that we're visible.
@@ -707,17 +708,22 @@ public class StackAnimationController extends
        Log.d(TAG, String.format("Setting position to (%f, %f).", pos.x, pos.y));
        Log.d(TAG, String.format("Setting position to (%f, %f).", pos.x, pos.y));
        mStackPosition.set(pos.x, pos.y);
        mStackPosition.set(pos.x, pos.y);


        // If we're not the active controller, we don't want to physically move the bubble views.
        if (isActiveController()) {
            mLayout.cancelAllAnimations();
            mLayout.cancelAllAnimations();
            cancelStackPositionAnimations();
            cancelStackPositionAnimations();


            // Since we're not using the chained animations, apply the offsets manually.
            // Since we're not using the chained animations, apply the offsets manually.
        final float xOffset = getOffsetForChainedPropertyAnimation(DynamicAnimation.TRANSLATION_X);
            final float xOffset = getOffsetForChainedPropertyAnimation(
        final float yOffset = getOffsetForChainedPropertyAnimation(DynamicAnimation.TRANSLATION_Y);
                    DynamicAnimation.TRANSLATION_X);
            final float yOffset = getOffsetForChainedPropertyAnimation(
                    DynamicAnimation.TRANSLATION_Y);
            for (int i = 0; i < mLayout.getChildCount(); i++) {
            for (int i = 0; i < mLayout.getChildCount(); i++) {
                mLayout.getChildAt(i).setTranslationX(pos.x + (i * xOffset));
                mLayout.getChildAt(i).setTranslationX(pos.x + (i * xOffset));
                mLayout.getChildAt(i).setTranslationY(pos.y + (i * yOffset));
                mLayout.getChildAt(i).setTranslationY(pos.y + (i * yOffset));
            }
            }
        }
        }
    }


    /** Returns the default stack position, which is on the top right. */
    /** Returns the default stack position, which is on the top right. */
    private PointF getDefaultStartPosition() {
    private PointF getDefaultStartPosition() {
@@ -732,6 +738,10 @@ public class StackAnimationController extends


    /** Animates in the given bubble. */
    /** Animates in the given bubble. */
    private void animateInBubble(View child) {
    private void animateInBubble(View child) {
        if (!isActiveController()) {
            return;
        }

        child.setTranslationY(mStackPosition.y);
        child.setTranslationY(mStackPosition.y);


        float xOffset = getOffsetForChainedPropertyAnimation(DynamicAnimation.TRANSLATION_X);
        float xOffset = getOffsetForChainedPropertyAnimation(DynamicAnimation.TRANSLATION_X);
+1 −1
Original line number Original line Diff line number Diff line
@@ -61,7 +61,7 @@ public class ExpandedAnimationControllerTest extends PhysicsAnimationLayoutTestC
    public void setUp() throws Exception {
    public void setUp() throws Exception {
        super.setUp();
        super.setUp();
        addOneMoreThanRenderLimitBubbles();
        addOneMoreThanRenderLimitBubbles();
        mLayout.setController(mExpandedController);
        mLayout.setActiveController(mExpandedController);


        Resources res = mLayout.getResources();
        Resources res = mLayout.getResources();
        mStackOffset = res.getDimensionPixelSize(R.dimen.bubble_stack_offset);
        mStackOffset = res.getDimensionPixelSize(R.dimen.bubble_stack_offset);
Loading