StackStateAnimator.java revision 8efa6dde2b4f2cdbf046b87b7366404c3cc46219
1572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek/*
2572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek * Copyright (C) 2014 The Android Open Source Project
3572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek *
4572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek * Licensed under the Apache License, Version 2.0 (the "License");
5572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek * you may not use this file except in compliance with the License.
6572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek * You may obtain a copy of the License at
7572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek *
8572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek *      http://www.apache.org/licenses/LICENSE-2.0
9572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek *
10572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek * Unless required by applicable law or agreed to in writing, software
11572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek * distributed under the License is distributed on an "AS IS" BASIS,
12572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek * See the License for the specific language governing permissions and
14572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek * limitations under the License
15572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek */
16572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek
17572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinekpackage com.android.systemui.statusbar.stack;
18572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek
19eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinekimport android.animation.Animator;
20eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinekimport android.animation.AnimatorListenerAdapter;
21eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinekimport android.animation.ObjectAnimator;
22d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggiimport android.animation.PropertyValuesHolder;
23572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinekimport android.animation.ValueAnimator;
24572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinekimport android.view.View;
25572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinekimport android.view.animation.AnimationUtils;
26572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinekimport android.view.animation.Interpolator;
27eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek
28eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinekimport com.android.systemui.R;
29572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinekimport com.android.systemui.statusbar.ExpandableView;
30572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek
31572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinekimport java.util.ArrayList;
32eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinekimport java.util.HashSet;
33eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinekimport java.util.Set;
34eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinekimport java.util.Stack;
35572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek
36572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek/**
37572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek * An stack state animator which handles animations to new StackScrollStates
38572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek */
39572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinekpublic class StackStateAnimator {
40572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek
415aa045cc6bca84f5c11f1a99999546ba5e5949a5Jorim Jaggi    public static final int ANIMATION_DURATION_STANDARD = 360;
428efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek    public static final int ANIMATION_DURATION_APPEAR_DISAPPEAR = 464;
435aa045cc6bca84f5c11f1a99999546ba5e5949a5Jorim Jaggi    public static final int ANIMATION_DURATION_DIMMED_ACTIVATED = 220;
448efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek    public static final int ANIMATION_DELAY_PER_ELEMENT_INTERRUPTING = 80;
458efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek    public static final int ANIMATION_DELAY_PER_ELEMENT_MANUAL = 32;
468efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek    private static final int DELAY_EFFECT_MAX_INDEX_DIFFERENCE = 2;
475aa045cc6bca84f5c11f1a99999546ba5e5949a5Jorim Jaggi
48eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek    private static final int TAG_ANIMATOR_TRANSLATION_Y = R.id.translation_y_animator_tag;
49eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek    private static final int TAG_ANIMATOR_TRANSLATION_Z = R.id.translation_z_animator_tag;
50d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi    private static final int TAG_ANIMATOR_SCALE = R.id.scale_animator_tag;
51eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek    private static final int TAG_ANIMATOR_ALPHA = R.id.alpha_animator_tag;
52eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek    private static final int TAG_ANIMATOR_HEIGHT = R.id.height_animator_tag;
53eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek    private static final int TAG_ANIMATOR_TOP_INSET = R.id.top_inset_animator_tag;
54eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek    private static final int TAG_END_TRANSLATION_Y = R.id.translation_y_animator_end_value_tag;
55eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek    private static final int TAG_END_TRANSLATION_Z = R.id.translation_z_animator_end_value_tag;
56d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi    private static final int TAG_END_SCALE = R.id.scale_animator_end_value_tag;
57eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek    private static final int TAG_END_ALPHA = R.id.alpha_animator_end_value_tag;
58eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek    private static final int TAG_END_HEIGHT = R.id.height_animator_end_value_tag;
59eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek    private static final int TAG_END_TOP_INSET = R.id.top_inset_animator_end_value_tag;
608df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek    private static final int TAG_START_TRANSLATION_Y = R.id.translation_y_animator_start_value_tag;
618df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek    private static final int TAG_START_TRANSLATION_Z = R.id.translation_z_animator_start_value_tag;
628df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek    private static final int TAG_START_SCALE = R.id.scale_animator_start_value_tag;
638df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek    private static final int TAG_START_ALPHA = R.id.alpha_animator_start_value_tag;
648df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek    private static final int TAG_START_HEIGHT = R.id.height_animator_start_value_tag;
658df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek    private static final int TAG_START_TOP_INSET = R.id.top_inset_animator_start_value_tag;
66572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek
67572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek    private final Interpolator mFastOutSlowInInterpolator;
68572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek    public NotificationStackScrollLayout mHostLayout;
69eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek    private ArrayList<NotificationStackScrollLayout.AnimationEvent> mNewEvents =
70eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek            new ArrayList<>();
718efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek    private ArrayList<View> mNewAddChildren = new ArrayList<>();
72eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek    private Set<Animator> mAnimatorSet = new HashSet<Animator>();
73eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek    private Stack<AnimatorListenerAdapter> mAnimationListenerPool
74eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek            = new Stack<AnimatorListenerAdapter>();
75d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi    private AnimationFilter mAnimationFilter = new AnimationFilter();
765aa045cc6bca84f5c11f1a99999546ba5e5949a5Jorim Jaggi    private long mCurrentLength;
77572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek
788d9ff9c2c66bc1d3b92eb6992d58599ff80ed6dcSelim Cinek    private ValueAnimator mTopOverScrollAnimator;
798d9ff9c2c66bc1d3b92eb6992d58599ff80ed6dcSelim Cinek    private ValueAnimator mBottomOverScrollAnimator;
808d9ff9c2c66bc1d3b92eb6992d58599ff80ed6dcSelim Cinek
81572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek    public StackStateAnimator(NotificationStackScrollLayout hostLayout) {
82572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek        mHostLayout = hostLayout;
83572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek        mFastOutSlowInInterpolator = AnimationUtils.loadInterpolator(hostLayout.getContext(),
84eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek                android.R.interpolator.fast_out_slow_in);
85572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek    }
86572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek
87572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek    public boolean isRunning() {
88eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        return !mAnimatorSet.isEmpty();
89572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek    }
90572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek
91572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek    public void startAnimationForEvents(
920dd6881ea481c855976214807c17595b34a2920aJorim Jaggi            ArrayList<NotificationStackScrollLayout.AnimationEvent> mAnimationEvents,
93572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek            StackScrollState finalState) {
94eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek
95eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        processAnimationEvents(mAnimationEvents, finalState);
96eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek
97572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek        int childCount = mHostLayout.getChildCount();
98d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi        mAnimationFilter.applyCombination(mNewEvents);
995aa045cc6bca84f5c11f1a99999546ba5e5949a5Jorim Jaggi        mCurrentLength = NotificationStackScrollLayout.AnimationEvent.combineLength(mNewEvents);
100572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek        for (int i = 0; i < childCount; i++) {
101572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek            final ExpandableView child = (ExpandableView) mHostLayout.getChildAt(i);
1028efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek
103572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek            StackScrollState.ViewState viewState = finalState.getViewStateForView(child);
1048efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek            if (viewState == null || child.getVisibility() == View.GONE) {
105572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek                continue;
106572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek            }
107572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek
108572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek            child.setClipBounds(null);
1098efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek            startAnimations(child, viewState, finalState);
110572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek        }
111eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        if (!isRunning()) {
112eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek            // no child has preformed any animation, lets finish
113eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek            onAnimationFinished();
114eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        }
1158efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek        mNewEvents.clear();
1168efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek        mNewAddChildren.clear();
117572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek    }
118572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek
119eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek    /**
120eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek     * Start an animation to the given viewState
121eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek     */
1228efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek    private void startAnimations(final ExpandableView child, StackScrollState.ViewState viewState,
1238efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek            StackScrollState finalState) {
124eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        int childVisibility = child.getVisibility();
125eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        boolean wasVisible = childVisibility == View.VISIBLE;
126eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        final float alpha = viewState.alpha;
127eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        if (!wasVisible && alpha != 0 && !viewState.gone) {
128eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek            child.setVisibility(View.VISIBLE);
129eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        }
1308efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek
1318efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek        boolean yTranslationChanging = child.getTranslationY() != viewState.yTranslation;
1328efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek        boolean zTranslationChanging = child.getTranslationZ() != viewState.zTranslation;
1338efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek        boolean scaleChanging = child.getScaleX() != viewState.scale;
1348efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek        boolean alphaChanging = alpha != child.getAlpha();
1358efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek        boolean heightChanging = viewState.height != child.getActualHeight();
1368efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek        boolean wasAdded = mNewAddChildren.contains(child);
1378efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek        boolean hasDelays = mAnimationFilter.hasDelays;
1388efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek        boolean isDelayRelevant = yTranslationChanging || zTranslationChanging || scaleChanging ||
1398efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek                alphaChanging || heightChanging;
1408efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek        long delay = 0;
1418efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek        if (hasDelays && isDelayRelevant || wasAdded) {
1428efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek            delay = calculateChildAnimationDelay(viewState, finalState);
1438efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek        }
1448efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek
145eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        // start translationY animation
1468efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek        if (yTranslationChanging) {
1478efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek            startYTranslationAnimation(child, viewState, delay);
148eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        }
1498efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek
150eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        // start translationZ animation
1518efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek        if (zTranslationChanging) {
1528efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek            startZTranslationAnimation(child, viewState, delay);
153d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi        }
1548efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek
155d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi        // start scale animation
1568efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek        if (scaleChanging) {
157d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi            startScaleAnimation(child, viewState);
158eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        }
1598efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek
160eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        // start alpha animation
1618efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek        if (alphaChanging) {
1628efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek            startAlphaAnimation(child, viewState, delay);
163eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        }
1648efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek
165eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        // start height animation
1668efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek        if (heightChanging) {
1678efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek            startHeightAnimation(child, viewState, delay);
16859b5a356b828fe60ea2874b0680a1bf7c84809a1Jorim Jaggi        }
1698efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek
170d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi        // start dimmed animation
171d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi        child.setDimmed(viewState.dimmed, mAnimationFilter.animateDimmed);
1728efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek
1738efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek        if (wasAdded) {
1748efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek            child.performAddAnimation(delay);
1758efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek        }
1768efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek    }
1778efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek
1788efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek    private long calculateChildAnimationDelay(StackScrollState.ViewState viewState,
1798efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek            StackScrollState finalState) {
1808efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek        long minDelay = 0;
1818efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek        for (NotificationStackScrollLayout.AnimationEvent event : mNewEvents) {
1828efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek            long delayPerElement = ANIMATION_DELAY_PER_ELEMENT_INTERRUPTING;
1838efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek            switch (event.animationType) {
1848efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek                case NotificationStackScrollLayout.AnimationEvent.ANIMATION_TYPE_ADD: {
1858efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek                    int ownIndex = viewState.notGoneIndex;
1868efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek                    int changingIndex = finalState
1878efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek                            .getViewStateForView(event.changingView).notGoneIndex;
1888efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek                    int difference = Math.abs(ownIndex - changingIndex);
1898efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek                    difference = Math.max(0, Math.min(DELAY_EFFECT_MAX_INDEX_DIFFERENCE,
1908efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek                            difference - 1));
1918efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek                    long delay = (DELAY_EFFECT_MAX_INDEX_DIFFERENCE - difference) * delayPerElement;
1928efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek                    minDelay = Math.max(delay, minDelay);
1938efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek                    break;
1948efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek                }
1958efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek                case NotificationStackScrollLayout.AnimationEvent.ANIMATION_TYPE_REMOVE_SWIPED_OUT:
1968efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek                    delayPerElement = ANIMATION_DELAY_PER_ELEMENT_MANUAL;
1978efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek                case NotificationStackScrollLayout.AnimationEvent.ANIMATION_TYPE_REMOVE: {
1988efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek                    int ownIndex = viewState.notGoneIndex;
1998efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek                    boolean noNextView = event.viewAfterChangingView == null;
2008efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek                    View viewAfterChangingView = noNextView
2018efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek                            ? mHostLayout.getLastChildNotGone()
2028efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek                            : event.viewAfterChangingView;
2038efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek
2048efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek                    int nextIndex = finalState
2058efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek                            .getViewStateForView(viewAfterChangingView).notGoneIndex;
2068efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek                    if (ownIndex >= nextIndex) {
2078efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek                        // we only have the view afterwards
2088efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek                        ownIndex++;
2098efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek                    }
2108efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek                    int difference = Math.abs(ownIndex - nextIndex);
2118efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek                    difference = Math.max(0, Math.min(DELAY_EFFECT_MAX_INDEX_DIFFERENCE,
2128efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek                            difference - 1));
2138efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek                    long delay = difference * delayPerElement;
2148efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek                    minDelay = Math.max(delay, minDelay);
2158efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek                    break;
2168efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek                }
2178efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek                default:
2188efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek                    break;
2198efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek            }
2208efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek        }
2218efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek        return minDelay;
222572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek    }
223572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek
224eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek    private void startHeightAnimation(final ExpandableView child,
2258efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek            StackScrollState.ViewState viewState, long delay) {
2268df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek        Integer previousStartValue = getChildTag(child, TAG_START_HEIGHT);
227d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi        Integer previousEndValue = getChildTag(child, TAG_END_HEIGHT);
2288df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek        int newEndValue = viewState.height;
2298df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek        if (previousEndValue != null && previousEndValue == newEndValue) {
230eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek            return;
231eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        }
232eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        ValueAnimator previousAnimator = getChildTag(child, TAG_ANIMATOR_HEIGHT);
2338df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek        if (!mAnimationFilter.animateHeight) {
2348df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek            // just a local update was performed
2358df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek            if (previousAnimator != null) {
2368df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek                // we need to increase all animation keyframes of the previous animator by the
2378df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek                // relative change to the end value
2388df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek                PropertyValuesHolder[] values = previousAnimator.getValues();
2398df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek                int relativeDiff = newEndValue - previousEndValue;
2408df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek                int newStartValue = previousStartValue + relativeDiff;
2418df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek                values[0].setIntValues(newStartValue, newEndValue);
2428df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek                child.setTag(TAG_START_HEIGHT, newStartValue);
2438df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek                child.setTag(TAG_END_HEIGHT, newEndValue);
2448df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek                previousAnimator.setCurrentPlayTime(previousAnimator.getCurrentPlayTime());
2458df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek                return;
2468df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek            } else {
2478df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek                // no new animation needed, let's just apply the value
2488df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek                child.setActualHeight(newEndValue, false);
2498df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek                return;
250eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek            }
251eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        }
252eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek
2538df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek        ValueAnimator animator = ValueAnimator.ofInt(child.getActualHeight(), newEndValue);
254eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
255572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek            @Override
256572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek            public void onAnimationUpdate(ValueAnimator animation) {
257d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi                child.setActualHeight((int) animation.getAnimatedValue(),
258d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi                        false /* notifyListeners */);
259572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek            }
260572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek        });
261eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        animator.setInterpolator(mFastOutSlowInInterpolator);
2628df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek        long newDuration = cancelAnimatorAndGetNewDuration(previousAnimator);
263eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        animator.setDuration(newDuration);
2648efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek        if (delay > 0 && (previousAnimator == null || !previousAnimator.isRunning())) {
2658efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek            animator.setStartDelay(delay);
2668efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek        }
267eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        animator.addListener(getGlobalAnimationFinishedListener());
268eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        // remove the tag when the animation is finished
269eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        animator.addListener(new AnimatorListenerAdapter() {
270eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek            @Override
271eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek            public void onAnimationEnd(Animator animation) {
272eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek                child.setTag(TAG_ANIMATOR_HEIGHT, null);
2738df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek                child.setTag(TAG_START_HEIGHT, null);
274eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek                child.setTag(TAG_END_HEIGHT, null);
275eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek            }
276eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        });
2778efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek        startAnimator(animator);
278eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        child.setTag(TAG_ANIMATOR_HEIGHT, animator);
2798df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek        child.setTag(TAG_START_HEIGHT, child.getActualHeight());
2808df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek        child.setTag(TAG_END_HEIGHT, newEndValue);
281eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek    }
282eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek
283eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek    private void startAlphaAnimation(final ExpandableView child,
2848efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek            final StackScrollState.ViewState viewState, long delay) {
2858df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek        Float previousStartValue = getChildTag(child,TAG_START_ALPHA);
286eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        Float previousEndValue = getChildTag(child,TAG_END_ALPHA);
2878df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek        final float newEndValue = viewState.alpha;
2888df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek        if (previousEndValue != null && previousEndValue == newEndValue) {
289eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek            return;
290eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        }
291eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        ObjectAnimator previousAnimator = getChildTag(child, TAG_ANIMATOR_ALPHA);
2928df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek        if (!mAnimationFilter.animateAlpha) {
2938df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek            // just a local update was performed
2948df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek            if (previousAnimator != null) {
2958df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek                // we need to increase all animation keyframes of the previous animator by the
2968df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek                // relative change to the end value
2978df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek                PropertyValuesHolder[] values = previousAnimator.getValues();
2988df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek                float relativeDiff = newEndValue - previousEndValue;
2998df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek                float newStartValue = previousStartValue + relativeDiff;
3008df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek                values[0].setFloatValues(newStartValue, newEndValue);
3018df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek                child.setTag(TAG_START_ALPHA, newStartValue);
3028df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek                child.setTag(TAG_END_ALPHA, newEndValue);
3038df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek                previousAnimator.setCurrentPlayTime(previousAnimator.getCurrentPlayTime());
3048df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek                return;
3058df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek            } else {
3068df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek                // no new animation needed, let's just apply the value
3078df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek                child.setAlpha(newEndValue);
3088df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek                if (newEndValue == 0) {
3098df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek                    child.setVisibility(View.INVISIBLE);
3108df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek                }
311eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek            }
312eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        }
313eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek
314eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        ObjectAnimator animator = ObjectAnimator.ofFloat(child, View.ALPHA,
3158df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek                child.getAlpha(), newEndValue);
316eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        animator.setInterpolator(mFastOutSlowInInterpolator);
317eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        // Handle layer type
318eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        final int currentLayerType = child.getLayerType();
319eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        child.setLayerType(View.LAYER_TYPE_HARDWARE, null);
320eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        animator.addListener(new AnimatorListenerAdapter() {
321eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek            public boolean mWasCancelled;
322eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek
323eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek            @Override
324eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek            public void onAnimationEnd(Animator animation) {
325eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek                child.setLayerType(currentLayerType, null);
3268df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek                if (newEndValue == 0 && !mWasCancelled) {
327eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek                    child.setVisibility(View.INVISIBLE);
328eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek                }
329eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek                child.setTag(TAG_ANIMATOR_ALPHA, null);
3308df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek                child.setTag(TAG_START_ALPHA, null);
331eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek                child.setTag(TAG_END_ALPHA, null);
332eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek            }
333eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek
334eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek            @Override
335eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek            public void onAnimationCancel(Animator animation) {
336eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek                mWasCancelled = true;
337eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek            }
338eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek
339eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek            @Override
340eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek            public void onAnimationStart(Animator animation) {
341eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek                mWasCancelled = false;
342eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek            }
343eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        });
3448df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek        long newDuration = cancelAnimatorAndGetNewDuration(previousAnimator);
3453af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek        animator.setDuration(newDuration);
3468efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek        if (delay > 0 && (previousAnimator == null || !previousAnimator.isRunning())) {
3478efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek            animator.setStartDelay(delay);
3488efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek        }
349eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        animator.addListener(getGlobalAnimationFinishedListener());
350eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        // remove the tag when the animation is finished
351eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        animator.addListener(new AnimatorListenerAdapter() {
352eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek            @Override
353eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek            public void onAnimationEnd(Animator animation) {
354eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek
355eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek            }
356eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        });
3578efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek        startAnimator(animator);
358eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        child.setTag(TAG_ANIMATOR_ALPHA, animator);
3598df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek        child.setTag(TAG_START_ALPHA, child.getAlpha());
3608df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek        child.setTag(TAG_END_ALPHA, newEndValue);
361572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek    }
362572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek
363eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek    private void startZTranslationAnimation(final ExpandableView child,
3648efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek            final StackScrollState.ViewState viewState, long delay) {
3658df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek        Float previousStartValue = getChildTag(child,TAG_START_TRANSLATION_Z);
366eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        Float previousEndValue = getChildTag(child,TAG_END_TRANSLATION_Z);
3678df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek        float newEndValue = viewState.zTranslation;
3688df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek        if (previousEndValue != null && previousEndValue == newEndValue) {
369eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek            return;
370eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        }
371eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        ObjectAnimator previousAnimator = getChildTag(child, TAG_ANIMATOR_TRANSLATION_Z);
3728df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek        if (!mAnimationFilter.animateZ) {
3738df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek            // just a local update was performed
3748df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek            if (previousAnimator != null) {
3758df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek                // we need to increase all animation keyframes of the previous animator by the
3768df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek                // relative change to the end value
3778df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek                PropertyValuesHolder[] values = previousAnimator.getValues();
3788df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek                float relativeDiff = newEndValue - previousEndValue;
3798df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek                float newStartValue = previousStartValue + relativeDiff;
3808df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek                values[0].setFloatValues(newStartValue, newEndValue);
3818df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek                child.setTag(TAG_START_TRANSLATION_Z, newStartValue);
3828df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek                child.setTag(TAG_END_TRANSLATION_Z, newEndValue);
3838df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek                previousAnimator.setCurrentPlayTime(previousAnimator.getCurrentPlayTime());
3848df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek                return;
3858df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek            } else {
3868df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek                // no new animation needed, let's just apply the value
3878df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek                child.setTranslationZ(newEndValue);
388eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek            }
389eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        }
390eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek
391eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        ObjectAnimator animator = ObjectAnimator.ofFloat(child, View.TRANSLATION_Z,
3928df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek                child.getTranslationZ(), newEndValue);
393eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        animator.setInterpolator(mFastOutSlowInInterpolator);
3948df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek        long newDuration = cancelAnimatorAndGetNewDuration(previousAnimator);
3953af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek        animator.setDuration(newDuration);
3968efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek        if (delay > 0 && (previousAnimator == null || !previousAnimator.isRunning())) {
3978efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek            animator.setStartDelay(delay);
3988efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek        }
399eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        animator.addListener(getGlobalAnimationFinishedListener());
400eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        // remove the tag when the animation is finished
401eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        animator.addListener(new AnimatorListenerAdapter() {
402eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek            @Override
403eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek            public void onAnimationEnd(Animator animation) {
404eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek                child.setTag(TAG_ANIMATOR_TRANSLATION_Z, null);
4058df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek                child.setTag(TAG_START_TRANSLATION_Z, null);
406eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek                child.setTag(TAG_END_TRANSLATION_Z, null);
407eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek            }
408eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        });
4098efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek        startAnimator(animator);
410eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        child.setTag(TAG_ANIMATOR_TRANSLATION_Z, animator);
4118df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek        child.setTag(TAG_START_TRANSLATION_Z, child.getTranslationZ());
4128df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek        child.setTag(TAG_END_TRANSLATION_Z, newEndValue);
413eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek    }
414eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek
415eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek    private void startYTranslationAnimation(final ExpandableView child,
4168efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek            StackScrollState.ViewState viewState, long delay) {
4178df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek        Float previousStartValue = getChildTag(child,TAG_START_TRANSLATION_Y);
418eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        Float previousEndValue = getChildTag(child,TAG_END_TRANSLATION_Y);
4198df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek        float newEndValue = viewState.yTranslation;
4208df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek        if (previousEndValue != null && previousEndValue == newEndValue) {
421eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek            return;
422eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        }
423eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        ObjectAnimator previousAnimator = getChildTag(child, TAG_ANIMATOR_TRANSLATION_Y);
4248df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek        if (!mAnimationFilter.animateY) {
4258df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek            // just a local update was performed
4268df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek            if (previousAnimator != null) {
4278df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek                // we need to increase all animation keyframes of the previous animator by the
4288df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek                // relative change to the end value
4298df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek                PropertyValuesHolder[] values = previousAnimator.getValues();
4308df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek                float relativeDiff = newEndValue - previousEndValue;
4318df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek                float newStartValue = previousStartValue + relativeDiff;
4328df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek                values[0].setFloatValues(newStartValue, newEndValue);
4338df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek                child.setTag(TAG_START_TRANSLATION_Y, newStartValue);
4348df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek                child.setTag(TAG_END_TRANSLATION_Y, newEndValue);
4358df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek                previousAnimator.setCurrentPlayTime(previousAnimator.getCurrentPlayTime());
4368df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek                return;
4378df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek            } else {
4388df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek                // no new animation needed, let's just apply the value
4398df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek                child.setTranslationY(newEndValue);
4408df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek                return;
441eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek            }
442eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        }
443eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek
444eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        ObjectAnimator animator = ObjectAnimator.ofFloat(child, View.TRANSLATION_Y,
4458df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek                child.getTranslationY(), newEndValue);
446eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        animator.setInterpolator(mFastOutSlowInInterpolator);
4478df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek        long newDuration = cancelAnimatorAndGetNewDuration(previousAnimator);
4483af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek        animator.setDuration(newDuration);
4498efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek        if (delay > 0 && (previousAnimator == null || !previousAnimator.isRunning())) {
4508efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek            animator.setStartDelay(delay);
4518efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek        }
452eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        animator.addListener(getGlobalAnimationFinishedListener());
453eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        // remove the tag when the animation is finished
454eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        animator.addListener(new AnimatorListenerAdapter() {
455eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek            @Override
456eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek            public void onAnimationEnd(Animator animation) {
457eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek                child.setTag(TAG_ANIMATOR_TRANSLATION_Y, null);
4588df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek                child.setTag(TAG_START_TRANSLATION_Y, null);
459eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek                child.setTag(TAG_END_TRANSLATION_Y, null);
460eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek            }
461eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        });
4628efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek        startAnimator(animator);
463eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        child.setTag(TAG_ANIMATOR_TRANSLATION_Y, animator);
4648df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek        child.setTag(TAG_START_TRANSLATION_Y, child.getTranslationY());
4658df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek        child.setTag(TAG_END_TRANSLATION_Y, newEndValue);
466eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek    }
467eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek
468d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi    private void startScaleAnimation(final ExpandableView child,
469d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi            StackScrollState.ViewState viewState) {
4708df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek        Float previousStartValue = getChildTag(child, TAG_START_SCALE);
471d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi        Float previousEndValue = getChildTag(child, TAG_END_SCALE);
4728df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek        float newEndValue = viewState.scale;
4738df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek        if (previousEndValue != null && previousEndValue == newEndValue) {
474d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi            return;
475d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi        }
476d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi        ObjectAnimator previousAnimator = getChildTag(child, TAG_ANIMATOR_SCALE);
4778df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek        if (!mAnimationFilter.animateScale) {
4788df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek            // just a local update was performed
4798df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek            if (previousAnimator != null) {
4808df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek                // we need to increase all animation keyframes of the previous animator by the
4818df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek                // relative change to the end value
4828df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek                PropertyValuesHolder[] values = previousAnimator.getValues();
4838df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek                float relativeDiff = newEndValue - previousEndValue;
4848df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek                float newStartValue = previousStartValue + relativeDiff;
4858df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek                values[0].setFloatValues(newStartValue, newEndValue);
4868df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek                values[1].setFloatValues(newStartValue, newEndValue);
4878df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek                child.setTag(TAG_START_SCALE, newStartValue);
4888df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek                child.setTag(TAG_END_SCALE, newEndValue);
4898df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek                previousAnimator.setCurrentPlayTime(previousAnimator.getCurrentPlayTime());
4908df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek                return;
4918df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek            } else {
4928df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek                // no new animation needed, let's just apply the value
4938df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek                child.setScaleX(newEndValue);
4948df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek                child.setScaleY(newEndValue);
495d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi            }
496d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi        }
497d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi
498d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi        PropertyValuesHolder holderX =
4998df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek                PropertyValuesHolder.ofFloat(View.SCALE_X, child.getScaleX(), newEndValue);
500d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi        PropertyValuesHolder holderY =
5018df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek                PropertyValuesHolder.ofFloat(View.SCALE_Y, child.getScaleY(), newEndValue);
502d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi        ObjectAnimator animator = ObjectAnimator.ofPropertyValuesHolder(child, holderX, holderY);
503d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi        animator.setInterpolator(mFastOutSlowInInterpolator);
5048df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek        long newDuration = cancelAnimatorAndGetNewDuration(previousAnimator);
505d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi        animator.setDuration(newDuration);
506d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi        animator.addListener(getGlobalAnimationFinishedListener());
507d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi        // remove the tag when the animation is finished
508d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi        animator.addListener(new AnimatorListenerAdapter() {
509d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi            @Override
510d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi            public void onAnimationEnd(Animator animation) {
511d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi                child.setTag(TAG_ANIMATOR_SCALE, null);
5128df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek                child.setTag(TAG_START_SCALE, null);
513d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi                child.setTag(TAG_END_SCALE, null);
514d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi            }
515d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi        });
5168efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek        startAnimator(animator);
517d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi        child.setTag(TAG_ANIMATOR_SCALE, animator);
5188df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek        child.setTag(TAG_START_SCALE, child.getScaleX());
5198df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek        child.setTag(TAG_END_SCALE, newEndValue);
520d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi    }
521d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi
5228efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek    private void startAnimator(ValueAnimator animator) {
5238efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek        mAnimatorSet.add(animator);
5243af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek        animator.start();
5253af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek    }
5263af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek
5273af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek    /**
5283af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek     * @return an adapter which ensures that onAnimationFinished is called once no animation is
5293af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek     *         running anymore
5303af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek     */
5313af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek    private AnimatorListenerAdapter getGlobalAnimationFinishedListener() {
5323af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek        if (!mAnimationListenerPool.empty()) {
5333af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek            return mAnimationListenerPool.pop();
5343af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek        }
5353af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek
5363af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek        // We need to create a new one, no reusable ones found
5373af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek        return new AnimatorListenerAdapter() {
5383af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek            private boolean mWasCancelled;
5393af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek
5403af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek            @Override
5413af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek            public void onAnimationEnd(Animator animation) {
5423af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek                mAnimatorSet.remove(animation);
5433af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek                if (mAnimatorSet.isEmpty() && !mWasCancelled) {
5443af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek                    onAnimationFinished();
5453af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek                }
5463af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek                mAnimationListenerPool.push(this);
5473af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek            }
5483af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek
5493af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek            @Override
5503af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek            public void onAnimationCancel(Animator animation) {
5513af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek                mWasCancelled = true;
5523af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek            }
5533af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek
5543af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek            @Override
5553af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek            public void onAnimationStart(Animator animation) {
5563af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek                mWasCancelled = false;
5573af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek            }
5583af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek        };
5593af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek    }
5603af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek
561eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek    private <T> T getChildTag(View child, int tag) {
562eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        return (T) child.getTag(tag);
563eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek    }
564eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek
565eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek    /**
566eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek     * Cancel the previous animator and get the duration of the new animation.
56739610545f0c2714a3526bc935effe57b421542d1Selim Cinek     *
568eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek     * @param previousAnimator the animator which was running before
569eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek     * @return the new duration
570eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek     */
5718df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek    private long cancelAnimatorAndGetNewDuration(ValueAnimator previousAnimator) {
5725aa045cc6bca84f5c11f1a99999546ba5e5949a5Jorim Jaggi        long newDuration = mCurrentLength;
573eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        if (previousAnimator != null) {
5748df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek            // We take either the desired length of the new animation or the remaining time of
5758df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek            // the previous animator, whichever is longer.
5768df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek            newDuration = Math.max(previousAnimator.getDuration()
5778df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek                    - previousAnimator.getCurrentPlayTime(), newDuration);
5783af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek            previousAnimator.cancel();
579eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        }
5803af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek        return newDuration;
581eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek    }
582eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek
583eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek    private void onAnimationFinished() {
584eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        mHostLayout.onChildAnimationFinished();
585eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek    }
586eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek
587eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek    /**
588eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek     * Process the animationEvents for a new animation
589eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek     *
590eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek     * @param animationEvents the animation events for the animation to perform
59139610545f0c2714a3526bc935effe57b421542d1Selim Cinek     * @param finalState the final state to animate to
59239610545f0c2714a3526bc935effe57b421542d1Selim Cinek     */
593eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek    private void processAnimationEvents(
5940dd6881ea481c855976214807c17595b34a2920aJorim Jaggi            ArrayList<NotificationStackScrollLayout.AnimationEvent> animationEvents,
595572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek            StackScrollState finalState) {
5968d9ff9c2c66bc1d3b92eb6992d58599ff80ed6dcSelim Cinek        for (NotificationStackScrollLayout.AnimationEvent event : animationEvents) {
5978efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek            final ExpandableView changingView = (ExpandableView) event.changingView;
5988efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek            if (event.animationType ==
5998efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek                    NotificationStackScrollLayout.AnimationEvent.ANIMATION_TYPE_ADD) {
6008efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek
6018efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek                // This item is added, initialize it's properties.
6028efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek                StackScrollState.ViewState viewState = finalState
6038efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek                        .getViewStateForView(changingView);
6048efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek                if (viewState == null) {
6058efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek                    // The position for this child was never generated, let's continue.
6068efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek                    continue;
6078efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek                }
6088efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek                if (changingView.getVisibility() == View.GONE) {
6098efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek                    // The view was set to gone but the state never removed
6108efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek                    finalState.removeViewStateForView(changingView);
6118efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek                    continue;
61239610545f0c2714a3526bc935effe57b421542d1Selim Cinek                }
6138efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek                changingView.setAlpha(viewState.alpha);
6148efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek                changingView.setTranslationY(viewState.yTranslation);
6158efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek                changingView.setTranslationZ(viewState.zTranslation);
6168efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek                changingView.setActualHeight(viewState.height, false);
6178efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek                mNewAddChildren.add(changingView);
6188efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek
6198efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek            } else if (event.animationType ==
6208efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek                    NotificationStackScrollLayout.AnimationEvent.ANIMATION_TYPE_REMOVE) {
6218efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek                if (changingView.getVisibility() == View.GONE) {
6228efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek                    continue;
6238efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek                }
6248efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek
6258efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek                // Find the amount to translate up. This is needed in order to understand the
6268efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek                // direction of the remove animation (either downwards or upwards)
6278efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek                StackScrollState.ViewState viewState = finalState
6288efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek                        .getViewStateForView(event.viewAfterChangingView);
6298efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek                int actualHeight = changingView.getActualHeight();
6308efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek                // upwards by default
6318efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek                float translationDirection = -1.0f;
6328efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek                if (viewState != null) {
6338efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek                    // there was a view after this one, Approximate the distance the next child
6348efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek                    // travelled
6358efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek                    translationDirection = ((viewState.yTranslation
6368efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek                            - (changingView.getTranslationY() + actualHeight / 2.0f)) * 2 /
6378efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek                            actualHeight);
6388efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek                    translationDirection = Math.max(Math.min(translationDirection, 1.0f),-1.0f);
6398efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek
6408efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek                }
6418efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek                changingView.performRemoveAnimation(translationDirection, new Runnable() {
6428efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek                    @Override
6438efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek                    public void run() {
6448efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek                        // remove the temporary overlay
6458efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek                        mHostLayout.getOverlay().remove(changingView);
6468efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek                    }
6478efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek                });
648572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek            }
6498efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek            mNewEvents.add(event);
650572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek        }
651572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek    }
6528d9ff9c2c66bc1d3b92eb6992d58599ff80ed6dcSelim Cinek
6538d9ff9c2c66bc1d3b92eb6992d58599ff80ed6dcSelim Cinek    public void animateOverScrollToAmount(float targetAmount, final boolean onTop) {
6548d9ff9c2c66bc1d3b92eb6992d58599ff80ed6dcSelim Cinek        final float startOverScrollAmount = mHostLayout.getCurrentOverScrollAmount(onTop);
6558d9ff9c2c66bc1d3b92eb6992d58599ff80ed6dcSelim Cinek        cancelOverScrollAnimators(onTop);
6568d9ff9c2c66bc1d3b92eb6992d58599ff80ed6dcSelim Cinek        ValueAnimator overScrollAnimator = ValueAnimator.ofFloat(startOverScrollAmount,
6578d9ff9c2c66bc1d3b92eb6992d58599ff80ed6dcSelim Cinek                targetAmount);
6588d9ff9c2c66bc1d3b92eb6992d58599ff80ed6dcSelim Cinek        overScrollAnimator.setDuration(ANIMATION_DURATION_STANDARD);
6598d9ff9c2c66bc1d3b92eb6992d58599ff80ed6dcSelim Cinek        overScrollAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
6608d9ff9c2c66bc1d3b92eb6992d58599ff80ed6dcSelim Cinek            @Override
6618d9ff9c2c66bc1d3b92eb6992d58599ff80ed6dcSelim Cinek            public void onAnimationUpdate(ValueAnimator animation) {
6628d9ff9c2c66bc1d3b92eb6992d58599ff80ed6dcSelim Cinek                float currentOverScroll = (float) animation.getAnimatedValue();
6638d9ff9c2c66bc1d3b92eb6992d58599ff80ed6dcSelim Cinek                mHostLayout.setOverScrollAmount(currentOverScroll, onTop, false /* animate */,
6648d9ff9c2c66bc1d3b92eb6992d58599ff80ed6dcSelim Cinek                        false /* cancelAnimators */);
6658d9ff9c2c66bc1d3b92eb6992d58599ff80ed6dcSelim Cinek            }
6668d9ff9c2c66bc1d3b92eb6992d58599ff80ed6dcSelim Cinek        });
6678d9ff9c2c66bc1d3b92eb6992d58599ff80ed6dcSelim Cinek        overScrollAnimator.setInterpolator(mFastOutSlowInInterpolator);
6688d9ff9c2c66bc1d3b92eb6992d58599ff80ed6dcSelim Cinek        overScrollAnimator.start();
6698d9ff9c2c66bc1d3b92eb6992d58599ff80ed6dcSelim Cinek        if (onTop) {
6708d9ff9c2c66bc1d3b92eb6992d58599ff80ed6dcSelim Cinek            mTopOverScrollAnimator = overScrollAnimator;
6718d9ff9c2c66bc1d3b92eb6992d58599ff80ed6dcSelim Cinek        } else {
6728d9ff9c2c66bc1d3b92eb6992d58599ff80ed6dcSelim Cinek            mBottomOverScrollAnimator = overScrollAnimator;
6738d9ff9c2c66bc1d3b92eb6992d58599ff80ed6dcSelim Cinek        }
6748d9ff9c2c66bc1d3b92eb6992d58599ff80ed6dcSelim Cinek    }
6758d9ff9c2c66bc1d3b92eb6992d58599ff80ed6dcSelim Cinek
6768d9ff9c2c66bc1d3b92eb6992d58599ff80ed6dcSelim Cinek    public void cancelOverScrollAnimators(boolean onTop) {
6778d9ff9c2c66bc1d3b92eb6992d58599ff80ed6dcSelim Cinek        ValueAnimator currentAnimator = onTop ? mTopOverScrollAnimator : mBottomOverScrollAnimator;
6788d9ff9c2c66bc1d3b92eb6992d58599ff80ed6dcSelim Cinek        if (currentAnimator != null) {
6798d9ff9c2c66bc1d3b92eb6992d58599ff80ed6dcSelim Cinek            currentAnimator.cancel();
6808d9ff9c2c66bc1d3b92eb6992d58599ff80ed6dcSelim Cinek        }
6818d9ff9c2c66bc1d3b92eb6992d58599ff80ed6dcSelim Cinek    }
682572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek}
683