StackStateAnimator.java revision eb973565f3efc6417ca35363e4d6c642947775d8
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;
22572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinekimport android.animation.ValueAnimator;
23572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinekimport android.view.View;
24572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinekimport android.view.animation.AnimationUtils;
25572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinekimport android.view.animation.Interpolator;
26eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek
27eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinekimport com.android.systemui.R;
28572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinekimport com.android.systemui.statusbar.ExpandableView;
29572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek
30572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinekimport java.util.ArrayList;
31eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinekimport java.util.HashSet;
32eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinekimport java.util.Set;
33eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinekimport java.util.Stack;
34572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek
35572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek/**
36572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek * An stack state animator which handles animations to new StackScrollStates
37572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek */
38572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinekpublic class StackStateAnimator {
39572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek
40572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek    private static final int ANIMATION_DURATION = 360;
41eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek    private static final int TAG_ANIMATOR_TRANSLATION_Y = R.id.translation_y_animator_tag;
42eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek    private static final int TAG_ANIMATOR_TRANSLATION_Z = R.id.translation_z_animator_tag;
43eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek    private static final int TAG_ANIMATOR_ALPHA = R.id.alpha_animator_tag;
44eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek    private static final int TAG_ANIMATOR_HEIGHT = R.id.height_animator_tag;
45eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek    private static final int TAG_ANIMATOR_TOP_INSET = R.id.top_inset_animator_tag;
46eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek    private static final int TAG_END_TRANSLATION_Y = R.id.translation_y_animator_end_value_tag;
47eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek    private static final int TAG_END_TRANSLATION_Z = R.id.translation_z_animator_end_value_tag;
48eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek    private static final int TAG_END_ALPHA = R.id.alpha_animator_end_value_tag;
49eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek    private static final int TAG_END_HEIGHT = R.id.height_animator_end_value_tag;
50eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek    private static final int TAG_END_TOP_INSET = R.id.top_inset_animator_end_value_tag;
51572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek
52572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek    private final Interpolator mFastOutSlowInInterpolator;
53572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek    public NotificationStackScrollLayout mHostLayout;
540dd6881ea481c855976214807c17595b34a2920aJorim Jaggi    private ArrayList<NotificationStackScrollLayout.AnimationEvent> mHandledEvents =
55572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek            new ArrayList<>();
56eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek    private ArrayList<NotificationStackScrollLayout.AnimationEvent> mNewEvents =
57eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek            new ArrayList<>();
58eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek    private Set<Animator> mAnimatorSet = new HashSet<Animator>();
59eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek    private Stack<AnimatorListenerAdapter> mAnimationListenerPool
60eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek            = new Stack<AnimatorListenerAdapter>();
61572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek
62572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek    public StackStateAnimator(NotificationStackScrollLayout hostLayout) {
63572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek        mHostLayout = hostLayout;
64572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek        mFastOutSlowInInterpolator = AnimationUtils.loadInterpolator(hostLayout.getContext(),
65eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek                android.R.interpolator.fast_out_slow_in);
66572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek    }
67572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek
68572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek    public boolean isRunning() {
69eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        return !mAnimatorSet.isEmpty();
70572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek    }
71572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek
72572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek    public void startAnimationForEvents(
730dd6881ea481c855976214807c17595b34a2920aJorim Jaggi            ArrayList<NotificationStackScrollLayout.AnimationEvent> mAnimationEvents,
74572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek            StackScrollState finalState) {
75eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek
76eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        processAnimationEvents(mAnimationEvents, finalState);
77eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek
78eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        boolean hasNewEvents = !mNewEvents.isEmpty();
79572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek        int childCount = mHostLayout.getChildCount();
80572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek        for (int i = 0; i < childCount; i++) {
81572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek            final ExpandableView child = (ExpandableView) mHostLayout.getChildAt(i);
82572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek            StackScrollState.ViewState viewState = finalState.getViewStateForView(child);
83572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek            if (viewState == null) {
84572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek                continue;
85572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek            }
86572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek
87eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek            startAnimations(child, viewState, hasNewEvents);
88572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek
89572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek            child.setClipBounds(null);
90572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek        }
91eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        if (!isRunning()) {
92eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek            // no child has preformed any animation, lets finish
93eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek            onAnimationFinished();
94eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        }
95572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek    }
96572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek
97eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek    /**
98eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek     * Start an animation to the given viewState
99eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek     */
100eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek    private void startAnimations(final ExpandableView child, StackScrollState.ViewState viewState,
101eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek            boolean hasNewEvents) {
102eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        int childVisibility = child.getVisibility();
103eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        boolean wasVisible = childVisibility == View.VISIBLE;
104eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        final float alpha = viewState.alpha;
105eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        if (!wasVisible && alpha != 0 && !viewState.gone) {
106eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek            child.setVisibility(View.VISIBLE);
107eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        }
108eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        // start translationY animation
109eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        if (child.getTranslationY() != viewState.yTranslation) {
110eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek            startYTranslationAnimation(child, viewState, hasNewEvents);
111eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        }
112eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        // start translationZ animation
113eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        if (child.getTranslationZ() != viewState.zTranslation) {
114eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek            startZTranslationAnimation(child, viewState, hasNewEvents);
115eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        }
116eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        // start alpha animation
11759b5a356b828fe60ea2874b0680a1bf7c84809a1Jorim Jaggi        if (alpha != child.getAlpha()) {
118eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek            startAlphaAnimation(child, viewState, hasNewEvents);
119eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        }
120eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        // start height animation
121eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        if (viewState.height != child.getActualHeight()) {
122eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek            startHeightAnimation(child, viewState, hasNewEvents);
12359b5a356b828fe60ea2874b0680a1bf7c84809a1Jorim Jaggi        }
124572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek    }
125572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek
126eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek    private void startHeightAnimation(final ExpandableView child,
127eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek            StackScrollState.ViewState viewState, boolean hasNewEvents) {
128eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        Integer previousEndValue = getChildTag(child,TAG_END_HEIGHT);
129eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        if (previousEndValue != null && previousEndValue == viewState.height) {
130eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek            return;
131eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        }
132eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        ValueAnimator previousAnimator = getChildTag(child, TAG_ANIMATOR_HEIGHT);
133eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        long newDuration = cancelAnimatorAndGetNewDuration(previousAnimator, hasNewEvents);
134eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        if (newDuration <= 0) {
135eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek            if (previousAnimator == null) {
136eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek                // no animation was running, but also no new animation should be performed,
137eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek                // lets just apply the value
138eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek                child.setActualHeight(viewState.height);
139eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek            }
140eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek            return;
141eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        }
142eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek
143eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        ValueAnimator animator = ValueAnimator.ofInt(child.getActualHeight(), viewState.height);
144eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
145572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek            @Override
146572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek            public void onAnimationUpdate(ValueAnimator animation) {
147572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek                child.setActualHeight((int) animation.getAnimatedValue());
148572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek            }
149572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek        });
150eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        animator.setInterpolator(mFastOutSlowInInterpolator);
151eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        animator.setDuration(newDuration);
152eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        animator.addListener(getGlobalAnimationFinishedListener());
153eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        // remove the tag when the animation is finished
154eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        animator.addListener(new AnimatorListenerAdapter() {
155eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek            @Override
156eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek            public void onAnimationEnd(Animator animation) {
157eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek                child.setTag(TAG_ANIMATOR_HEIGHT, null);
158eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek                child.setTag(TAG_END_HEIGHT, null);
159eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek            }
160eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        });
161eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        animator.start();
162eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        child.setTag(TAG_ANIMATOR_HEIGHT, animator);
163eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        child.setTag(TAG_END_HEIGHT, viewState.height);
164eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek    }
165eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek
166eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek    private void startAlphaAnimation(final ExpandableView child,
167eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek            final StackScrollState.ViewState viewState, boolean hasNewEvents) {
168eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        final float endAlpha = viewState.alpha;
169eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        Float previousEndValue = getChildTag(child,TAG_END_ALPHA);
170eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        if (previousEndValue != null && previousEndValue == endAlpha) {
171eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek            return;
172eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        }
173eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        ObjectAnimator previousAnimator = getChildTag(child, TAG_ANIMATOR_ALPHA);
174eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        long newDuration = cancelAnimatorAndGetNewDuration(previousAnimator, hasNewEvents);
175eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        if (newDuration <= 0) {
176eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek            if (previousAnimator == null) {
177eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek                // no animation was running, but also no new animation should be performed,
178eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek                // lets just apply the value
179eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek                child.setAlpha(endAlpha);
180eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek                if (endAlpha == 0) {
181eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek                    child.setVisibility(View.INVISIBLE);
182eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek                }
183eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek            }
184eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek            return;
185eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        }
186eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek
187eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        ObjectAnimator animator = ObjectAnimator.ofFloat(child, View.ALPHA,
188eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek                child.getAlpha(), endAlpha);
189eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        animator.setInterpolator(mFastOutSlowInInterpolator);
190eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        // Handle layer type
191eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        final int currentLayerType = child.getLayerType();
192eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        child.setLayerType(View.LAYER_TYPE_HARDWARE, null);
193eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        animator.addListener(new AnimatorListenerAdapter() {
194eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek            public boolean mWasCancelled;
195eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek
196eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek            @Override
197eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek            public void onAnimationEnd(Animator animation) {
198eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek                child.setLayerType(currentLayerType, null);
199eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek                if (endAlpha == 0 && !mWasCancelled) {
200eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek                    child.setVisibility(View.INVISIBLE);
201eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek                }
202eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek                child.setTag(TAG_ANIMATOR_ALPHA, null);
203eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek                child.setTag(TAG_END_ALPHA, null);
204eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek            }
205eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek
206eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek            @Override
207eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek            public void onAnimationCancel(Animator animation) {
208eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek                mWasCancelled = true;
209eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek            }
210eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek
211eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek            @Override
212eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek            public void onAnimationStart(Animator animation) {
213eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek                mWasCancelled = false;
214eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek            }
215eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        });
216eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        animator.addListener(getGlobalAnimationFinishedListener());
217eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        // remove the tag when the animation is finished
218eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        animator.addListener(new AnimatorListenerAdapter() {
219eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek            @Override
220eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek            public void onAnimationEnd(Animator animation) {
221eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek
222eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek            }
223eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        });
224eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        animator.start();
225eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        child.setTag(TAG_ANIMATOR_ALPHA, animator);
226eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        child.setTag(TAG_END_ALPHA, endAlpha);
227572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek    }
228572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek
22939610545f0c2714a3526bc935effe57b421542d1Selim Cinek    /**
230eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek     * @return an adapter which ensures that onAnimationFinished is called once no animation is
231eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek     *         running anymore
232eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek     */
233eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek    private AnimatorListenerAdapter getGlobalAnimationFinishedListener() {
234eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        if (!mAnimationListenerPool.empty()) {
235eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek            return mAnimationListenerPool.pop();
236eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        }
237eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek
238eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        // We need to create a new one, no reusable ones found
239eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        return new AnimatorListenerAdapter() {
240eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek            private boolean mWasCancelled;
241eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek
242eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek            @Override
243eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek            public void onAnimationEnd(Animator animation) {
244eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek                mAnimatorSet.remove(animation);
245eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek                if (mAnimatorSet.isEmpty() && !mWasCancelled) {
246eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek                    onAnimationFinished();
247eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek                }
248eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek                mAnimationListenerPool.push(this);
249eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek            }
250eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek
251eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek            @Override
252eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek            public void onAnimationCancel(Animator animation) {
253eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek                mWasCancelled = true;
254eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek            }
255eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek
256eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek            @Override
257eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek            public void onAnimationStart(Animator animation) {
258eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek                mAnimatorSet.add(animation);
259eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek                mWasCancelled = false;
260eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek            }
261eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        };
262eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek
263eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek    }
264eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek
265eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek    private void startZTranslationAnimation(final ExpandableView child,
266eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek            final StackScrollState.ViewState viewState, boolean hasNewEvents) {
267eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        Float previousEndValue = getChildTag(child,TAG_END_TRANSLATION_Z);
268eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        if (previousEndValue != null && previousEndValue == viewState.zTranslation) {
269eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek            return;
270eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        }
271eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        ObjectAnimator previousAnimator = getChildTag(child, TAG_ANIMATOR_TRANSLATION_Z);
272eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        long newDuration = cancelAnimatorAndGetNewDuration(previousAnimator, hasNewEvents);
273eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        if (newDuration <= 0) {
274eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek            if (previousAnimator == null) {
275eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek                // no animation was running, but also no new animation should be performed,
276eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek                // lets just apply the value
277eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek                child.setTranslationZ(viewState.zTranslation);
278eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek            }
279eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek            return;
280eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        }
281eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek
282eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        ObjectAnimator animator = ObjectAnimator.ofFloat(child, View.TRANSLATION_Z,
283eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek                child.getTranslationZ(), viewState.zTranslation);
284eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        animator.setInterpolator(mFastOutSlowInInterpolator);
285eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        animator.addListener(getGlobalAnimationFinishedListener());
286eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        // remove the tag when the animation is finished
287eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        animator.addListener(new AnimatorListenerAdapter() {
288eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek            @Override
289eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek            public void onAnimationEnd(Animator animation) {
290eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek                child.setTag(TAG_ANIMATOR_TRANSLATION_Z, null);
291eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek                child.setTag(TAG_END_TRANSLATION_Z, null);
292eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek            }
293eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        });
294eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        animator.start();
295eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        child.setTag(TAG_ANIMATOR_TRANSLATION_Z, animator);
296eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        child.setTag(TAG_END_TRANSLATION_Z, viewState.zTranslation);
297eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek    }
298eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek
299eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek    private void startYTranslationAnimation(final ExpandableView child,
300eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek            StackScrollState.ViewState viewState, boolean hasNewEvents) {
301eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        Float previousEndValue = getChildTag(child,TAG_END_TRANSLATION_Y);
302eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        if (previousEndValue != null && previousEndValue == viewState.yTranslation) {
303eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek            return;
304eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        }
305eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        ObjectAnimator previousAnimator = getChildTag(child, TAG_ANIMATOR_TRANSLATION_Y);
306eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        long newDuration = cancelAnimatorAndGetNewDuration(previousAnimator, hasNewEvents);
307eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        if (newDuration <= 0) {
308eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek            if (previousAnimator == null) {
309eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek                // no animation was running, but also no new animation should be performed,
310eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek                // lets just apply the value
311eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek                child.setTranslationY(viewState.yTranslation);
312eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek            }
313eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek            return;
314eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        }
315eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek
316eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        ObjectAnimator animator = ObjectAnimator.ofFloat(child, View.TRANSLATION_Y,
317eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek                child.getTranslationY(), viewState.yTranslation);
318eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        animator.setInterpolator(mFastOutSlowInInterpolator);
319eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        animator.addListener(getGlobalAnimationFinishedListener());
320eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        // remove the tag when the animation is finished
321eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        animator.addListener(new AnimatorListenerAdapter() {
322eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek            @Override
323eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek            public void onAnimationEnd(Animator animation) {
324eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek                child.setTag(TAG_ANIMATOR_TRANSLATION_Y, null);
325eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek                child.setTag(TAG_END_TRANSLATION_Y, null);
326eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek            }
327eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        });
328eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        animator.start();
329eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        child.setTag(TAG_ANIMATOR_TRANSLATION_Y, animator);
330eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        child.setTag(TAG_END_TRANSLATION_Y, viewState.yTranslation);
331eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek    }
332eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek
333eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek    private <T> T getChildTag(View child, int tag) {
334eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        return (T) child.getTag(tag);
335eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek    }
336eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek
337eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek    /**
338eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek     * Cancel the previous animator and get the duration of the new animation.
33939610545f0c2714a3526bc935effe57b421542d1Selim Cinek     *
340eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek     * @param previousAnimator the animator which was running before
341eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek     * @param hasNewEvents indicating whether new events came in in this animation
342eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek     * @return the new duration
343eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek     */
344eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek    private long cancelAnimatorAndGetNewDuration(ValueAnimator previousAnimator,
345eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek            boolean hasNewEvents) {
346eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        if (previousAnimator != null) {
347eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek            previousAnimator.cancel();
348eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek            if (!hasNewEvents) {
349eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek                // This is only an update, no new event came in. lets just take the remaining
350eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek                // duration as the new duration
351eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek                return (long) ((1.0f - previousAnimator.getAnimatedFraction()) *
352eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek                        previousAnimator.getDuration());
353eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek            }
354eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        } else if (!hasNewEvents){
355eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek            return 0;
356eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        }
357eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        return ANIMATION_DURATION;
358eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek    }
359eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek
360eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek    private void onAnimationFinished() {
361eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        mHandledEvents.clear();
362eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        mNewEvents.clear();
363eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        mHostLayout.onChildAnimationFinished();
364eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek    }
365eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek
366eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek    /**
367eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek     * Process the animationEvents for a new animation
368eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek     *
369eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek     * @param animationEvents the animation events for the animation to perform
37039610545f0c2714a3526bc935effe57b421542d1Selim Cinek     * @param finalState the final state to animate to
37139610545f0c2714a3526bc935effe57b421542d1Selim Cinek     */
372eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek    private void processAnimationEvents(
3730dd6881ea481c855976214807c17595b34a2920aJorim Jaggi            ArrayList<NotificationStackScrollLayout.AnimationEvent> animationEvents,
374572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek            StackScrollState finalState) {
375eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        mNewEvents.clear();
3760dd6881ea481c855976214807c17595b34a2920aJorim Jaggi        for (NotificationStackScrollLayout.AnimationEvent event: animationEvents) {
377572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek            View changingView = event.changingView;
378eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek            if (!mHandledEvents.contains(event)) {
379eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek                if (event.animationType == NotificationStackScrollLayout.AnimationEvent
380eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek                        .ANIMATION_TYPE_ADD) {
381eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek
382eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek                    // This item is added, initialize it's properties.
383eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek                    StackScrollState.ViewState viewState = finalState
384eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek                            .getViewStateForView(changingView);
385eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek                    if (viewState == null) {
386eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek                        // The position for this child was never generated, let's continue.
387eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek                        continue;
388eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek                    }
389eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek                    changingView.setAlpha(0);
390eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek                    changingView.setTranslationY(viewState.yTranslation);
391eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek                    changingView.setTranslationZ(viewState.zTranslation);
39239610545f0c2714a3526bc935effe57b421542d1Selim Cinek                }
393572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek                mHandledEvents.add(event);
394eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek                mNewEvents.add(event);
395572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek            }
396572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek        }
397572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek    }
398572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek}
399