StackStateAnimator.java revision 8df56452cb696ebdee82df6fb255892eabf3febc
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;
21d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggiimport android.animation.AnimatorSet;
22eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinekimport android.animation.ObjectAnimator;
23d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggiimport android.animation.PropertyValuesHolder;
24572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinekimport android.animation.ValueAnimator;
25572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinekimport android.view.View;
26572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinekimport android.view.animation.AnimationUtils;
27572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinekimport android.view.animation.Interpolator;
28eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek
29eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinekimport com.android.systemui.R;
30572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinekimport com.android.systemui.statusbar.ExpandableView;
31572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek
32572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinekimport java.util.ArrayList;
33eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinekimport java.util.HashSet;
34eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinekimport java.util.Set;
35eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinekimport java.util.Stack;
36572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek
37572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek/**
38572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek * An stack state animator which handles animations to new StackScrollStates
39572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek */
40572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinekpublic class StackStateAnimator {
41572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek
425aa045cc6bca84f5c11f1a99999546ba5e5949a5Jorim Jaggi    public static final int ANIMATION_DURATION_STANDARD = 360;
435aa045cc6bca84f5c11f1a99999546ba5e5949a5Jorim Jaggi    public static final int ANIMATION_DURATION_DIMMED_ACTIVATED = 220;
445aa045cc6bca84f5c11f1a99999546ba5e5949a5Jorim Jaggi
45eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek    private static final int TAG_ANIMATOR_TRANSLATION_Y = R.id.translation_y_animator_tag;
46eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek    private static final int TAG_ANIMATOR_TRANSLATION_Z = R.id.translation_z_animator_tag;
47d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi    private static final int TAG_ANIMATOR_SCALE = R.id.scale_animator_tag;
48eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek    private static final int TAG_ANIMATOR_ALPHA = R.id.alpha_animator_tag;
49eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek    private static final int TAG_ANIMATOR_HEIGHT = R.id.height_animator_tag;
50eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek    private static final int TAG_ANIMATOR_TOP_INSET = R.id.top_inset_animator_tag;
51eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek    private static final int TAG_END_TRANSLATION_Y = R.id.translation_y_animator_end_value_tag;
52eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek    private static final int TAG_END_TRANSLATION_Z = R.id.translation_z_animator_end_value_tag;
53d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi    private static final int TAG_END_SCALE = R.id.scale_animator_end_value_tag;
54eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek    private static final int TAG_END_ALPHA = R.id.alpha_animator_end_value_tag;
55eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek    private static final int TAG_END_HEIGHT = R.id.height_animator_end_value_tag;
56eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek    private static final int TAG_END_TOP_INSET = R.id.top_inset_animator_end_value_tag;
578df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek    private static final int TAG_START_TRANSLATION_Y = R.id.translation_y_animator_start_value_tag;
588df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek    private static final int TAG_START_TRANSLATION_Z = R.id.translation_z_animator_start_value_tag;
598df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek    private static final int TAG_START_SCALE = R.id.scale_animator_start_value_tag;
608df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek    private static final int TAG_START_ALPHA = R.id.alpha_animator_start_value_tag;
618df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek    private static final int TAG_START_HEIGHT = R.id.height_animator_start_value_tag;
628df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek    private static final int TAG_START_TOP_INSET = R.id.top_inset_animator_start_value_tag;
63572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek
64572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek    private final Interpolator mFastOutSlowInInterpolator;
65572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek    public NotificationStackScrollLayout mHostLayout;
660dd6881ea481c855976214807c17595b34a2920aJorim Jaggi    private ArrayList<NotificationStackScrollLayout.AnimationEvent> mHandledEvents =
67572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek            new ArrayList<>();
68eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek    private ArrayList<NotificationStackScrollLayout.AnimationEvent> mNewEvents =
69eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek            new ArrayList<>();
70eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek    private Set<Animator> mAnimatorSet = new HashSet<Animator>();
71eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek    private Stack<AnimatorListenerAdapter> mAnimationListenerPool
72eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek            = new Stack<AnimatorListenerAdapter>();
73d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi    private AnimationFilter mAnimationFilter = new AnimationFilter();
745aa045cc6bca84f5c11f1a99999546ba5e5949a5Jorim Jaggi    private long mCurrentLength;
75572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek
76572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek    public StackStateAnimator(NotificationStackScrollLayout hostLayout) {
77572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek        mHostLayout = hostLayout;
78572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek        mFastOutSlowInInterpolator = AnimationUtils.loadInterpolator(hostLayout.getContext(),
79eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek                android.R.interpolator.fast_out_slow_in);
80572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek    }
81572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek
82572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek    public boolean isRunning() {
83eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        return !mAnimatorSet.isEmpty();
84572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek    }
85572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek
86572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek    public void startAnimationForEvents(
870dd6881ea481c855976214807c17595b34a2920aJorim Jaggi            ArrayList<NotificationStackScrollLayout.AnimationEvent> mAnimationEvents,
88572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek            StackScrollState finalState) {
89eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek
90eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        processAnimationEvents(mAnimationEvents, finalState);
91eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek
92572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek        int childCount = mHostLayout.getChildCount();
93d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi        mAnimationFilter.applyCombination(mNewEvents);
945aa045cc6bca84f5c11f1a99999546ba5e5949a5Jorim Jaggi        mCurrentLength = NotificationStackScrollLayout.AnimationEvent.combineLength(mNewEvents);
95572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek        for (int i = 0; i < childCount; i++) {
96572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek            final ExpandableView child = (ExpandableView) mHostLayout.getChildAt(i);
97572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek            StackScrollState.ViewState viewState = finalState.getViewStateForView(child);
98572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek            if (viewState == null) {
99572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek                continue;
100572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek            }
101572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek
102d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi            startAnimations(child, viewState);
103572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek
104572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek            child.setClipBounds(null);
105572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek        }
106eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        if (!isRunning()) {
107eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek            // no child has preformed any animation, lets finish
108eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek            onAnimationFinished();
109eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        }
110572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek    }
111572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek
112eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek    /**
113eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek     * Start an animation to the given viewState
114eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek     */
115d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi    private void startAnimations(final ExpandableView child, StackScrollState.ViewState viewState) {
116eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        int childVisibility = child.getVisibility();
117eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        boolean wasVisible = childVisibility == View.VISIBLE;
118eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        final float alpha = viewState.alpha;
119eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        if (!wasVisible && alpha != 0 && !viewState.gone) {
120eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek            child.setVisibility(View.VISIBLE);
121eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        }
122eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        // start translationY animation
123eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        if (child.getTranslationY() != viewState.yTranslation) {
124d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi            startYTranslationAnimation(child, viewState);
125eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        }
126eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        // start translationZ animation
127eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        if (child.getTranslationZ() != viewState.zTranslation) {
128d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi            startZTranslationAnimation(child, viewState);
129d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi        }
130d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi        // start scale animation
131d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi        if (child.getScaleX() != viewState.scale) {
132d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi            startScaleAnimation(child, viewState);
133eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        }
134eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        // start alpha animation
13559b5a356b828fe60ea2874b0680a1bf7c84809a1Jorim Jaggi        if (alpha != child.getAlpha()) {
136d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi            startAlphaAnimation(child, viewState);
137eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        }
138eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        // start height animation
139eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        if (viewState.height != child.getActualHeight()) {
140d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi            startHeightAnimation(child, viewState);
14159b5a356b828fe60ea2874b0680a1bf7c84809a1Jorim Jaggi        }
142d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi        // start dimmed animation
143d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi        child.setDimmed(viewState.dimmed, mAnimationFilter.animateDimmed);
144572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek    }
145572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek
146eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek    private void startHeightAnimation(final ExpandableView child,
147d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi            StackScrollState.ViewState viewState) {
1488df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek        Integer previousStartValue = getChildTag(child, TAG_START_HEIGHT);
149d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi        Integer previousEndValue = getChildTag(child, TAG_END_HEIGHT);
1508df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek        int newEndValue = viewState.height;
1518df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek        if (previousEndValue != null && previousEndValue == newEndValue) {
152eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek            return;
153eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        }
154eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        ValueAnimator previousAnimator = getChildTag(child, TAG_ANIMATOR_HEIGHT);
1558df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek        if (!mAnimationFilter.animateHeight) {
1568df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek            // just a local update was performed
1578df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek            if (previousAnimator != null) {
1588df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek                // we need to increase all animation keyframes of the previous animator by the
1598df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek                // relative change to the end value
1608df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek                PropertyValuesHolder[] values = previousAnimator.getValues();
1618df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek                int relativeDiff = newEndValue - previousEndValue;
1628df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek                int newStartValue = previousStartValue + relativeDiff;
1638df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek                values[0].setIntValues(newStartValue, newEndValue);
1648df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek                child.setTag(TAG_START_HEIGHT, newStartValue);
1658df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek                child.setTag(TAG_END_HEIGHT, newEndValue);
1668df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek                previousAnimator.setCurrentPlayTime(previousAnimator.getCurrentPlayTime());
1678df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek                return;
1688df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek            } else {
1698df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek                // no new animation needed, let's just apply the value
1708df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek                child.setActualHeight(newEndValue, false);
1718df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek                return;
172eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek            }
173eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        }
174eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek
1758df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek        ValueAnimator animator = ValueAnimator.ofInt(child.getActualHeight(), newEndValue);
176eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
177572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek            @Override
178572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek            public void onAnimationUpdate(ValueAnimator animation) {
179d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi                child.setActualHeight((int) animation.getAnimatedValue(),
180d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi                        false /* notifyListeners */);
181572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek            }
182572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek        });
183eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        animator.setInterpolator(mFastOutSlowInInterpolator);
1848df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek        long newDuration = cancelAnimatorAndGetNewDuration(previousAnimator);
185eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        animator.setDuration(newDuration);
186eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        animator.addListener(getGlobalAnimationFinishedListener());
187eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        // remove the tag when the animation is finished
188eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        animator.addListener(new AnimatorListenerAdapter() {
189eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek            @Override
190eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek            public void onAnimationEnd(Animator animation) {
191eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek                child.setTag(TAG_ANIMATOR_HEIGHT, null);
1928df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek                child.setTag(TAG_START_HEIGHT, null);
193eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek                child.setTag(TAG_END_HEIGHT, null);
194eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek            }
195eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        });
1963af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek        startInstantly(animator);
197eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        child.setTag(TAG_ANIMATOR_HEIGHT, animator);
1988df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek        child.setTag(TAG_START_HEIGHT, child.getActualHeight());
1998df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek        child.setTag(TAG_END_HEIGHT, newEndValue);
200eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek    }
201eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek
202eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek    private void startAlphaAnimation(final ExpandableView child,
203d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi            final StackScrollState.ViewState viewState) {
2048df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek        Float previousStartValue = getChildTag(child,TAG_START_ALPHA);
205eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        Float previousEndValue = getChildTag(child,TAG_END_ALPHA);
2068df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek        final float newEndValue = viewState.alpha;
2078df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek        if (previousEndValue != null && previousEndValue == newEndValue) {
208eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek            return;
209eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        }
210eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        ObjectAnimator previousAnimator = getChildTag(child, TAG_ANIMATOR_ALPHA);
2118df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek        if (!mAnimationFilter.animateAlpha) {
2128df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek            // just a local update was performed
2138df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek            if (previousAnimator != null) {
2148df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek                // we need to increase all animation keyframes of the previous animator by the
2158df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek                // relative change to the end value
2168df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek                PropertyValuesHolder[] values = previousAnimator.getValues();
2178df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek                float relativeDiff = newEndValue - previousEndValue;
2188df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek                float newStartValue = previousStartValue + relativeDiff;
2198df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek                values[0].setFloatValues(newStartValue, newEndValue);
2208df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek                child.setTag(TAG_START_ALPHA, newStartValue);
2218df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek                child.setTag(TAG_END_ALPHA, newEndValue);
2228df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek                previousAnimator.setCurrentPlayTime(previousAnimator.getCurrentPlayTime());
2238df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek                return;
2248df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek            } else {
2258df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek                // no new animation needed, let's just apply the value
2268df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek                child.setAlpha(newEndValue);
2278df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek                if (newEndValue == 0) {
2288df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek                    child.setVisibility(View.INVISIBLE);
2298df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek                }
230eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek            }
231eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        }
232eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek
233eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        ObjectAnimator animator = ObjectAnimator.ofFloat(child, View.ALPHA,
2348df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek                child.getAlpha(), newEndValue);
235eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        animator.setInterpolator(mFastOutSlowInInterpolator);
236eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        // Handle layer type
237eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        final int currentLayerType = child.getLayerType();
238eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        child.setLayerType(View.LAYER_TYPE_HARDWARE, null);
239eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        animator.addListener(new AnimatorListenerAdapter() {
240eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek            public boolean mWasCancelled;
241eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek
242eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek            @Override
243eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek            public void onAnimationEnd(Animator animation) {
244eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek                child.setLayerType(currentLayerType, null);
2458df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek                if (newEndValue == 0 && !mWasCancelled) {
246eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek                    child.setVisibility(View.INVISIBLE);
247eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek                }
248eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek                child.setTag(TAG_ANIMATOR_ALPHA, null);
2498df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek                child.setTag(TAG_START_ALPHA, null);
250eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek                child.setTag(TAG_END_ALPHA, null);
251eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek            }
252eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek
253eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek            @Override
254eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek            public void onAnimationCancel(Animator animation) {
255eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek                mWasCancelled = true;
256eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek            }
257eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek
258eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek            @Override
259eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek            public void onAnimationStart(Animator animation) {
260eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek                mWasCancelled = false;
261eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek            }
262eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        });
2638df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek        long newDuration = cancelAnimatorAndGetNewDuration(previousAnimator);
2643af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek        animator.setDuration(newDuration);
265eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        animator.addListener(getGlobalAnimationFinishedListener());
266eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        // remove the tag when the animation is finished
267eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        animator.addListener(new AnimatorListenerAdapter() {
268eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek            @Override
269eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek            public void onAnimationEnd(Animator animation) {
270eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek
271eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek            }
272eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        });
2733af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek        startInstantly(animator);
274eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        child.setTag(TAG_ANIMATOR_ALPHA, animator);
2758df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek        child.setTag(TAG_START_ALPHA, child.getAlpha());
2768df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek        child.setTag(TAG_END_ALPHA, newEndValue);
277572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek    }
278572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek
279eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek    private void startZTranslationAnimation(final ExpandableView child,
280d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi            final StackScrollState.ViewState viewState) {
2818df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek        Float previousStartValue = getChildTag(child,TAG_START_TRANSLATION_Z);
282eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        Float previousEndValue = getChildTag(child,TAG_END_TRANSLATION_Z);
2838df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek        float newEndValue = viewState.zTranslation;
2848df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek        if (previousEndValue != null && previousEndValue == newEndValue) {
285eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek            return;
286eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        }
287eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        ObjectAnimator previousAnimator = getChildTag(child, TAG_ANIMATOR_TRANSLATION_Z);
2888df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek        if (!mAnimationFilter.animateZ) {
2898df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek            // just a local update was performed
2908df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek            if (previousAnimator != null) {
2918df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek                // we need to increase all animation keyframes of the previous animator by the
2928df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek                // relative change to the end value
2938df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek                PropertyValuesHolder[] values = previousAnimator.getValues();
2948df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek                float relativeDiff = newEndValue - previousEndValue;
2958df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek                float newStartValue = previousStartValue + relativeDiff;
2968df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek                values[0].setFloatValues(newStartValue, newEndValue);
2978df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek                child.setTag(TAG_START_TRANSLATION_Z, newStartValue);
2988df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek                child.setTag(TAG_END_TRANSLATION_Z, newEndValue);
2998df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek                previousAnimator.setCurrentPlayTime(previousAnimator.getCurrentPlayTime());
3008df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek                return;
3018df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek            } else {
3028df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek                // no new animation needed, let's just apply the value
3038df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek                child.setTranslationZ(newEndValue);
304eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek            }
305eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        }
306eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek
307eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        ObjectAnimator animator = ObjectAnimator.ofFloat(child, View.TRANSLATION_Z,
3088df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek                child.getTranslationZ(), newEndValue);
309eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        animator.setInterpolator(mFastOutSlowInInterpolator);
3108df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek        long newDuration = cancelAnimatorAndGetNewDuration(previousAnimator);
3113af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek        animator.setDuration(newDuration);
312eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        animator.addListener(getGlobalAnimationFinishedListener());
313eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        // remove the tag when the animation is finished
314eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        animator.addListener(new AnimatorListenerAdapter() {
315eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek            @Override
316eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek            public void onAnimationEnd(Animator animation) {
317eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek                child.setTag(TAG_ANIMATOR_TRANSLATION_Z, null);
3188df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek                child.setTag(TAG_START_TRANSLATION_Z, null);
319eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek                child.setTag(TAG_END_TRANSLATION_Z, null);
320eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek            }
321eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        });
3223af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek        startInstantly(animator);
323eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        child.setTag(TAG_ANIMATOR_TRANSLATION_Z, animator);
3248df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek        child.setTag(TAG_START_TRANSLATION_Z, child.getTranslationZ());
3258df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek        child.setTag(TAG_END_TRANSLATION_Z, newEndValue);
326eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek    }
327eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek
328eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek    private void startYTranslationAnimation(final ExpandableView child,
329d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi            StackScrollState.ViewState viewState) {
3308df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek        Float previousStartValue = getChildTag(child,TAG_START_TRANSLATION_Y);
331eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        Float previousEndValue = getChildTag(child,TAG_END_TRANSLATION_Y);
3328df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek        float newEndValue = viewState.yTranslation;
3338df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek        if (previousEndValue != null && previousEndValue == newEndValue) {
334eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek            return;
335eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        }
336eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        ObjectAnimator previousAnimator = getChildTag(child, TAG_ANIMATOR_TRANSLATION_Y);
3378df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek        if (!mAnimationFilter.animateY) {
3388df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek            // just a local update was performed
3398df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek            if (previousAnimator != null) {
3408df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek                // we need to increase all animation keyframes of the previous animator by the
3418df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek                // relative change to the end value
3428df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek                PropertyValuesHolder[] values = previousAnimator.getValues();
3438df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek                float relativeDiff = newEndValue - previousEndValue;
3448df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek                float newStartValue = previousStartValue + relativeDiff;
3458df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek                values[0].setFloatValues(newStartValue, newEndValue);
3468df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek                child.setTag(TAG_START_TRANSLATION_Y, newStartValue);
3478df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek                child.setTag(TAG_END_TRANSLATION_Y, newEndValue);
3488df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek                previousAnimator.setCurrentPlayTime(previousAnimator.getCurrentPlayTime());
3498df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek                return;
3508df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek            } else {
3518df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek                // no new animation needed, let's just apply the value
3528df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek                child.setTranslationY(newEndValue);
3538df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek                return;
354eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek            }
355eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        }
356eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek
357eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        ObjectAnimator animator = ObjectAnimator.ofFloat(child, View.TRANSLATION_Y,
3588df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek                child.getTranslationY(), newEndValue);
359eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        animator.setInterpolator(mFastOutSlowInInterpolator);
3608df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek        long newDuration = cancelAnimatorAndGetNewDuration(previousAnimator);
3613af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek        animator.setDuration(newDuration);
362eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        animator.addListener(getGlobalAnimationFinishedListener());
363eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        // remove the tag when the animation is finished
364eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        animator.addListener(new AnimatorListenerAdapter() {
365eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek            @Override
366eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek            public void onAnimationEnd(Animator animation) {
367eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek                child.setTag(TAG_ANIMATOR_TRANSLATION_Y, null);
3688df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek                child.setTag(TAG_START_TRANSLATION_Y, null);
369eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek                child.setTag(TAG_END_TRANSLATION_Y, null);
370eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek            }
371eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        });
3723af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek        startInstantly(animator);
373eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        child.setTag(TAG_ANIMATOR_TRANSLATION_Y, animator);
3748df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek        child.setTag(TAG_START_TRANSLATION_Y, child.getTranslationY());
3758df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek        child.setTag(TAG_END_TRANSLATION_Y, newEndValue);
376eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek    }
377eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek
378d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi    private void startScaleAnimation(final ExpandableView child,
379d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi            StackScrollState.ViewState viewState) {
3808df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek        Float previousStartValue = getChildTag(child, TAG_START_SCALE);
381d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi        Float previousEndValue = getChildTag(child, TAG_END_SCALE);
3828df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek        float newEndValue = viewState.scale;
3838df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek        if (previousEndValue != null && previousEndValue == newEndValue) {
384d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi            return;
385d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi        }
386d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi        ObjectAnimator previousAnimator = getChildTag(child, TAG_ANIMATOR_SCALE);
3878df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek        if (!mAnimationFilter.animateScale) {
3888df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek            // just a local update was performed
3898df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek            if (previousAnimator != null) {
3908df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek                // we need to increase all animation keyframes of the previous animator by the
3918df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek                // relative change to the end value
3928df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek                PropertyValuesHolder[] values = previousAnimator.getValues();
3938df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek                float relativeDiff = newEndValue - previousEndValue;
3948df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek                float newStartValue = previousStartValue + relativeDiff;
3958df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek                values[0].setFloatValues(newStartValue, newEndValue);
3968df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek                values[1].setFloatValues(newStartValue, newEndValue);
3978df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek                child.setTag(TAG_START_SCALE, newStartValue);
3988df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek                child.setTag(TAG_END_SCALE, newEndValue);
3998df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek                previousAnimator.setCurrentPlayTime(previousAnimator.getCurrentPlayTime());
4008df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek                return;
4018df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek            } else {
4028df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek                // no new animation needed, let's just apply the value
4038df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek                child.setScaleX(newEndValue);
4048df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek                child.setScaleY(newEndValue);
405d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi            }
406d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi        }
407d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi
408d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi        PropertyValuesHolder holderX =
4098df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek                PropertyValuesHolder.ofFloat(View.SCALE_X, child.getScaleX(), newEndValue);
410d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi        PropertyValuesHolder holderY =
4118df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek                PropertyValuesHolder.ofFloat(View.SCALE_Y, child.getScaleY(), newEndValue);
412d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi        ObjectAnimator animator = ObjectAnimator.ofPropertyValuesHolder(child, holderX, holderY);
413d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi        animator.setInterpolator(mFastOutSlowInInterpolator);
4148df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek        long newDuration = cancelAnimatorAndGetNewDuration(previousAnimator);
415d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi        animator.setDuration(newDuration);
416d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi        animator.addListener(getGlobalAnimationFinishedListener());
417d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi        // remove the tag when the animation is finished
418d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi        animator.addListener(new AnimatorListenerAdapter() {
419d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi            @Override
420d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi            public void onAnimationEnd(Animator animation) {
421d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi                child.setTag(TAG_ANIMATOR_SCALE, null);
4228df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek                child.setTag(TAG_START_SCALE, null);
423d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi                child.setTag(TAG_END_SCALE, null);
424d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi            }
425d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi        });
426d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi        startInstantly(animator);
427d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi        child.setTag(TAG_ANIMATOR_SCALE, animator);
4288df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek        child.setTag(TAG_START_SCALE, child.getScaleX());
4298df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek        child.setTag(TAG_END_SCALE, newEndValue);
430d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi    }
431d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi
4323af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek    /**
4333af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek     * Start an animator instantly instead of waiting on the next synchronization frame
4343af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek     */
4353af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek    private void startInstantly(ValueAnimator animator) {
4363af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek        animator.start();
4373af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek        animator.setCurrentPlayTime(0);
4383af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek    }
4393af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek
4403af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek    /**
4413af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek     * @return an adapter which ensures that onAnimationFinished is called once no animation is
4423af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek     *         running anymore
4433af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek     */
4443af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek    private AnimatorListenerAdapter getGlobalAnimationFinishedListener() {
4453af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek        if (!mAnimationListenerPool.empty()) {
4463af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek            return mAnimationListenerPool.pop();
4473af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek        }
4483af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek
4493af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek        // We need to create a new one, no reusable ones found
4503af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek        return new AnimatorListenerAdapter() {
4513af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek            private boolean mWasCancelled;
4523af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek
4533af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek            @Override
4543af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek            public void onAnimationEnd(Animator animation) {
4553af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek                mAnimatorSet.remove(animation);
4563af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek                if (mAnimatorSet.isEmpty() && !mWasCancelled) {
4573af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek                    onAnimationFinished();
4583af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek                }
4593af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek                mAnimationListenerPool.push(this);
4603af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek            }
4613af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek
4623af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek            @Override
4633af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek            public void onAnimationCancel(Animator animation) {
4643af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek                mWasCancelled = true;
4653af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek            }
4663af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek
4673af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek            @Override
4683af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek            public void onAnimationStart(Animator animation) {
4693af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek                mAnimatorSet.add(animation);
4703af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek                mWasCancelled = false;
4713af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek            }
4723af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek        };
4733af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek    }
4743af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek
475eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek    private <T> T getChildTag(View child, int tag) {
476eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        return (T) child.getTag(tag);
477eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek    }
478eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek
479eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek    /**
480eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek     * Cancel the previous animator and get the duration of the new animation.
48139610545f0c2714a3526bc935effe57b421542d1Selim Cinek     *
482eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek     * @param previousAnimator the animator which was running before
483eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek     * @return the new duration
484eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek     */
4858df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek    private long cancelAnimatorAndGetNewDuration(ValueAnimator previousAnimator) {
4865aa045cc6bca84f5c11f1a99999546ba5e5949a5Jorim Jaggi        long newDuration = mCurrentLength;
487eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        if (previousAnimator != null) {
4888df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek            // We take either the desired length of the new animation or the remaining time of
4898df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek            // the previous animator, whichever is longer.
4908df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek            newDuration = Math.max(previousAnimator.getDuration()
4918df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek                    - previousAnimator.getCurrentPlayTime(), newDuration);
4923af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek            previousAnimator.cancel();
493eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        }
4943af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek        return newDuration;
495eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek    }
496eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek
497eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek    private void onAnimationFinished() {
498eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        mHandledEvents.clear();
499eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        mNewEvents.clear();
500eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        mHostLayout.onChildAnimationFinished();
501eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek    }
502eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek
503eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek    /**
504eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek     * Process the animationEvents for a new animation
505eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek     *
506eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek     * @param animationEvents the animation events for the animation to perform
50739610545f0c2714a3526bc935effe57b421542d1Selim Cinek     * @param finalState the final state to animate to
50839610545f0c2714a3526bc935effe57b421542d1Selim Cinek     */
509eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek    private void processAnimationEvents(
5100dd6881ea481c855976214807c17595b34a2920aJorim Jaggi            ArrayList<NotificationStackScrollLayout.AnimationEvent> animationEvents,
511572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek            StackScrollState finalState) {
512eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        mNewEvents.clear();
5130dd6881ea481c855976214807c17595b34a2920aJorim Jaggi        for (NotificationStackScrollLayout.AnimationEvent event: animationEvents) {
514572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek            View changingView = event.changingView;
515eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek            if (!mHandledEvents.contains(event)) {
516eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek                if (event.animationType == NotificationStackScrollLayout.AnimationEvent
517eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek                        .ANIMATION_TYPE_ADD) {
518eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek
519eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek                    // This item is added, initialize it's properties.
520eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek                    StackScrollState.ViewState viewState = finalState
521eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek                            .getViewStateForView(changingView);
522eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek                    if (viewState == null) {
523eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek                        // The position for this child was never generated, let's continue.
524eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek                        continue;
525eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek                    }
526eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek                    changingView.setAlpha(0);
527eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek                    changingView.setTranslationY(viewState.yTranslation);
528eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek                    changingView.setTranslationZ(viewState.zTranslation);
52939610545f0c2714a3526bc935effe57b421542d1Selim Cinek                }
530572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek                mHandledEvents.add(event);
531eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek                mNewEvents.add(event);
532572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek            }
533572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek        }
534572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek    }
535572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek}
536