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;
25a5703187a9dba418f742fab0874a8f683b0bbb0eSelim Cinekimport android.view.ViewGroup;
26572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinekimport android.view.animation.Interpolator;
27eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek
28c0d7058b14c24cd07912f5629c26b39b7b4673d5Winsonimport com.android.systemui.Interpolators;
29eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinekimport com.android.systemui.R;
30b5605e58cb8080c8c887b1885336b707596c8094Selim Cinekimport com.android.systemui.statusbar.ExpandableNotificationRow;
31572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinekimport com.android.systemui.statusbar.ExpandableView;
320fccc729fd3b19a62efd90ae13b207faa3d1e8bbSelim Cinekimport com.android.systemui.statusbar.policy.HeadsUpManager;
33572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek
34572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinekimport java.util.ArrayList;
35eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinekimport java.util.HashSet;
36eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinekimport java.util.Stack;
37572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek
38572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek/**
39572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek * An stack state animator which handles animations to new StackScrollStates
40572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek */
41572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinekpublic class StackStateAnimator {
42572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek
435aa045cc6bca84f5c11f1a99999546ba5e5949a5Jorim Jaggi    public static final int ANIMATION_DURATION_STANDARD = 360;
4460d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi    public static final int ANIMATION_DURATION_GO_TO_FULL_SHADE = 448;
458efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek    public static final int ANIMATION_DURATION_APPEAR_DISAPPEAR = 464;
465aa045cc6bca84f5c11f1a99999546ba5e5949a5Jorim Jaggi    public static final int ANIMATION_DURATION_DIMMED_ACTIVATED = 220;
47a7840af09d808ecd3f7547bbdea58d4422bd4d8fSelim Cinek    public static final int ANIMATION_DURATION_HEADS_UP_APPEAR = 650;
48a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek    public static final int ANIMATION_DURATION_HEADS_UP_DISAPPEAR = 230;
498efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek    public static final int ANIMATION_DELAY_PER_ELEMENT_INTERRUPTING = 80;
508efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek    public static final int ANIMATION_DELAY_PER_ELEMENT_MANUAL = 32;
5160d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi    public static final int ANIMATION_DELAY_PER_ELEMENT_GO_TO_FULL_SHADE = 48;
524e857f4ef0357e05806819d0488a73a12208fe8fJorim Jaggi    public static final int ANIMATION_DELAY_PER_ELEMENT_DARK = 24;
53b5605e58cb8080c8c887b1885336b707596c8094Selim Cinek    public static final int DELAY_EFFECT_MAX_INDEX_DIFFERENCE = 2;
545eb67c22489f5fda22770d1fc824e7a8013ec025Jorim Jaggi    public static final int ANIMATION_DELAY_HEADS_UP = 120;
555aa045cc6bca84f5c11f1a99999546ba5e5949a5Jorim Jaggi
56eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek    private static final int TAG_ANIMATOR_TRANSLATION_Y = R.id.translation_y_animator_tag;
57eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek    private static final int TAG_ANIMATOR_TRANSLATION_Z = R.id.translation_z_animator_tag;
58eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek    private static final int TAG_ANIMATOR_ALPHA = R.id.alpha_animator_tag;
59eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek    private static final int TAG_ANIMATOR_HEIGHT = R.id.height_animator_tag;
60eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek    private static final int TAG_ANIMATOR_TOP_INSET = R.id.top_inset_animator_tag;
61277a8aa409def1a35eb662c974b91c0f133926aaSelim Cinek    private static final int TAG_ANIMATOR_SHADOW_ALPHA = R.id.shadow_alpha_animator_tag;
62eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek    private static final int TAG_END_TRANSLATION_Y = R.id.translation_y_animator_end_value_tag;
63eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek    private static final int TAG_END_TRANSLATION_Z = R.id.translation_z_animator_end_value_tag;
64eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek    private static final int TAG_END_ALPHA = R.id.alpha_animator_end_value_tag;
65eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek    private static final int TAG_END_HEIGHT = R.id.height_animator_end_value_tag;
66eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek    private static final int TAG_END_TOP_INSET = R.id.top_inset_animator_end_value_tag;
67277a8aa409def1a35eb662c974b91c0f133926aaSelim Cinek    private static final int TAG_END_SHADOW_ALPHA = R.id.shadow_alpha_animator_end_value_tag;
688df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek    private static final int TAG_START_TRANSLATION_Y = R.id.translation_y_animator_start_value_tag;
698df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek    private static final int TAG_START_TRANSLATION_Z = R.id.translation_z_animator_start_value_tag;
708df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek    private static final int TAG_START_ALPHA = R.id.alpha_animator_start_value_tag;
718df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek    private static final int TAG_START_HEIGHT = R.id.height_animator_start_value_tag;
728df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek    private static final int TAG_START_TOP_INSET = R.id.top_inset_animator_start_value_tag;
73277a8aa409def1a35eb662c974b91c0f133926aaSelim Cinek    private static final int TAG_START_SHADOW_ALPHA = R.id.shadow_alpha_animator_start_value_tag;
74572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek
75a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek    private final Interpolator mHeadsUpAppearInterpolator;
7660d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi    private final int mGoToFullShadeAppearingTranslation;
77684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek    private final StackViewState mTmpState = new StackViewState();
78572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek    public NotificationStackScrollLayout mHostLayout;
79eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek    private ArrayList<NotificationStackScrollLayout.AnimationEvent> mNewEvents =
80eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek            new ArrayList<>();
818efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek    private ArrayList<View> mNewAddChildren = new ArrayList<>();
82a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek    private HashSet<View> mHeadsUpAppearChildren = new HashSet<>();
83a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek    private HashSet<View> mHeadsUpDisappearChildren = new HashSet<>();
84a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek    private HashSet<Animator> mAnimatorSet = new HashSet<>();
8560d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi    private Stack<AnimatorListenerAdapter> mAnimationListenerPool = new Stack<>();
86d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi    private AnimationFilter mAnimationFilter = new AnimationFilter();
875aa045cc6bca84f5c11f1a99999546ba5e5949a5Jorim Jaggi    private long mCurrentLength;
88dbc3dce886cb7abba23a9f18e60d0f5af5c93226Jorim Jaggi    private long mCurrentAdditionalDelay;
89572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek
9060d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi    /** The current index for the last child which was not added in this event set. */
9160d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi    private int mCurrentLastNotAddedIndex;
928d9ff9c2c66bc1d3b92eb6992d58599ff80ed6dcSelim Cinek    private ValueAnimator mTopOverScrollAnimator;
938d9ff9c2c66bc1d3b92eb6992d58599ff80ed6dcSelim Cinek    private ValueAnimator mBottomOverScrollAnimator;
94a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek    private int mHeadsUpAppearHeightBottom;
95a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek    private boolean mShadeExpanded;
968f93763cf0f4b3c41f6b0ecac09b621eb0ca328bSelim Cinek    private ArrayList<View> mChildrenToClearFromOverlay = new ArrayList<>();
978d9ff9c2c66bc1d3b92eb6992d58599ff80ed6dcSelim Cinek
98572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek    public StackStateAnimator(NotificationStackScrollLayout hostLayout) {
99572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek        mHostLayout = hostLayout;
10060d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi        mGoToFullShadeAppearingTranslation =
10160d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi                hostLayout.getContext().getResources().getDimensionPixelSize(
10260d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi                        R.dimen.go_to_full_shade_appearing_translation);
103684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek        mHeadsUpAppearInterpolator = new HeadsUpAppearInterpolator();
104572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek    }
105572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek
106572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek    public boolean isRunning() {
107eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        return !mAnimatorSet.isEmpty();
108572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek    }
109572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek
110572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek    public void startAnimationForEvents(
1110dd6881ea481c855976214807c17595b34a2920aJorim Jaggi            ArrayList<NotificationStackScrollLayout.AnimationEvent> mAnimationEvents,
112dbc3dce886cb7abba23a9f18e60d0f5af5c93226Jorim Jaggi            StackScrollState finalState, long additionalDelay) {
113eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek
114eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        processAnimationEvents(mAnimationEvents, finalState);
115eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek
116572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek        int childCount = mHostLayout.getChildCount();
117d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi        mAnimationFilter.applyCombination(mNewEvents);
118dbc3dce886cb7abba23a9f18e60d0f5af5c93226Jorim Jaggi        mCurrentAdditionalDelay = additionalDelay;
1195aa045cc6bca84f5c11f1a99999546ba5e5949a5Jorim Jaggi        mCurrentLength = NotificationStackScrollLayout.AnimationEvent.combineLength(mNewEvents);
12060d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi        mCurrentLastNotAddedIndex = findLastNotAddedIndex(finalState);
121572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek        for (int i = 0; i < childCount; i++) {
122572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek            final ExpandableView child = (ExpandableView) mHostLayout.getChildAt(i);
1238efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek
124b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek            StackViewState viewState = finalState.getViewStateForView(child);
125a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek            if (viewState == null || child.getVisibility() == View.GONE
126a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek                    || applyWithoutAnimation(child, viewState, finalState)) {
127572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek                continue;
128572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek            }
129572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek
130b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek            startStackAnimations(child, viewState, finalState, i, -1 /* fixedDelay */);
131572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek        }
132eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        if (!isRunning()) {
133eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek            // no child has preformed any animation, lets finish
134eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek            onAnimationFinished();
135eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        }
136a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek        mHeadsUpAppearChildren.clear();
137a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek        mHeadsUpDisappearChildren.clear();
1388efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek        mNewEvents.clear();
1398efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek        mNewAddChildren.clear();
140572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek    }
141572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek
142a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek    /**
143a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek     * Determines if a view should not perform an animation and applies it directly.
144a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek     *
145a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek     * @return true if no animation should be performed
146a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek     */
147a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek    private boolean applyWithoutAnimation(ExpandableView child, StackViewState viewState,
148a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek            StackScrollState finalState) {
149a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek        if (mShadeExpanded) {
150a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek            return false;
151a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek        }
152a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek        if (getChildTag(child, TAG_ANIMATOR_TRANSLATION_Y) != null) {
153a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek            // A Y translation animation is running
154a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek            return false;
155a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek        }
156a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek        if (mHeadsUpDisappearChildren.contains(child) || mHeadsUpAppearChildren.contains(child)) {
157a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek            // This is a heads up animation
158a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek            return false;
159a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek        }
160131c1e2960fa5bdf54bfb6fcd5ac98c9f728f796Selim Cinek        if (NotificationStackScrollLayout.isPinnedHeadsUp(child)) {
1611f3f544f21cab3728f749ab66cd6859e9dfcf389Selim Cinek            // This is another headsUp which might move. Let's animate!
1621f3f544f21cab3728f749ab66cd6859e9dfcf389Selim Cinek            return false;
1631f3f544f21cab3728f749ab66cd6859e9dfcf389Selim Cinek        }
164a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek        finalState.applyState(child, viewState);
165a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek        return true;
166a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek    }
167a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek
16860d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi    private int findLastNotAddedIndex(StackScrollState finalState) {
16960d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi        int childCount = mHostLayout.getChildCount();
17060d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi        for (int i = childCount - 1; i >= 0; i--) {
17160d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi            final ExpandableView child = (ExpandableView) mHostLayout.getChildAt(i);
17260d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi
173b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek            StackViewState viewState = finalState.getViewStateForView(child);
17460d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi            if (viewState == null || child.getVisibility() == View.GONE) {
17560d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi                continue;
17660d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi            }
17760d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi            if (!mNewAddChildren.contains(child)) {
17860d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi                return viewState.notGoneIndex;
17960d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi            }
18060d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi        }
18160d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi        return -1;
18260d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi    }
18360d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi
184b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek
185eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek    /**
186b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek     * Start an animation to the given  {@link StackViewState}.
187b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek     *
188b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek     * @param child the child to start the animation on
189b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek     * @param viewState the {@link StackViewState} of the view to animate to
190b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek     * @param finalState the final state after the animation
191b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek     * @param i the index of the view; only relevant if the view is the speed bump and is
192b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek     *          ignored otherwise
193b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek     * @param fixedDelay a fixed delay if desired or -1 if the delay should be calculated
194eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek     */
195b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek    public void startStackAnimations(final ExpandableView child, StackViewState viewState,
196b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek            StackScrollState finalState, int i, long fixedDelay) {
197b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek        boolean wasAdded = mNewAddChildren.contains(child);
198b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek        long duration = mCurrentLength;
199b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek        if (wasAdded && mAnimationFilter.hasGoToFullShadeEvent) {
200b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek            child.setTranslationY(child.getTranslationY() + mGoToFullShadeAppearingTranslation);
201b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek            float longerDurationFactor = viewState.notGoneIndex - mCurrentLastNotAddedIndex;
202b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek            longerDurationFactor = (float) Math.pow(longerDurationFactor, 0.7f);
203b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek            duration = ANIMATION_DURATION_APPEAR_DISAPPEAR + 50 +
204b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek                    (long) (100 * longerDurationFactor);
205eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        }
2068efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek        boolean yTranslationChanging = child.getTranslationY() != viewState.yTranslation;
2078efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek        boolean zTranslationChanging = child.getTranslationZ() != viewState.zTranslation;
208277a8aa409def1a35eb662c974b91c0f133926aaSelim Cinek        boolean alphaChanging = viewState.alpha != child.getAlpha();
2098efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek        boolean heightChanging = viewState.height != child.getActualHeight();
210277a8aa409def1a35eb662c974b91c0f133926aaSelim Cinek        boolean shadowAlphaChanging = viewState.shadowAlpha != child.getShadowAlpha();
2114e857f4ef0357e05806819d0488a73a12208fe8fJorim Jaggi        boolean darkChanging = viewState.dark != child.isDark();
212708a6c120da6750d281195ef15a240a5627efed4Selim Cinek        boolean topInsetChanging = viewState.clipTopAmount != child.getClipTopAmount();
2138efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek        boolean hasDelays = mAnimationFilter.hasDelays;
214587cbf30a7be07b502ab78b21c9f4e24370f8fceSelim Cinek        boolean isDelayRelevant = yTranslationChanging || zTranslationChanging || alphaChanging
215277a8aa409def1a35eb662c974b91c0f133926aaSelim Cinek                || heightChanging || topInsetChanging || darkChanging || shadowAlphaChanging;
2168efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek        long delay = 0;
217b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek        if (fixedDelay != -1) {
218b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek            delay = fixedDelay;
219b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek        } else if (hasDelays && isDelayRelevant || wasAdded) {
220dbc3dce886cb7abba23a9f18e60d0f5af5c93226Jorim Jaggi            delay = mCurrentAdditionalDelay + calculateChildAnimationDelay(viewState, finalState);
2218efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek        }
2228efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek
223b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek        startViewAnimations(child, viewState, delay, duration);
2248efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek
225eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        // start height animation
226b0a824687f56b6950338aad169d8d837f8ed657bMady Mellor        if (heightChanging) {
227b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek            startHeightAnimation(child, viewState, duration, delay);
228843f54173743abe8cd4aa83b87d38e6b8b4e0ab3Selim Cinek        }  else {
229843f54173743abe8cd4aa83b87d38e6b8b4e0ab3Selim Cinek            abortAnimation(child, TAG_ANIMATOR_HEIGHT);
23059b5a356b828fe60ea2874b0680a1bf7c84809a1Jorim Jaggi        }
2318efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek
232277a8aa409def1a35eb662c974b91c0f133926aaSelim Cinek        // start shadow alpha animation
233277a8aa409def1a35eb662c974b91c0f133926aaSelim Cinek        if (shadowAlphaChanging) {
234277a8aa409def1a35eb662c974b91c0f133926aaSelim Cinek            startShadowAlphaAnimation(child, viewState, duration, delay);
235843f54173743abe8cd4aa83b87d38e6b8b4e0ab3Selim Cinek        } else {
236843f54173743abe8cd4aa83b87d38e6b8b4e0ab3Selim Cinek            abortAnimation(child, TAG_ANIMATOR_SHADOW_ALPHA);
237277a8aa409def1a35eb662c974b91c0f133926aaSelim Cinek        }
238277a8aa409def1a35eb662c974b91c0f133926aaSelim Cinek
239708a6c120da6750d281195ef15a240a5627efed4Selim Cinek        // start top inset animation
240708a6c120da6750d281195ef15a240a5627efed4Selim Cinek        if (topInsetChanging) {
241b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek            startInsetAnimation(child, viewState, duration, delay);
242843f54173743abe8cd4aa83b87d38e6b8b4e0ab3Selim Cinek        } else {
243843f54173743abe8cd4aa83b87d38e6b8b4e0ab3Selim Cinek            abortAnimation(child, TAG_ANIMATOR_TOP_INSET);
244708a6c120da6750d281195ef15a240a5627efed4Selim Cinek        }
245708a6c120da6750d281195ef15a240a5627efed4Selim Cinek
246d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi        // start dimmed animation
247b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek        child.setDimmed(viewState.dimmed, mAnimationFilter.animateDimmed);
2488efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek
2493d2b94bf8e32640e57573ebb17911b1db9440231Selim Cinek        // apply speed bump state
2503d2b94bf8e32640e57573ebb17911b1db9440231Selim Cinek        child.setBelowSpeedBump(viewState.belowSpeedBump);
2513d2b94bf8e32640e57573ebb17911b1db9440231Selim Cinek
252ae44128776410abd11bd06ae700db9cc4606a773Jorim Jaggi        // start hiding sensitive animation
253b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek        child.setHideSensitive(viewState.hideSensitive, mAnimationFilter.animateHideSensitive,
254b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek                delay, duration);
255ae44128776410abd11bd06ae700db9cc4606a773Jorim Jaggi
256021eee53e52c880a529ece28a3a9edb3cd0776b7Jorim Jaggi        // start dark animation
257021eee53e52c880a529ece28a3a9edb3cd0776b7Jorim Jaggi        child.setDark(viewState.dark, mAnimationFilter.animateDark, delay);
258021eee53e52c880a529ece28a3a9edb3cd0776b7Jorim Jaggi
2598efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek        if (wasAdded) {
26060d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi            child.performAddAnimation(delay, mCurrentLength);
2618efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek        }
2624fd5dfc1c3c356c62db329b2c1ccc00e3e75dabcSelim Cinek        if (child instanceof ExpandableNotificationRow) {
263b5605e58cb8080c8c887b1885336b707596c8094Selim Cinek            ExpandableNotificationRow row = (ExpandableNotificationRow) child;
2649969559da02f54fcbd7832de9810507a581d98d8Selim Cinek            row.startChildAnimation(finalState, this, delay, duration);
2658b73006a36b3000a9847534dcb01a2e7066e9d93Jorim Jaggi        }
2668efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek    }
2678efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek
268b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek    /**
269b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek     * Start an animation to a new {@link ViewState}.
270b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek     *
271b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek     * @param child the child to start the animation on
272b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek     * @param viewState the  {@link StackViewState} of the view to animate to
273b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek     * @param delay a fixed delay
274b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek     * @param duration the duration of the animation
275b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek     */
276b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek    public void startViewAnimations(View child, ViewState viewState, long delay, long duration) {
277b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek        boolean wasVisible = child.getVisibility() == View.VISIBLE;
278b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek        final float alpha = viewState.alpha;
279b0a824687f56b6950338aad169d8d837f8ed657bMady Mellor        if (!wasVisible && (alpha != 0 || child.getAlpha() != 0)
280b0a824687f56b6950338aad169d8d837f8ed657bMady Mellor                && !viewState.gone && !viewState.hidden) {
281b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek            child.setVisibility(View.VISIBLE);
282b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek        }
283b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek        boolean yTranslationChanging = child.getTranslationY() != viewState.yTranslation;
284b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek        boolean zTranslationChanging = child.getTranslationZ() != viewState.zTranslation;
285277a8aa409def1a35eb662c974b91c0f133926aaSelim Cinek        float childAlpha = child.getAlpha();
286b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek        boolean alphaChanging = viewState.alpha != childAlpha;
2872cd45dfba6a9105a305ea20b110ba5ac078a9dc6Selim Cinek        if (child instanceof ExpandableView) {
2882cd45dfba6a9105a305ea20b110ba5ac078a9dc6Selim Cinek            // We don't want views to change visibility when they are animating to GONE
2892cd45dfba6a9105a305ea20b110ba5ac078a9dc6Selim Cinek            alphaChanging &= !((ExpandableView) child).willBeGone();
2902cd45dfba6a9105a305ea20b110ba5ac078a9dc6Selim Cinek        }
291b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek
292b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek        // start translationY animation
293b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek        if (yTranslationChanging) {
294b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek            startYTranslationAnimation(child, viewState, duration, delay);
295843f54173743abe8cd4aa83b87d38e6b8b4e0ab3Selim Cinek        } else {
296843f54173743abe8cd4aa83b87d38e6b8b4e0ab3Selim Cinek            abortAnimation(child, TAG_ANIMATOR_TRANSLATION_Y);
297b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek        }
298b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek
299b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek        // start translationZ animation
300b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek        if (zTranslationChanging) {
301b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek            startZTranslationAnimation(child, viewState, duration, delay);
302843f54173743abe8cd4aa83b87d38e6b8b4e0ab3Selim Cinek        } else {
303843f54173743abe8cd4aa83b87d38e6b8b4e0ab3Selim Cinek            abortAnimation(child, TAG_ANIMATOR_TRANSLATION_Z);
304b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek        }
305b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek
306b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek        // start alpha animation
307b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek        if (alphaChanging && child.getTranslationX() == 0) {
308b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek            startAlphaAnimation(child, viewState, duration, delay);
309843f54173743abe8cd4aa83b87d38e6b8b4e0ab3Selim Cinek        }  else {
310843f54173743abe8cd4aa83b87d38e6b8b4e0ab3Selim Cinek            abortAnimation(child, TAG_ANIMATOR_ALPHA);
311843f54173743abe8cd4aa83b87d38e6b8b4e0ab3Selim Cinek        }
312843f54173743abe8cd4aa83b87d38e6b8b4e0ab3Selim Cinek    }
313843f54173743abe8cd4aa83b87d38e6b8b4e0ab3Selim Cinek
314843f54173743abe8cd4aa83b87d38e6b8b4e0ab3Selim Cinek    private void abortAnimation(View child, int animatorTag) {
315843f54173743abe8cd4aa83b87d38e6b8b4e0ab3Selim Cinek        Animator previousAnimator = getChildTag(child, animatorTag);
316843f54173743abe8cd4aa83b87d38e6b8b4e0ab3Selim Cinek        if (previousAnimator != null) {
317843f54173743abe8cd4aa83b87d38e6b8b4e0ab3Selim Cinek            previousAnimator.cancel();
318b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek        }
319b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek    }
320b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek
321b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek    private long calculateChildAnimationDelay(StackViewState viewState,
3228efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek            StackScrollState finalState) {
3234e857f4ef0357e05806819d0488a73a12208fe8fJorim Jaggi        if (mAnimationFilter.hasDarkEvent) {
3244e857f4ef0357e05806819d0488a73a12208fe8fJorim Jaggi            return calculateDelayDark(viewState);
3254e857f4ef0357e05806819d0488a73a12208fe8fJorim Jaggi        }
32660d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi        if (mAnimationFilter.hasGoToFullShadeEvent) {
32760d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi            return calculateDelayGoToFullShade(viewState);
32860d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi        }
3295eb67c22489f5fda22770d1fc824e7a8013ec025Jorim Jaggi        if (mAnimationFilter.hasHeadsUpDisappearClickEvent) {
3305eb67c22489f5fda22770d1fc824e7a8013ec025Jorim Jaggi            return ANIMATION_DELAY_HEADS_UP;
3315eb67c22489f5fda22770d1fc824e7a8013ec025Jorim Jaggi        }
3328efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek        long minDelay = 0;
3338efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek        for (NotificationStackScrollLayout.AnimationEvent event : mNewEvents) {
3348efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek            long delayPerElement = ANIMATION_DELAY_PER_ELEMENT_INTERRUPTING;
3358efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek            switch (event.animationType) {
3368efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek                case NotificationStackScrollLayout.AnimationEvent.ANIMATION_TYPE_ADD: {
3378efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek                    int ownIndex = viewState.notGoneIndex;
3388efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek                    int changingIndex = finalState
3398efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek                            .getViewStateForView(event.changingView).notGoneIndex;
3408efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek                    int difference = Math.abs(ownIndex - changingIndex);
3418efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek                    difference = Math.max(0, Math.min(DELAY_EFFECT_MAX_INDEX_DIFFERENCE,
3428efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek                            difference - 1));
3438efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek                    long delay = (DELAY_EFFECT_MAX_INDEX_DIFFERENCE - difference) * delayPerElement;
3448efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek                    minDelay = Math.max(delay, minDelay);
3458efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek                    break;
3468efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek                }
3478efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek                case NotificationStackScrollLayout.AnimationEvent.ANIMATION_TYPE_REMOVE_SWIPED_OUT:
3488efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek                    delayPerElement = ANIMATION_DELAY_PER_ELEMENT_MANUAL;
3498efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek                case NotificationStackScrollLayout.AnimationEvent.ANIMATION_TYPE_REMOVE: {
3508efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek                    int ownIndex = viewState.notGoneIndex;
3518efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek                    boolean noNextView = event.viewAfterChangingView == null;
3528efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek                    View viewAfterChangingView = noNextView
3538efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek                            ? mHostLayout.getLastChildNotGone()
3548efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek                            : event.viewAfterChangingView;
3558efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek
3568efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek                    int nextIndex = finalState
3578efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek                            .getViewStateForView(viewAfterChangingView).notGoneIndex;
3588efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek                    if (ownIndex >= nextIndex) {
3598efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek                        // we only have the view afterwards
3608efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek                        ownIndex++;
3618efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek                    }
3628efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek                    int difference = Math.abs(ownIndex - nextIndex);
3638efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek                    difference = Math.max(0, Math.min(DELAY_EFFECT_MAX_INDEX_DIFFERENCE,
3648efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek                            difference - 1));
3658efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek                    long delay = difference * delayPerElement;
3668efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek                    minDelay = Math.max(delay, minDelay);
3678efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek                    break;
3688efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek                }
3698efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek                default:
3708efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek                    break;
3718efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek            }
3728efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek        }
3738efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek        return minDelay;
374572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek    }
375572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek
376b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek    private long calculateDelayDark(StackViewState viewState) {
3772a5e452f4f70d2dd717946dd50486cc66ea4cc43Jorim Jaggi        int referenceIndex;
3782a5e452f4f70d2dd717946dd50486cc66ea4cc43Jorim Jaggi        if (mAnimationFilter.darkAnimationOriginIndex ==
3792a5e452f4f70d2dd717946dd50486cc66ea4cc43Jorim Jaggi                NotificationStackScrollLayout.AnimationEvent.DARK_ANIMATION_ORIGIN_INDEX_ABOVE) {
3802a5e452f4f70d2dd717946dd50486cc66ea4cc43Jorim Jaggi            referenceIndex = 0;
3812a5e452f4f70d2dd717946dd50486cc66ea4cc43Jorim Jaggi        } else if (mAnimationFilter.darkAnimationOriginIndex ==
3822a5e452f4f70d2dd717946dd50486cc66ea4cc43Jorim Jaggi                NotificationStackScrollLayout.AnimationEvent.DARK_ANIMATION_ORIGIN_INDEX_BELOW) {
3832a5e452f4f70d2dd717946dd50486cc66ea4cc43Jorim Jaggi            referenceIndex = mHostLayout.getNotGoneChildCount() - 1;
3842a5e452f4f70d2dd717946dd50486cc66ea4cc43Jorim Jaggi        } else {
3852a5e452f4f70d2dd717946dd50486cc66ea4cc43Jorim Jaggi            referenceIndex = mAnimationFilter.darkAnimationOriginIndex;
3862a5e452f4f70d2dd717946dd50486cc66ea4cc43Jorim Jaggi        }
3872a5e452f4f70d2dd717946dd50486cc66ea4cc43Jorim Jaggi        return Math.abs(referenceIndex - viewState.notGoneIndex) * ANIMATION_DELAY_PER_ELEMENT_DARK;
3884e857f4ef0357e05806819d0488a73a12208fe8fJorim Jaggi    }
3894e857f4ef0357e05806819d0488a73a12208fe8fJorim Jaggi
390b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek    private long calculateDelayGoToFullShade(StackViewState viewState) {
39160d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi        float index = viewState.notGoneIndex;
39260d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi        index = (float) Math.pow(index, 0.7f);
39360d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi        return (long) (index * ANIMATION_DELAY_PER_ELEMENT_GO_TO_FULL_SHADE);
39460d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi    }
39560d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi
396277a8aa409def1a35eb662c974b91c0f133926aaSelim Cinek    private void startShadowAlphaAnimation(final ExpandableView child,
397277a8aa409def1a35eb662c974b91c0f133926aaSelim Cinek            StackViewState viewState, long duration, long delay) {
398277a8aa409def1a35eb662c974b91c0f133926aaSelim Cinek        Float previousStartValue = getChildTag(child, TAG_START_SHADOW_ALPHA);
399277a8aa409def1a35eb662c974b91c0f133926aaSelim Cinek        Float previousEndValue = getChildTag(child, TAG_END_SHADOW_ALPHA);
400277a8aa409def1a35eb662c974b91c0f133926aaSelim Cinek        float newEndValue = viewState.shadowAlpha;
401277a8aa409def1a35eb662c974b91c0f133926aaSelim Cinek        if (previousEndValue != null && previousEndValue == newEndValue) {
402277a8aa409def1a35eb662c974b91c0f133926aaSelim Cinek            return;
403277a8aa409def1a35eb662c974b91c0f133926aaSelim Cinek        }
404277a8aa409def1a35eb662c974b91c0f133926aaSelim Cinek        ValueAnimator previousAnimator = getChildTag(child, TAG_ANIMATOR_SHADOW_ALPHA);
405277a8aa409def1a35eb662c974b91c0f133926aaSelim Cinek        if (!mAnimationFilter.animateShadowAlpha) {
406277a8aa409def1a35eb662c974b91c0f133926aaSelim Cinek            // just a local update was performed
407277a8aa409def1a35eb662c974b91c0f133926aaSelim Cinek            if (previousAnimator != null) {
408277a8aa409def1a35eb662c974b91c0f133926aaSelim Cinek                // we need to increase all animation keyframes of the previous animator by the
409277a8aa409def1a35eb662c974b91c0f133926aaSelim Cinek                // relative change to the end value
410277a8aa409def1a35eb662c974b91c0f133926aaSelim Cinek                PropertyValuesHolder[] values = previousAnimator.getValues();
411277a8aa409def1a35eb662c974b91c0f133926aaSelim Cinek                float relativeDiff = newEndValue - previousEndValue;
412277a8aa409def1a35eb662c974b91c0f133926aaSelim Cinek                float newStartValue = previousStartValue + relativeDiff;
413277a8aa409def1a35eb662c974b91c0f133926aaSelim Cinek                values[0].setFloatValues(newStartValue, newEndValue);
414277a8aa409def1a35eb662c974b91c0f133926aaSelim Cinek                child.setTag(TAG_START_SHADOW_ALPHA, newStartValue);
415277a8aa409def1a35eb662c974b91c0f133926aaSelim Cinek                child.setTag(TAG_END_SHADOW_ALPHA, newEndValue);
416277a8aa409def1a35eb662c974b91c0f133926aaSelim Cinek                previousAnimator.setCurrentPlayTime(previousAnimator.getCurrentPlayTime());
417277a8aa409def1a35eb662c974b91c0f133926aaSelim Cinek                return;
418277a8aa409def1a35eb662c974b91c0f133926aaSelim Cinek            } else {
419277a8aa409def1a35eb662c974b91c0f133926aaSelim Cinek                // no new animation needed, let's just apply the value
420277a8aa409def1a35eb662c974b91c0f133926aaSelim Cinek                child.setShadowAlpha(newEndValue);
421277a8aa409def1a35eb662c974b91c0f133926aaSelim Cinek                return;
422277a8aa409def1a35eb662c974b91c0f133926aaSelim Cinek            }
423277a8aa409def1a35eb662c974b91c0f133926aaSelim Cinek        }
424277a8aa409def1a35eb662c974b91c0f133926aaSelim Cinek
425277a8aa409def1a35eb662c974b91c0f133926aaSelim Cinek        ValueAnimator animator = ValueAnimator.ofFloat(child.getShadowAlpha(), newEndValue);
426277a8aa409def1a35eb662c974b91c0f133926aaSelim Cinek        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
427277a8aa409def1a35eb662c974b91c0f133926aaSelim Cinek            @Override
428277a8aa409def1a35eb662c974b91c0f133926aaSelim Cinek            public void onAnimationUpdate(ValueAnimator animation) {
429277a8aa409def1a35eb662c974b91c0f133926aaSelim Cinek                child.setShadowAlpha((float) animation.getAnimatedValue());
430277a8aa409def1a35eb662c974b91c0f133926aaSelim Cinek            }
431277a8aa409def1a35eb662c974b91c0f133926aaSelim Cinek        });
432277a8aa409def1a35eb662c974b91c0f133926aaSelim Cinek        animator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
433277a8aa409def1a35eb662c974b91c0f133926aaSelim Cinek        long newDuration = cancelAnimatorAndGetNewDuration(duration, previousAnimator);
434277a8aa409def1a35eb662c974b91c0f133926aaSelim Cinek        animator.setDuration(newDuration);
435843f54173743abe8cd4aa83b87d38e6b8b4e0ab3Selim Cinek        if (delay > 0 && (previousAnimator == null
436843f54173743abe8cd4aa83b87d38e6b8b4e0ab3Selim Cinek                || previousAnimator.getAnimatedFraction() == 0)) {
437277a8aa409def1a35eb662c974b91c0f133926aaSelim Cinek            animator.setStartDelay(delay);
438277a8aa409def1a35eb662c974b91c0f133926aaSelim Cinek        }
439277a8aa409def1a35eb662c974b91c0f133926aaSelim Cinek        animator.addListener(getGlobalAnimationFinishedListener());
440277a8aa409def1a35eb662c974b91c0f133926aaSelim Cinek        // remove the tag when the animation is finished
441277a8aa409def1a35eb662c974b91c0f133926aaSelim Cinek        animator.addListener(new AnimatorListenerAdapter() {
442277a8aa409def1a35eb662c974b91c0f133926aaSelim Cinek            @Override
443277a8aa409def1a35eb662c974b91c0f133926aaSelim Cinek            public void onAnimationEnd(Animator animation) {
444277a8aa409def1a35eb662c974b91c0f133926aaSelim Cinek                child.setTag(TAG_ANIMATOR_SHADOW_ALPHA, null);
445277a8aa409def1a35eb662c974b91c0f133926aaSelim Cinek                child.setTag(TAG_START_SHADOW_ALPHA, null);
446277a8aa409def1a35eb662c974b91c0f133926aaSelim Cinek                child.setTag(TAG_END_SHADOW_ALPHA, null);
447277a8aa409def1a35eb662c974b91c0f133926aaSelim Cinek            }
448277a8aa409def1a35eb662c974b91c0f133926aaSelim Cinek        });
449277a8aa409def1a35eb662c974b91c0f133926aaSelim Cinek        startAnimator(animator);
450277a8aa409def1a35eb662c974b91c0f133926aaSelim Cinek        child.setTag(TAG_ANIMATOR_SHADOW_ALPHA, animator);
451277a8aa409def1a35eb662c974b91c0f133926aaSelim Cinek        child.setTag(TAG_START_SHADOW_ALPHA, child.getShadowAlpha());
452277a8aa409def1a35eb662c974b91c0f133926aaSelim Cinek        child.setTag(TAG_END_SHADOW_ALPHA, newEndValue);
453277a8aa409def1a35eb662c974b91c0f133926aaSelim Cinek    }
454277a8aa409def1a35eb662c974b91c0f133926aaSelim Cinek
455eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek    private void startHeightAnimation(final ExpandableView child,
456b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek            StackViewState viewState, long duration, long delay) {
4578df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek        Integer previousStartValue = getChildTag(child, TAG_START_HEIGHT);
458d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi        Integer previousEndValue = getChildTag(child, TAG_END_HEIGHT);
4598df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek        int newEndValue = viewState.height;
4608df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek        if (previousEndValue != null && previousEndValue == newEndValue) {
461eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek            return;
462eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        }
463eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        ValueAnimator previousAnimator = getChildTag(child, TAG_ANIMATOR_HEIGHT);
4648df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek        if (!mAnimationFilter.animateHeight) {
4658df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek            // just a local update was performed
4668df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek            if (previousAnimator != null) {
4678df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek                // we need to increase all animation keyframes of the previous animator by the
4688df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek                // relative change to the end value
4698df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek                PropertyValuesHolder[] values = previousAnimator.getValues();
4708df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek                int relativeDiff = newEndValue - previousEndValue;
4718df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek                int newStartValue = previousStartValue + relativeDiff;
4728df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek                values[0].setIntValues(newStartValue, newEndValue);
4738df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek                child.setTag(TAG_START_HEIGHT, newStartValue);
4748df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek                child.setTag(TAG_END_HEIGHT, newEndValue);
4758df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek                previousAnimator.setCurrentPlayTime(previousAnimator.getCurrentPlayTime());
4768df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek                return;
4778df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek            } else {
4788df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek                // no new animation needed, let's just apply the value
4798df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek                child.setActualHeight(newEndValue, false);
4808df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek                return;
481eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek            }
482eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        }
483eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek
4848df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek        ValueAnimator animator = ValueAnimator.ofInt(child.getActualHeight(), newEndValue);
485eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
486572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek            @Override
487572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek            public void onAnimationUpdate(ValueAnimator animation) {
488d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi                child.setActualHeight((int) animation.getAnimatedValue(),
489d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi                        false /* notifyListeners */);
490572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek            }
491572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek        });
492c18010f6720f606003cde3cd376ddacaca30f6e5Selim Cinek        animator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
49360d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi        long newDuration = cancelAnimatorAndGetNewDuration(duration, previousAnimator);
494eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        animator.setDuration(newDuration);
495843f54173743abe8cd4aa83b87d38e6b8b4e0ab3Selim Cinek        if (delay > 0 && (previousAnimator == null
496843f54173743abe8cd4aa83b87d38e6b8b4e0ab3Selim Cinek                || previousAnimator.getAnimatedFraction() == 0)) {
4978efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek            animator.setStartDelay(delay);
4988efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek        }
499eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        animator.addListener(getGlobalAnimationFinishedListener());
500eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        // remove the tag when the animation is finished
501eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        animator.addListener(new AnimatorListenerAdapter() {
502b0a824687f56b6950338aad169d8d837f8ed657bMady Mellor            boolean mWasCancelled;
503b0a824687f56b6950338aad169d8d837f8ed657bMady Mellor
504eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek            @Override
505eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek            public void onAnimationEnd(Animator animation) {
506eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek                child.setTag(TAG_ANIMATOR_HEIGHT, null);
5078df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek                child.setTag(TAG_START_HEIGHT, null);
508eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek                child.setTag(TAG_END_HEIGHT, null);
509599be34e6e3274e97512f3970a0d6edddedb8eb4Adrian Roos                child.setActualHeightAnimating(false);
510b0a824687f56b6950338aad169d8d837f8ed657bMady Mellor                if (!mWasCancelled && child instanceof ExpandableNotificationRow) {
511b0a824687f56b6950338aad169d8d837f8ed657bMady Mellor                    ((ExpandableNotificationRow) child).setGroupExpansionChanging(
512b0a824687f56b6950338aad169d8d837f8ed657bMady Mellor                            false /* isExpansionChanging */);
513b0a824687f56b6950338aad169d8d837f8ed657bMady Mellor                }
514b0a824687f56b6950338aad169d8d837f8ed657bMady Mellor            }
515b0a824687f56b6950338aad169d8d837f8ed657bMady Mellor
516b0a824687f56b6950338aad169d8d837f8ed657bMady Mellor            @Override
517b0a824687f56b6950338aad169d8d837f8ed657bMady Mellor            public void onAnimationStart(Animator animation) {
518b0a824687f56b6950338aad169d8d837f8ed657bMady Mellor                mWasCancelled = false;
519b0a824687f56b6950338aad169d8d837f8ed657bMady Mellor            }
520b0a824687f56b6950338aad169d8d837f8ed657bMady Mellor
521b0a824687f56b6950338aad169d8d837f8ed657bMady Mellor            @Override
522b0a824687f56b6950338aad169d8d837f8ed657bMady Mellor            public void onAnimationCancel(Animator animation) {
523b0a824687f56b6950338aad169d8d837f8ed657bMady Mellor                mWasCancelled = true;
524eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek            }
525eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        });
5268efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek        startAnimator(animator);
527eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        child.setTag(TAG_ANIMATOR_HEIGHT, animator);
5288df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek        child.setTag(TAG_START_HEIGHT, child.getActualHeight());
5298df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek        child.setTag(TAG_END_HEIGHT, newEndValue);
530599be34e6e3274e97512f3970a0d6edddedb8eb4Adrian Roos        child.setActualHeightAnimating(true);
531eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek    }
532eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek
533708a6c120da6750d281195ef15a240a5627efed4Selim Cinek    private void startInsetAnimation(final ExpandableView child,
534b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek            StackViewState viewState, long duration, long delay) {
535708a6c120da6750d281195ef15a240a5627efed4Selim Cinek        Integer previousStartValue = getChildTag(child, TAG_START_TOP_INSET);
536708a6c120da6750d281195ef15a240a5627efed4Selim Cinek        Integer previousEndValue = getChildTag(child, TAG_END_TOP_INSET);
537708a6c120da6750d281195ef15a240a5627efed4Selim Cinek        int newEndValue = viewState.clipTopAmount;
538708a6c120da6750d281195ef15a240a5627efed4Selim Cinek        if (previousEndValue != null && previousEndValue == newEndValue) {
539708a6c120da6750d281195ef15a240a5627efed4Selim Cinek            return;
540708a6c120da6750d281195ef15a240a5627efed4Selim Cinek        }
541708a6c120da6750d281195ef15a240a5627efed4Selim Cinek        ValueAnimator previousAnimator = getChildTag(child, TAG_ANIMATOR_TOP_INSET);
542708a6c120da6750d281195ef15a240a5627efed4Selim Cinek        if (!mAnimationFilter.animateTopInset) {
543708a6c120da6750d281195ef15a240a5627efed4Selim Cinek            // just a local update was performed
544708a6c120da6750d281195ef15a240a5627efed4Selim Cinek            if (previousAnimator != null) {
545708a6c120da6750d281195ef15a240a5627efed4Selim Cinek                // we need to increase all animation keyframes of the previous animator by the
546708a6c120da6750d281195ef15a240a5627efed4Selim Cinek                // relative change to the end value
547708a6c120da6750d281195ef15a240a5627efed4Selim Cinek                PropertyValuesHolder[] values = previousAnimator.getValues();
548708a6c120da6750d281195ef15a240a5627efed4Selim Cinek                int relativeDiff = newEndValue - previousEndValue;
549708a6c120da6750d281195ef15a240a5627efed4Selim Cinek                int newStartValue = previousStartValue + relativeDiff;
550708a6c120da6750d281195ef15a240a5627efed4Selim Cinek                values[0].setIntValues(newStartValue, newEndValue);
551708a6c120da6750d281195ef15a240a5627efed4Selim Cinek                child.setTag(TAG_START_TOP_INSET, newStartValue);
552708a6c120da6750d281195ef15a240a5627efed4Selim Cinek                child.setTag(TAG_END_TOP_INSET, newEndValue);
553708a6c120da6750d281195ef15a240a5627efed4Selim Cinek                previousAnimator.setCurrentPlayTime(previousAnimator.getCurrentPlayTime());
554708a6c120da6750d281195ef15a240a5627efed4Selim Cinek                return;
555708a6c120da6750d281195ef15a240a5627efed4Selim Cinek            } else {
556708a6c120da6750d281195ef15a240a5627efed4Selim Cinek                // no new animation needed, let's just apply the value
557708a6c120da6750d281195ef15a240a5627efed4Selim Cinek                child.setClipTopAmount(newEndValue);
558708a6c120da6750d281195ef15a240a5627efed4Selim Cinek                return;
559708a6c120da6750d281195ef15a240a5627efed4Selim Cinek            }
560708a6c120da6750d281195ef15a240a5627efed4Selim Cinek        }
561708a6c120da6750d281195ef15a240a5627efed4Selim Cinek
562708a6c120da6750d281195ef15a240a5627efed4Selim Cinek        ValueAnimator animator = ValueAnimator.ofInt(child.getClipTopAmount(), newEndValue);
563708a6c120da6750d281195ef15a240a5627efed4Selim Cinek        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
564708a6c120da6750d281195ef15a240a5627efed4Selim Cinek            @Override
565708a6c120da6750d281195ef15a240a5627efed4Selim Cinek            public void onAnimationUpdate(ValueAnimator animation) {
566708a6c120da6750d281195ef15a240a5627efed4Selim Cinek                child.setClipTopAmount((int) animation.getAnimatedValue());
567708a6c120da6750d281195ef15a240a5627efed4Selim Cinek            }
568708a6c120da6750d281195ef15a240a5627efed4Selim Cinek        });
569c18010f6720f606003cde3cd376ddacaca30f6e5Selim Cinek        animator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
57060d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi        long newDuration = cancelAnimatorAndGetNewDuration(duration, previousAnimator);
571708a6c120da6750d281195ef15a240a5627efed4Selim Cinek        animator.setDuration(newDuration);
572843f54173743abe8cd4aa83b87d38e6b8b4e0ab3Selim Cinek        if (delay > 0 && (previousAnimator == null
573843f54173743abe8cd4aa83b87d38e6b8b4e0ab3Selim Cinek                || previousAnimator.getAnimatedFraction() == 0)) {
574708a6c120da6750d281195ef15a240a5627efed4Selim Cinek            animator.setStartDelay(delay);
575708a6c120da6750d281195ef15a240a5627efed4Selim Cinek        }
576708a6c120da6750d281195ef15a240a5627efed4Selim Cinek        animator.addListener(getGlobalAnimationFinishedListener());
577708a6c120da6750d281195ef15a240a5627efed4Selim Cinek        // remove the tag when the animation is finished
578708a6c120da6750d281195ef15a240a5627efed4Selim Cinek        animator.addListener(new AnimatorListenerAdapter() {
579708a6c120da6750d281195ef15a240a5627efed4Selim Cinek            @Override
580708a6c120da6750d281195ef15a240a5627efed4Selim Cinek            public void onAnimationEnd(Animator animation) {
581708a6c120da6750d281195ef15a240a5627efed4Selim Cinek                child.setTag(TAG_ANIMATOR_TOP_INSET, null);
582708a6c120da6750d281195ef15a240a5627efed4Selim Cinek                child.setTag(TAG_START_TOP_INSET, null);
583708a6c120da6750d281195ef15a240a5627efed4Selim Cinek                child.setTag(TAG_END_TOP_INSET, null);
584708a6c120da6750d281195ef15a240a5627efed4Selim Cinek            }
585708a6c120da6750d281195ef15a240a5627efed4Selim Cinek        });
586708a6c120da6750d281195ef15a240a5627efed4Selim Cinek        startAnimator(animator);
587708a6c120da6750d281195ef15a240a5627efed4Selim Cinek        child.setTag(TAG_ANIMATOR_TOP_INSET, animator);
588708a6c120da6750d281195ef15a240a5627efed4Selim Cinek        child.setTag(TAG_START_TOP_INSET, child.getClipTopAmount());
589708a6c120da6750d281195ef15a240a5627efed4Selim Cinek        child.setTag(TAG_END_TOP_INSET, newEndValue);
590708a6c120da6750d281195ef15a240a5627efed4Selim Cinek    }
591708a6c120da6750d281195ef15a240a5627efed4Selim Cinek
592b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek    private void startAlphaAnimation(final View child,
593b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek            final ViewState viewState, long duration, long delay) {
5948df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek        Float previousStartValue = getChildTag(child,TAG_START_ALPHA);
595eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        Float previousEndValue = getChildTag(child,TAG_END_ALPHA);
5968df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek        final float newEndValue = viewState.alpha;
5978df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek        if (previousEndValue != null && previousEndValue == newEndValue) {
598eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek            return;
599eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        }
600eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        ObjectAnimator previousAnimator = getChildTag(child, TAG_ANIMATOR_ALPHA);
6018df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek        if (!mAnimationFilter.animateAlpha) {
6028df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek            // just a local update was performed
6038df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek            if (previousAnimator != null) {
6048df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek                // we need to increase all animation keyframes of the previous animator by the
6058df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek                // relative change to the end value
6068df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek                PropertyValuesHolder[] values = previousAnimator.getValues();
6078df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek                float relativeDiff = newEndValue - previousEndValue;
6088df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek                float newStartValue = previousStartValue + relativeDiff;
6098df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek                values[0].setFloatValues(newStartValue, newEndValue);
6108df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek                child.setTag(TAG_START_ALPHA, newStartValue);
6118df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek                child.setTag(TAG_END_ALPHA, newEndValue);
6128df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek                previousAnimator.setCurrentPlayTime(previousAnimator.getCurrentPlayTime());
6138df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek                return;
6148df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek            } else {
6158df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek                // no new animation needed, let's just apply the value
6168df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek                child.setAlpha(newEndValue);
6178df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek                if (newEndValue == 0) {
6188df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek                    child.setVisibility(View.INVISIBLE);
6198df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek                }
620eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek            }
621eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        }
622eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek
623eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        ObjectAnimator animator = ObjectAnimator.ofFloat(child, View.ALPHA,
6248df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek                child.getAlpha(), newEndValue);
625c18010f6720f606003cde3cd376ddacaca30f6e5Selim Cinek        animator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
626eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        // Handle layer type
627eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        child.setLayerType(View.LAYER_TYPE_HARDWARE, null);
628eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        animator.addListener(new AnimatorListenerAdapter() {
629eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek            public boolean mWasCancelled;
630eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek
631eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek            @Override
632eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek            public void onAnimationEnd(Animator animation) {
6331efb240c1a0aeca9492cf8891794712adfdb1fa7Selim Cinek                child.setLayerType(View.LAYER_TYPE_NONE, null);
6348df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek                if (newEndValue == 0 && !mWasCancelled) {
635eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek                    child.setVisibility(View.INVISIBLE);
636eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek                }
637c5baa3eb0893cb764e7810f8c68e89b04653df86Selim Cinek                // remove the tag when the animation is finished
638eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek                child.setTag(TAG_ANIMATOR_ALPHA, null);
6398df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek                child.setTag(TAG_START_ALPHA, null);
640eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek                child.setTag(TAG_END_ALPHA, null);
641eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek            }
642eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek
643eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek            @Override
644eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek            public void onAnimationCancel(Animator animation) {
645eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek                mWasCancelled = true;
646eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek            }
647eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek
648eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek            @Override
649eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek            public void onAnimationStart(Animator animation) {
650eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek                mWasCancelled = false;
651eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek            }
652eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        });
65360d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi        long newDuration = cancelAnimatorAndGetNewDuration(duration, previousAnimator);
6543af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek        animator.setDuration(newDuration);
655843f54173743abe8cd4aa83b87d38e6b8b4e0ab3Selim Cinek        if (delay > 0 && (previousAnimator == null
656843f54173743abe8cd4aa83b87d38e6b8b4e0ab3Selim Cinek                || previousAnimator.getAnimatedFraction() == 0)) {
6578efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek            animator.setStartDelay(delay);
6588efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek        }
659eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        animator.addListener(getGlobalAnimationFinishedListener());
660eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek
6618efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek        startAnimator(animator);
662eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        child.setTag(TAG_ANIMATOR_ALPHA, animator);
6638df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek        child.setTag(TAG_START_ALPHA, child.getAlpha());
6648df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek        child.setTag(TAG_END_ALPHA, newEndValue);
665572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek    }
666572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek
667b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek    private void startZTranslationAnimation(final View child,
668b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek            final ViewState viewState, long duration, long delay) {
6698df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek        Float previousStartValue = getChildTag(child,TAG_START_TRANSLATION_Z);
670eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        Float previousEndValue = getChildTag(child,TAG_END_TRANSLATION_Z);
6718df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek        float newEndValue = viewState.zTranslation;
6728df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek        if (previousEndValue != null && previousEndValue == newEndValue) {
673eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek            return;
674eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        }
675eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        ObjectAnimator previousAnimator = getChildTag(child, TAG_ANIMATOR_TRANSLATION_Z);
6768df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek        if (!mAnimationFilter.animateZ) {
6778df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek            // just a local update was performed
6788df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek            if (previousAnimator != null) {
6798df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek                // we need to increase all animation keyframes of the previous animator by the
6808df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek                // relative change to the end value
6818df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek                PropertyValuesHolder[] values = previousAnimator.getValues();
6828df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek                float relativeDiff = newEndValue - previousEndValue;
6838df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek                float newStartValue = previousStartValue + relativeDiff;
6848df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek                values[0].setFloatValues(newStartValue, newEndValue);
6858df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek                child.setTag(TAG_START_TRANSLATION_Z, newStartValue);
6868df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek                child.setTag(TAG_END_TRANSLATION_Z, newEndValue);
6878df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek                previousAnimator.setCurrentPlayTime(previousAnimator.getCurrentPlayTime());
6888df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek                return;
6898df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek            } else {
6908df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek                // no new animation needed, let's just apply the value
6918df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek                child.setTranslationZ(newEndValue);
692eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek            }
693eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        }
694eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek
695eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        ObjectAnimator animator = ObjectAnimator.ofFloat(child, View.TRANSLATION_Z,
6968df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek                child.getTranslationZ(), newEndValue);
697c18010f6720f606003cde3cd376ddacaca30f6e5Selim Cinek        animator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
69860d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi        long newDuration = cancelAnimatorAndGetNewDuration(duration, previousAnimator);
6993af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek        animator.setDuration(newDuration);
700843f54173743abe8cd4aa83b87d38e6b8b4e0ab3Selim Cinek        if (delay > 0 && (previousAnimator == null
701843f54173743abe8cd4aa83b87d38e6b8b4e0ab3Selim Cinek                || previousAnimator.getAnimatedFraction() == 0)) {
7028efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek            animator.setStartDelay(delay);
7038efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek        }
704eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        animator.addListener(getGlobalAnimationFinishedListener());
705eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        // remove the tag when the animation is finished
706eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        animator.addListener(new AnimatorListenerAdapter() {
707eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek            @Override
708eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek            public void onAnimationEnd(Animator animation) {
709eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek                child.setTag(TAG_ANIMATOR_TRANSLATION_Z, null);
7108df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek                child.setTag(TAG_START_TRANSLATION_Z, null);
711eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek                child.setTag(TAG_END_TRANSLATION_Z, null);
712eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek            }
713eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        });
7148efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek        startAnimator(animator);
715eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        child.setTag(TAG_ANIMATOR_TRANSLATION_Z, animator);
7168df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek        child.setTag(TAG_START_TRANSLATION_Z, child.getTranslationZ());
7178df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek        child.setTag(TAG_END_TRANSLATION_Z, newEndValue);
718eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek    }
719eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek
720b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek    private void startYTranslationAnimation(final View child,
721b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek            ViewState viewState, long duration, long delay) {
7228df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek        Float previousStartValue = getChildTag(child,TAG_START_TRANSLATION_Y);
723eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        Float previousEndValue = getChildTag(child,TAG_END_TRANSLATION_Y);
7248df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek        float newEndValue = viewState.yTranslation;
7258df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek        if (previousEndValue != null && previousEndValue == newEndValue) {
726eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek            return;
727eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        }
728eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        ObjectAnimator previousAnimator = getChildTag(child, TAG_ANIMATOR_TRANSLATION_Y);
7298df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek        if (!mAnimationFilter.animateY) {
7308df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek            // just a local update was performed
7318df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek            if (previousAnimator != null) {
7328df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek                // we need to increase all animation keyframes of the previous animator by the
7338df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek                // relative change to the end value
7348df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek                PropertyValuesHolder[] values = previousAnimator.getValues();
7358df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek                float relativeDiff = newEndValue - previousEndValue;
7368df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek                float newStartValue = previousStartValue + relativeDiff;
7378df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek                values[0].setFloatValues(newStartValue, newEndValue);
7388df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek                child.setTag(TAG_START_TRANSLATION_Y, newStartValue);
7398df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek                child.setTag(TAG_END_TRANSLATION_Y, newEndValue);
7408df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek                previousAnimator.setCurrentPlayTime(previousAnimator.getCurrentPlayTime());
7418df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek                return;
7428df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek            } else {
7438df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek                // no new animation needed, let's just apply the value
7448df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek                child.setTranslationY(newEndValue);
7458df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek                return;
746eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek            }
747eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        }
748eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek
749eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        ObjectAnimator animator = ObjectAnimator.ofFloat(child, View.TRANSLATION_Y,
7508df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek                child.getTranslationY(), newEndValue);
751a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek        Interpolator interpolator = mHeadsUpAppearChildren.contains(child) ?
752c18010f6720f606003cde3cd376ddacaca30f6e5Selim Cinek                mHeadsUpAppearInterpolator :Interpolators.FAST_OUT_SLOW_IN;
753a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek        animator.setInterpolator(interpolator);
75460d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi        long newDuration = cancelAnimatorAndGetNewDuration(duration, previousAnimator);
7553af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek        animator.setDuration(newDuration);
756843f54173743abe8cd4aa83b87d38e6b8b4e0ab3Selim Cinek        if (delay > 0 && (previousAnimator == null
757843f54173743abe8cd4aa83b87d38e6b8b4e0ab3Selim Cinek                || previousAnimator.getAnimatedFraction() == 0)) {
7588efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek            animator.setStartDelay(delay);
7598efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek        }
760eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        animator.addListener(getGlobalAnimationFinishedListener());
76173cf02a0783e3857cae21fece9c292abcc9e1409Selim Cinek        final boolean isHeadsUpDisappear = mHeadsUpDisappearChildren.contains(child);
762eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        // remove the tag when the animation is finished
763eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        animator.addListener(new AnimatorListenerAdapter() {
764eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek            @Override
765eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek            public void onAnimationEnd(Animator animation) {
7660fccc729fd3b19a62efd90ae13b207faa3d1e8bbSelim Cinek                HeadsUpManager.setIsClickedNotification(child, false);
767eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek                child.setTag(TAG_ANIMATOR_TRANSLATION_Y, null);
7688df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek                child.setTag(TAG_START_TRANSLATION_Y, null);
769eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek                child.setTag(TAG_END_TRANSLATION_Y, null);
77073cf02a0783e3857cae21fece9c292abcc9e1409Selim Cinek                if (isHeadsUpDisappear) {
77173cf02a0783e3857cae21fece9c292abcc9e1409Selim Cinek                    ((ExpandableNotificationRow) child).setHeadsupDisappearRunning(false);
77273cf02a0783e3857cae21fece9c292abcc9e1409Selim Cinek                }
773eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek            }
774eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        });
7758efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek        startAnimator(animator);
776eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        child.setTag(TAG_ANIMATOR_TRANSLATION_Y, animator);
7778df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek        child.setTag(TAG_START_TRANSLATION_Y, child.getTranslationY());
7788df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek        child.setTag(TAG_END_TRANSLATION_Y, newEndValue);
779eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek    }
780eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek
7818efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek    private void startAnimator(ValueAnimator animator) {
7828efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek        mAnimatorSet.add(animator);
7833af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek        animator.start();
7843af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek    }
7853af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek
7863af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek    /**
7873af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek     * @return an adapter which ensures that onAnimationFinished is called once no animation is
7883af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek     *         running anymore
7893af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek     */
7903af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek    private AnimatorListenerAdapter getGlobalAnimationFinishedListener() {
7913af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek        if (!mAnimationListenerPool.empty()) {
7923af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek            return mAnimationListenerPool.pop();
7933af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek        }
7943af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek
7953af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek        // We need to create a new one, no reusable ones found
7963af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek        return new AnimatorListenerAdapter() {
7973af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek            private boolean mWasCancelled;
7983af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek
7993af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek            @Override
8003af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek            public void onAnimationEnd(Animator animation) {
8013af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek                mAnimatorSet.remove(animation);
8023af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek                if (mAnimatorSet.isEmpty() && !mWasCancelled) {
8033af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek                    onAnimationFinished();
8043af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek                }
8053af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek                mAnimationListenerPool.push(this);
8063af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek            }
8073af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek
8083af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek            @Override
8093af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek            public void onAnimationCancel(Animator animation) {
8103af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek                mWasCancelled = true;
8113af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek            }
8123af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek
8133af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek            @Override
8143af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek            public void onAnimationStart(Animator animation) {
8153af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek                mWasCancelled = false;
8163af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek            }
8173af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek        };
8183af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek    }
8193af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek
820aac932591d7aa05bae61d2b47ed7647f35da0001Selim Cinek    public static <T> T getChildTag(View child, int tag) {
821eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        return (T) child.getTag(tag);
822eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek    }
823eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek
824eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek    /**
825eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek     * Cancel the previous animator and get the duration of the new animation.
82639610545f0c2714a3526bc935effe57b421542d1Selim Cinek     *
82760d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi     * @param duration the new duration
828eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek     * @param previousAnimator the animator which was running before
829eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek     * @return the new duration
830eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek     */
83160d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi    private long cancelAnimatorAndGetNewDuration(long duration, ValueAnimator previousAnimator) {
83260d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi        long newDuration = duration;
833eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        if (previousAnimator != null) {
8348df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek            // We take either the desired length of the new animation or the remaining time of
8358df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek            // the previous animator, whichever is longer.
8368df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek            newDuration = Math.max(previousAnimator.getDuration()
8378df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek                    - previousAnimator.getCurrentPlayTime(), newDuration);
8383af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek            previousAnimator.cancel();
839eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        }
8403af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek        return newDuration;
841eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek    }
842eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek
843eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek    private void onAnimationFinished() {
844eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        mHostLayout.onChildAnimationFinished();
8458f93763cf0f4b3c41f6b0ecac09b621eb0ca328bSelim Cinek        for (View v : mChildrenToClearFromOverlay) {
846a5703187a9dba418f742fab0874a8f683b0bbb0eSelim Cinek            removeFromOverlay(v);
8478f93763cf0f4b3c41f6b0ecac09b621eb0ca328bSelim Cinek        }
8488f93763cf0f4b3c41f6b0ecac09b621eb0ca328bSelim Cinek        mChildrenToClearFromOverlay.clear();
849eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek    }
850eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek
851eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek    /**
852eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek     * Process the animationEvents for a new animation
853eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek     *
854eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek     * @param animationEvents the animation events for the animation to perform
85539610545f0c2714a3526bc935effe57b421542d1Selim Cinek     * @param finalState the final state to animate to
85639610545f0c2714a3526bc935effe57b421542d1Selim Cinek     */
857eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek    private void processAnimationEvents(
8580dd6881ea481c855976214807c17595b34a2920aJorim Jaggi            ArrayList<NotificationStackScrollLayout.AnimationEvent> animationEvents,
859572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek            StackScrollState finalState) {
8608d9ff9c2c66bc1d3b92eb6992d58599ff80ed6dcSelim Cinek        for (NotificationStackScrollLayout.AnimationEvent event : animationEvents) {
8618efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek            final ExpandableView changingView = (ExpandableView) event.changingView;
8628efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek            if (event.animationType ==
8638efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek                    NotificationStackScrollLayout.AnimationEvent.ANIMATION_TYPE_ADD) {
8648efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek
8658efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek                // This item is added, initialize it's properties.
866b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek                StackViewState viewState = finalState
8678efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek                        .getViewStateForView(changingView);
8688efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek                if (viewState == null) {
8698efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek                    // The position for this child was never generated, let's continue.
8708efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek                    continue;
8718efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek                }
872b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek                finalState.applyState(changingView, viewState);
8738efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek                mNewAddChildren.add(changingView);
8748efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek
8758efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek            } else if (event.animationType ==
8768efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek                    NotificationStackScrollLayout.AnimationEvent.ANIMATION_TYPE_REMOVE) {
8778efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek                if (changingView.getVisibility() == View.GONE) {
878a5703187a9dba418f742fab0874a8f683b0bbb0eSelim Cinek                    removeFromOverlay(changingView);
8798efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek                    continue;
8808efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek                }
8818efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek
8828efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek                // Find the amount to translate up. This is needed in order to understand the
8838efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek                // direction of the remove animation (either downwards or upwards)
884b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek                StackViewState viewState = finalState
8858efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek                        .getViewStateForView(event.viewAfterChangingView);
8868efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek                int actualHeight = changingView.getActualHeight();
8878efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek                // upwards by default
8888efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek                float translationDirection = -1.0f;
8898efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek                if (viewState != null) {
8908efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek                    // there was a view after this one, Approximate the distance the next child
8918efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek                    // travelled
8928efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek                    translationDirection = ((viewState.yTranslation
8938efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek                            - (changingView.getTranslationY() + actualHeight / 2.0f)) * 2 /
8948efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek                            actualHeight);
8958efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek                    translationDirection = Math.max(Math.min(translationDirection, 1.0f),-1.0f);
8968efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek
8978efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek                }
89860d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi                changingView.performRemoveAnimation(ANIMATION_DURATION_APPEAR_DISAPPEAR,
89960d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi                        translationDirection, new Runnable() {
9008efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek                    @Override
9018efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek                    public void run() {
9028efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek                        // remove the temporary overlay
903a5703187a9dba418f742fab0874a8f683b0bbb0eSelim Cinek                        removeFromOverlay(changingView);
9048efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek                    }
9058efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek                });
906b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek            } else if (event.animationType ==
907f336f4c13ad3be15e2b44266cd786685975425f2Selim Cinek                NotificationStackScrollLayout.AnimationEvent.ANIMATION_TYPE_REMOVE_SWIPED_OUT) {
908f336f4c13ad3be15e2b44266cd786685975425f2Selim Cinek                // A race condition can trigger the view to be added to the overlay even though
909d13956475e97da9de83f6519eab514770118e7afSelim Cinek                // it was fully swiped out. So let's remove it
910f336f4c13ad3be15e2b44266cd786685975425f2Selim Cinek                mHostLayout.getOverlay().remove(changingView);
911d13956475e97da9de83f6519eab514770118e7afSelim Cinek                if (Math.abs(changingView.getTranslation()) == changingView.getWidth()
912d13956475e97da9de83f6519eab514770118e7afSelim Cinek                        && changingView.getTransientContainer() != null) {
913d13956475e97da9de83f6519eab514770118e7afSelim Cinek                    changingView.getTransientContainer().removeTransientView(changingView);
914d13956475e97da9de83f6519eab514770118e7afSelim Cinek                }
915b5605e58cb8080c8c887b1885336b707596c8094Selim Cinek            } else if (event.animationType == NotificationStackScrollLayout
916b5605e58cb8080c8c887b1885336b707596c8094Selim Cinek                    .AnimationEvent.ANIMATION_TYPE_GROUP_EXPANSION_CHANGED) {
917b5605e58cb8080c8c887b1885336b707596c8094Selim Cinek                ExpandableNotificationRow row = (ExpandableNotificationRow) event.changingView;
918b5605e58cb8080c8c887b1885336b707596c8094Selim Cinek                row.prepareExpansionChanged(finalState);
919b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek            } else if (event.animationType == NotificationStackScrollLayout
920b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek                    .AnimationEvent.ANIMATION_TYPE_HEADS_UP_APPEAR) {
921b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek                // This item is added, initialize it's properties.
922b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek                StackViewState viewState = finalState.getViewStateForView(changingView);
923b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek                mTmpState.copyFrom(viewState);
924a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek                if (event.headsUpFromBottom) {
925a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek                    mTmpState.yTranslation = mHeadsUpAppearHeightBottom;
926a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek                } else {
927a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek                    mTmpState.yTranslation = -mTmpState.height;
928a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek                }
929a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek                mHeadsUpAppearChildren.add(changingView);
930b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek                finalState.applyState(changingView, mTmpState);
931a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek            } else if (event.animationType == NotificationStackScrollLayout
9325eb67c22489f5fda22770d1fc824e7a8013ec025Jorim Jaggi                            .AnimationEvent.ANIMATION_TYPE_HEADS_UP_DISAPPEAR ||
9335eb67c22489f5fda22770d1fc824e7a8013ec025Jorim Jaggi                    event.animationType == NotificationStackScrollLayout
9345eb67c22489f5fda22770d1fc824e7a8013ec025Jorim Jaggi                            .AnimationEvent.ANIMATION_TYPE_HEADS_UP_DISAPPEAR_CLICK) {
935a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek                mHeadsUpDisappearChildren.add(changingView);
936ef5127ea5f34f7a4c961021f6b691174bcb81d2eSelim Cinek                if (changingView.getParent() == null) {
9378f93763cf0f4b3c41f6b0ecac09b621eb0ca328bSelim Cinek                    // This notification was actually removed, so we need to add it to the overlay
9388f93763cf0f4b3c41f6b0ecac09b621eb0ca328bSelim Cinek                    mHostLayout.getOverlay().add(changingView);
939eaee9c01902ecfc253be98d68e5d7b586ed54463Selim Cinek                    mTmpState.initFrom(changingView);
940eaee9c01902ecfc253be98d68e5d7b586ed54463Selim Cinek                    mTmpState.yTranslation = -changingView.getActualHeight();
9418f93763cf0f4b3c41f6b0ecac09b621eb0ca328bSelim Cinek                    // We temporarily enable Y animations, the real filter will be combined
9428f93763cf0f4b3c41f6b0ecac09b621eb0ca328bSelim Cinek                    // afterwards anyway
9438f93763cf0f4b3c41f6b0ecac09b621eb0ca328bSelim Cinek                    mAnimationFilter.animateY = true;
9445eb67c22489f5fda22770d1fc824e7a8013ec025Jorim Jaggi                    startViewAnimations(changingView, mTmpState,
9455eb67c22489f5fda22770d1fc824e7a8013ec025Jorim Jaggi                            event.animationType == NotificationStackScrollLayout
9465eb67c22489f5fda22770d1fc824e7a8013ec025Jorim Jaggi                                    .AnimationEvent.ANIMATION_TYPE_HEADS_UP_DISAPPEAR_CLICK
9475eb67c22489f5fda22770d1fc824e7a8013ec025Jorim Jaggi                                            ? ANIMATION_DELAY_HEADS_UP
9485eb67c22489f5fda22770d1fc824e7a8013ec025Jorim Jaggi                                            : 0,
9498f93763cf0f4b3c41f6b0ecac09b621eb0ca328bSelim Cinek                            ANIMATION_DURATION_HEADS_UP_DISAPPEAR);
9508f93763cf0f4b3c41f6b0ecac09b621eb0ca328bSelim Cinek                    mChildrenToClearFromOverlay.add(changingView);
9518f93763cf0f4b3c41f6b0ecac09b621eb0ca328bSelim Cinek                }
952572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek            }
9538efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek            mNewEvents.add(event);
954572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek        }
955572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek    }
9568d9ff9c2c66bc1d3b92eb6992d58599ff80ed6dcSelim Cinek
957a5703187a9dba418f742fab0874a8f683b0bbb0eSelim Cinek    public static void removeFromOverlay(View changingView) {
958a5703187a9dba418f742fab0874a8f683b0bbb0eSelim Cinek        ViewGroup parent = (ViewGroup) changingView.getParent();
959a5703187a9dba418f742fab0874a8f683b0bbb0eSelim Cinek        if (parent != null) {
960a5703187a9dba418f742fab0874a8f683b0bbb0eSelim Cinek            parent.removeView(changingView);
961a5703187a9dba418f742fab0874a8f683b0bbb0eSelim Cinek        }
962a5703187a9dba418f742fab0874a8f683b0bbb0eSelim Cinek    }
963a5703187a9dba418f742fab0874a8f683b0bbb0eSelim Cinek
964475b21dfe517ec04f435f6b02f4a53083d040db4Jorim Jaggi    public void animateOverScrollToAmount(float targetAmount, final boolean onTop,
965475b21dfe517ec04f435f6b02f4a53083d040db4Jorim Jaggi            final boolean isRubberbanded) {
9668d9ff9c2c66bc1d3b92eb6992d58599ff80ed6dcSelim Cinek        final float startOverScrollAmount = mHostLayout.getCurrentOverScrollAmount(onTop);
9679012958742c7a66b37ba5f2196f9086bb1980e6bJorim Jaggi        if (targetAmount == startOverScrollAmount) {
9689012958742c7a66b37ba5f2196f9086bb1980e6bJorim Jaggi            return;
9699012958742c7a66b37ba5f2196f9086bb1980e6bJorim Jaggi        }
9708d9ff9c2c66bc1d3b92eb6992d58599ff80ed6dcSelim Cinek        cancelOverScrollAnimators(onTop);
9718d9ff9c2c66bc1d3b92eb6992d58599ff80ed6dcSelim Cinek        ValueAnimator overScrollAnimator = ValueAnimator.ofFloat(startOverScrollAmount,
9728d9ff9c2c66bc1d3b92eb6992d58599ff80ed6dcSelim Cinek                targetAmount);
9738d9ff9c2c66bc1d3b92eb6992d58599ff80ed6dcSelim Cinek        overScrollAnimator.setDuration(ANIMATION_DURATION_STANDARD);
9748d9ff9c2c66bc1d3b92eb6992d58599ff80ed6dcSelim Cinek        overScrollAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
9758d9ff9c2c66bc1d3b92eb6992d58599ff80ed6dcSelim Cinek            @Override
9768d9ff9c2c66bc1d3b92eb6992d58599ff80ed6dcSelim Cinek            public void onAnimationUpdate(ValueAnimator animation) {
9778d9ff9c2c66bc1d3b92eb6992d58599ff80ed6dcSelim Cinek                float currentOverScroll = (float) animation.getAnimatedValue();
97847c85a3525dcd0bbd3168632830e8ab491d18462Jorim Jaggi                mHostLayout.setOverScrollAmount(
979475b21dfe517ec04f435f6b02f4a53083d040db4Jorim Jaggi                        currentOverScroll, onTop, false /* animate */, false /* cancelAnimators */,
980475b21dfe517ec04f435f6b02f4a53083d040db4Jorim Jaggi                        isRubberbanded);
9818d9ff9c2c66bc1d3b92eb6992d58599ff80ed6dcSelim Cinek            }
9828d9ff9c2c66bc1d3b92eb6992d58599ff80ed6dcSelim Cinek        });
983c18010f6720f606003cde3cd376ddacaca30f6e5Selim Cinek        overScrollAnimator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
98447c85a3525dcd0bbd3168632830e8ab491d18462Jorim Jaggi        overScrollAnimator.addListener(new AnimatorListenerAdapter() {
98547c85a3525dcd0bbd3168632830e8ab491d18462Jorim Jaggi            @Override
98647c85a3525dcd0bbd3168632830e8ab491d18462Jorim Jaggi            public void onAnimationEnd(Animator animation) {
98747c85a3525dcd0bbd3168632830e8ab491d18462Jorim Jaggi                if (onTop) {
98847c85a3525dcd0bbd3168632830e8ab491d18462Jorim Jaggi                    mTopOverScrollAnimator = null;
98947c85a3525dcd0bbd3168632830e8ab491d18462Jorim Jaggi                } else {
99047c85a3525dcd0bbd3168632830e8ab491d18462Jorim Jaggi                    mBottomOverScrollAnimator = null;
99147c85a3525dcd0bbd3168632830e8ab491d18462Jorim Jaggi                }
99247c85a3525dcd0bbd3168632830e8ab491d18462Jorim Jaggi            }
99347c85a3525dcd0bbd3168632830e8ab491d18462Jorim Jaggi        });
9948d9ff9c2c66bc1d3b92eb6992d58599ff80ed6dcSelim Cinek        overScrollAnimator.start();
9958d9ff9c2c66bc1d3b92eb6992d58599ff80ed6dcSelim Cinek        if (onTop) {
9968d9ff9c2c66bc1d3b92eb6992d58599ff80ed6dcSelim Cinek            mTopOverScrollAnimator = overScrollAnimator;
9978d9ff9c2c66bc1d3b92eb6992d58599ff80ed6dcSelim Cinek        } else {
9988d9ff9c2c66bc1d3b92eb6992d58599ff80ed6dcSelim Cinek            mBottomOverScrollAnimator = overScrollAnimator;
9998d9ff9c2c66bc1d3b92eb6992d58599ff80ed6dcSelim Cinek        }
10008d9ff9c2c66bc1d3b92eb6992d58599ff80ed6dcSelim Cinek    }
10018d9ff9c2c66bc1d3b92eb6992d58599ff80ed6dcSelim Cinek
10028d9ff9c2c66bc1d3b92eb6992d58599ff80ed6dcSelim Cinek    public void cancelOverScrollAnimators(boolean onTop) {
10038d9ff9c2c66bc1d3b92eb6992d58599ff80ed6dcSelim Cinek        ValueAnimator currentAnimator = onTop ? mTopOverScrollAnimator : mBottomOverScrollAnimator;
10048d9ff9c2c66bc1d3b92eb6992d58599ff80ed6dcSelim Cinek        if (currentAnimator != null) {
10058d9ff9c2c66bc1d3b92eb6992d58599ff80ed6dcSelim Cinek            currentAnimator.cancel();
10068d9ff9c2c66bc1d3b92eb6992d58599ff80ed6dcSelim Cinek        }
10078d9ff9c2c66bc1d3b92eb6992d58599ff80ed6dcSelim Cinek    }
100802af41efe54eb2cc8fde7311e4cf5f0e5ff2373cSelim Cinek
100902af41efe54eb2cc8fde7311e4cf5f0e5ff2373cSelim Cinek    /**
101002af41efe54eb2cc8fde7311e4cf5f0e5ff2373cSelim Cinek     * Get the end value of the height animation running on a view or the actualHeight
101102af41efe54eb2cc8fde7311e4cf5f0e5ff2373cSelim Cinek     * if no animation is running.
101202af41efe54eb2cc8fde7311e4cf5f0e5ff2373cSelim Cinek     */
101302af41efe54eb2cc8fde7311e4cf5f0e5ff2373cSelim Cinek    public static int getFinalActualHeight(ExpandableView view) {
101402af41efe54eb2cc8fde7311e4cf5f0e5ff2373cSelim Cinek        if (view == null) {
101502af41efe54eb2cc8fde7311e4cf5f0e5ff2373cSelim Cinek            return 0;
101602af41efe54eb2cc8fde7311e4cf5f0e5ff2373cSelim Cinek        }
101702af41efe54eb2cc8fde7311e4cf5f0e5ff2373cSelim Cinek        ValueAnimator heightAnimator = getChildTag(view, TAG_ANIMATOR_HEIGHT);
101802af41efe54eb2cc8fde7311e4cf5f0e5ff2373cSelim Cinek        if (heightAnimator == null) {
101902af41efe54eb2cc8fde7311e4cf5f0e5ff2373cSelim Cinek            return view.getActualHeight();
102002af41efe54eb2cc8fde7311e4cf5f0e5ff2373cSelim Cinek        } else {
102102af41efe54eb2cc8fde7311e4cf5f0e5ff2373cSelim Cinek            return getChildTag(view, TAG_END_HEIGHT);
102202af41efe54eb2cc8fde7311e4cf5f0e5ff2373cSelim Cinek        }
102302af41efe54eb2cc8fde7311e4cf5f0e5ff2373cSelim Cinek    }
1024a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek
10256811d728fb988a32cbfc836b768a261a7f60c98aSelim Cinek    /**
1026c897bd3e91ed442b0f32c3a5b9918e9e580b62b1Selim Cinek     * Get the end value of the yTranslation animation running on a view or the yTranslation
10276811d728fb988a32cbfc836b768a261a7f60c98aSelim Cinek     * if no animation is running.
10286811d728fb988a32cbfc836b768a261a7f60c98aSelim Cinek     */
1029c897bd3e91ed442b0f32c3a5b9918e9e580b62b1Selim Cinek    public static float getFinalTranslationY(View view) {
10306811d728fb988a32cbfc836b768a261a7f60c98aSelim Cinek        if (view == null) {
10316811d728fb988a32cbfc836b768a261a7f60c98aSelim Cinek            return 0;
10326811d728fb988a32cbfc836b768a261a7f60c98aSelim Cinek        }
10336811d728fb988a32cbfc836b768a261a7f60c98aSelim Cinek        ValueAnimator yAnimator = getChildTag(view, TAG_ANIMATOR_TRANSLATION_Y);
10346811d728fb988a32cbfc836b768a261a7f60c98aSelim Cinek        if (yAnimator == null) {
10356811d728fb988a32cbfc836b768a261a7f60c98aSelim Cinek            return view.getTranslationY();
10366811d728fb988a32cbfc836b768a261a7f60c98aSelim Cinek        } else {
10376811d728fb988a32cbfc836b768a261a7f60c98aSelim Cinek            return getChildTag(view, TAG_END_TRANSLATION_Y);
10386811d728fb988a32cbfc836b768a261a7f60c98aSelim Cinek        }
10396811d728fb988a32cbfc836b768a261a7f60c98aSelim Cinek    }
10406811d728fb988a32cbfc836b768a261a7f60c98aSelim Cinek
1041a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek    public void setHeadsUpAppearHeightBottom(int headsUpAppearHeightBottom) {
1042a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek        mHeadsUpAppearHeightBottom = headsUpAppearHeightBottom;
1043a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek    }
1044a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek
1045a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek    public void setShadeExpanded(boolean shadeExpanded) {
1046a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek        mShadeExpanded = shadeExpanded;
1047a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek    }
1048572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek}
1049