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;
21572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinekimport android.animation.ValueAnimator;
220cfbef45f037efe966a5f57af63a328f6fd5d989Selim Cinekimport android.util.Property;
23572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinekimport android.view.View;
24a5703187a9dba418f742fab0874a8f683b0bbb0eSelim Cinekimport android.view.ViewGroup;
25572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinekimport android.view.animation.Interpolator;
26eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek
27c0d7058b14c24cd07912f5629c26b39b7b4673d5Winsonimport com.android.systemui.Interpolators;
28eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinekimport com.android.systemui.R;
29b5605e58cb8080c8c887b1885336b707596c8094Selim Cinekimport com.android.systemui.statusbar.ExpandableNotificationRow;
30572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinekimport com.android.systemui.statusbar.ExpandableView;
31eccb5de73cf793bd8b32d8861140fbdbfae0203fSelim Cinekimport com.android.systemui.statusbar.NotificationShelf;
32572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek
33572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinekimport java.util.ArrayList;
34eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinekimport java.util.HashSet;
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;
4328f90c78b213fb436b243126e8bc10c4f554bd1cAdrian Roos    public static final int ANIMATION_DURATION_WAKEUP = 200;
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;
47af06bf225ae0fe62f86c44d125437acaf82566cdAdrian Roos    public static final int ANIMATION_DURATION_CLOSE_REMOTE_INPUT = 150;
48a7840af09d808ecd3f7547bbdea58d4422bd4d8fSelim Cinek    public static final int ANIMATION_DURATION_HEADS_UP_APPEAR = 650;
49a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek    public static final int ANIMATION_DURATION_HEADS_UP_DISAPPEAR = 230;
508efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek    public static final int ANIMATION_DELAY_PER_ELEMENT_INTERRUPTING = 80;
518efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek    public static final int ANIMATION_DELAY_PER_ELEMENT_MANUAL = 32;
5260d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi    public static final int ANIMATION_DELAY_PER_ELEMENT_GO_TO_FULL_SHADE = 48;
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
5660d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi    private final int mGoToFullShadeAppearingTranslation;
57bbcebde5f47d070d82270a367bec2f375c55d359Selim Cinek    private final ExpandableViewState mTmpState = new ExpandableViewState();
580cfbef45f037efe966a5f57af63a328f6fd5d989Selim Cinek    private final AnimationProperties mAnimationProperties;
59572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek    public NotificationStackScrollLayout mHostLayout;
60eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek    private ArrayList<NotificationStackScrollLayout.AnimationEvent> mNewEvents =
61eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek            new ArrayList<>();
628efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek    private ArrayList<View> mNewAddChildren = new ArrayList<>();
63a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek    private HashSet<View> mHeadsUpAppearChildren = new HashSet<>();
64a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek    private HashSet<View> mHeadsUpDisappearChildren = new HashSet<>();
65a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek    private HashSet<Animator> mAnimatorSet = new HashSet<>();
6660d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi    private Stack<AnimatorListenerAdapter> mAnimationListenerPool = new Stack<>();
67d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi    private AnimationFilter mAnimationFilter = new AnimationFilter();
685aa045cc6bca84f5c11f1a99999546ba5e5949a5Jorim Jaggi    private long mCurrentLength;
69dbc3dce886cb7abba23a9f18e60d0f5af5c93226Jorim Jaggi    private long mCurrentAdditionalDelay;
70572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek
7160d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi    /** The current index for the last child which was not added in this event set. */
7260d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi    private int mCurrentLastNotAddedIndex;
738d9ff9c2c66bc1d3b92eb6992d58599ff80ed6dcSelim Cinek    private ValueAnimator mTopOverScrollAnimator;
748d9ff9c2c66bc1d3b92eb6992d58599ff80ed6dcSelim Cinek    private ValueAnimator mBottomOverScrollAnimator;
75a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek    private int mHeadsUpAppearHeightBottom;
76a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek    private boolean mShadeExpanded;
778f93763cf0f4b3c41f6b0ecac09b621eb0ca328bSelim Cinek    private ArrayList<View> mChildrenToClearFromOverlay = new ArrayList<>();
78eccb5de73cf793bd8b32d8861140fbdbfae0203fSelim Cinek    private NotificationShelf mShelf;
798d9ff9c2c66bc1d3b92eb6992d58599ff80ed6dcSelim Cinek
80572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek    public StackStateAnimator(NotificationStackScrollLayout hostLayout) {
81572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek        mHostLayout = hostLayout;
8260d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi        mGoToFullShadeAppearingTranslation =
8360d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi                hostLayout.getContext().getResources().getDimensionPixelSize(
8460d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi                        R.dimen.go_to_full_shade_appearing_translation);
850cfbef45f037efe966a5f57af63a328f6fd5d989Selim Cinek        mAnimationProperties = new AnimationProperties() {
860cfbef45f037efe966a5f57af63a328f6fd5d989Selim Cinek            @Override
870cfbef45f037efe966a5f57af63a328f6fd5d989Selim Cinek            public AnimationFilter getAnimationFilter() {
880cfbef45f037efe966a5f57af63a328f6fd5d989Selim Cinek                return mAnimationFilter;
890cfbef45f037efe966a5f57af63a328f6fd5d989Selim Cinek            }
900cfbef45f037efe966a5f57af63a328f6fd5d989Selim Cinek
910cfbef45f037efe966a5f57af63a328f6fd5d989Selim Cinek            @Override
920cfbef45f037efe966a5f57af63a328f6fd5d989Selim Cinek            public AnimatorListenerAdapter getAnimationFinishListener() {
930cfbef45f037efe966a5f57af63a328f6fd5d989Selim Cinek                return getGlobalAnimationFinishedListener();
940cfbef45f037efe966a5f57af63a328f6fd5d989Selim Cinek            }
950cfbef45f037efe966a5f57af63a328f6fd5d989Selim Cinek
960cfbef45f037efe966a5f57af63a328f6fd5d989Selim Cinek            @Override
970cfbef45f037efe966a5f57af63a328f6fd5d989Selim Cinek            public boolean wasAdded(View view) {
980cfbef45f037efe966a5f57af63a328f6fd5d989Selim Cinek                return mNewAddChildren.contains(view);
990cfbef45f037efe966a5f57af63a328f6fd5d989Selim Cinek            }
1000cfbef45f037efe966a5f57af63a328f6fd5d989Selim Cinek
1010cfbef45f037efe966a5f57af63a328f6fd5d989Selim Cinek            @Override
1020cfbef45f037efe966a5f57af63a328f6fd5d989Selim Cinek            public Interpolator getCustomInterpolator(View child, Property property) {
1030cfbef45f037efe966a5f57af63a328f6fd5d989Selim Cinek                if (mHeadsUpAppearChildren.contains(child) && View.TRANSLATION_Y.equals(property)) {
104061d90729bba582395e9050898dab20fd434cf66Selim Cinek                    return Interpolators.HEADS_UP_APPEAR;
1050cfbef45f037efe966a5f57af63a328f6fd5d989Selim Cinek                }
1060cfbef45f037efe966a5f57af63a328f6fd5d989Selim Cinek                return null;
1070cfbef45f037efe966a5f57af63a328f6fd5d989Selim Cinek            }
1080cfbef45f037efe966a5f57af63a328f6fd5d989Selim Cinek        };
109572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek    }
110572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek
111572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek    public boolean isRunning() {
112eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        return !mAnimatorSet.isEmpty();
113572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek    }
114572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek
115572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek    public void startAnimationForEvents(
1160dd6881ea481c855976214807c17595b34a2920aJorim Jaggi            ArrayList<NotificationStackScrollLayout.AnimationEvent> mAnimationEvents,
117dbc3dce886cb7abba23a9f18e60d0f5af5c93226Jorim Jaggi            StackScrollState finalState, long additionalDelay) {
118eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek
119eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        processAnimationEvents(mAnimationEvents, finalState);
120eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek
121572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek        int childCount = mHostLayout.getChildCount();
122d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi        mAnimationFilter.applyCombination(mNewEvents);
123dbc3dce886cb7abba23a9f18e60d0f5af5c93226Jorim Jaggi        mCurrentAdditionalDelay = additionalDelay;
1245aa045cc6bca84f5c11f1a99999546ba5e5949a5Jorim Jaggi        mCurrentLength = NotificationStackScrollLayout.AnimationEvent.combineLength(mNewEvents);
12560d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi        mCurrentLastNotAddedIndex = findLastNotAddedIndex(finalState);
126572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek        for (int i = 0; i < childCount; i++) {
127572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek            final ExpandableView child = (ExpandableView) mHostLayout.getChildAt(i);
1288efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek
129bbcebde5f47d070d82270a367bec2f375c55d359Selim Cinek            ExpandableViewState viewState = finalState.getViewStateForView(child);
130a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek            if (viewState == null || child.getVisibility() == View.GONE
131a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek                    || applyWithoutAnimation(child, viewState, finalState)) {
132572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek                continue;
133572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek            }
134572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek
1350cfbef45f037efe966a5f57af63a328f6fd5d989Selim Cinek            initAnimationProperties(finalState, child, viewState);
1360cfbef45f037efe966a5f57af63a328f6fd5d989Selim Cinek            viewState.animateTo(child, mAnimationProperties);
137572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek        }
138eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        if (!isRunning()) {
139eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek            // no child has preformed any animation, lets finish
140eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek            onAnimationFinished();
141eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        }
142a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek        mHeadsUpAppearChildren.clear();
143a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek        mHeadsUpDisappearChildren.clear();
1448efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek        mNewEvents.clear();
1458efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek        mNewAddChildren.clear();
146572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek    }
147572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek
1480cfbef45f037efe966a5f57af63a328f6fd5d989Selim Cinek    private void initAnimationProperties(StackScrollState finalState, ExpandableView child,
1490cfbef45f037efe966a5f57af63a328f6fd5d989Selim Cinek            ExpandableViewState viewState) {
1500cfbef45f037efe966a5f57af63a328f6fd5d989Selim Cinek        boolean wasAdded = mAnimationProperties.wasAdded(child);
1510cfbef45f037efe966a5f57af63a328f6fd5d989Selim Cinek        mAnimationProperties.duration = mCurrentLength;
1520cfbef45f037efe966a5f57af63a328f6fd5d989Selim Cinek        adaptDurationWhenGoingToFullShade(child, viewState, wasAdded);
1530cfbef45f037efe966a5f57af63a328f6fd5d989Selim Cinek        mAnimationProperties.delay = 0;
1540cfbef45f037efe966a5f57af63a328f6fd5d989Selim Cinek        if (wasAdded || mAnimationFilter.hasDelays
1550cfbef45f037efe966a5f57af63a328f6fd5d989Selim Cinek                        && (viewState.yTranslation != child.getTranslationY()
1560cfbef45f037efe966a5f57af63a328f6fd5d989Selim Cinek                        || viewState.zTranslation != child.getTranslationZ()
1570cfbef45f037efe966a5f57af63a328f6fd5d989Selim Cinek                        || viewState.alpha != child.getAlpha()
1580cfbef45f037efe966a5f57af63a328f6fd5d989Selim Cinek                        || viewState.height != child.getActualHeight()
1590cfbef45f037efe966a5f57af63a328f6fd5d989Selim Cinek                        || viewState.clipTopAmount != child.getClipTopAmount()
1600cfbef45f037efe966a5f57af63a328f6fd5d989Selim Cinek                        || viewState.dark != child.isDark()
1610cfbef45f037efe966a5f57af63a328f6fd5d989Selim Cinek                        || viewState.shadowAlpha != child.getShadowAlpha())) {
1620cfbef45f037efe966a5f57af63a328f6fd5d989Selim Cinek            mAnimationProperties.delay = mCurrentAdditionalDelay
1630cfbef45f037efe966a5f57af63a328f6fd5d989Selim Cinek                    + calculateChildAnimationDelay(viewState, finalState);
1640cfbef45f037efe966a5f57af63a328f6fd5d989Selim Cinek        }
1650cfbef45f037efe966a5f57af63a328f6fd5d989Selim Cinek    }
1660cfbef45f037efe966a5f57af63a328f6fd5d989Selim Cinek
1670cfbef45f037efe966a5f57af63a328f6fd5d989Selim Cinek    private void adaptDurationWhenGoingToFullShade(ExpandableView child,
1680cfbef45f037efe966a5f57af63a328f6fd5d989Selim Cinek            ExpandableViewState viewState, boolean wasAdded) {
1690cfbef45f037efe966a5f57af63a328f6fd5d989Selim Cinek        if (wasAdded && mAnimationFilter.hasGoToFullShadeEvent) {
1700cfbef45f037efe966a5f57af63a328f6fd5d989Selim Cinek            child.setTranslationY(child.getTranslationY() + mGoToFullShadeAppearingTranslation);
1710cfbef45f037efe966a5f57af63a328f6fd5d989Selim Cinek            float longerDurationFactor = viewState.notGoneIndex - mCurrentLastNotAddedIndex;
1720cfbef45f037efe966a5f57af63a328f6fd5d989Selim Cinek            longerDurationFactor = (float) Math.pow(longerDurationFactor, 0.7f);
1730cfbef45f037efe966a5f57af63a328f6fd5d989Selim Cinek            mAnimationProperties.duration = ANIMATION_DURATION_APPEAR_DISAPPEAR + 50 +
1740cfbef45f037efe966a5f57af63a328f6fd5d989Selim Cinek                    (long) (100 * longerDurationFactor);
1750cfbef45f037efe966a5f57af63a328f6fd5d989Selim Cinek        }
1760cfbef45f037efe966a5f57af63a328f6fd5d989Selim Cinek    }
1770cfbef45f037efe966a5f57af63a328f6fd5d989Selim Cinek
178a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek    /**
179a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek     * Determines if a view should not perform an animation and applies it directly.
180a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek     *
181a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek     * @return true if no animation should be performed
182a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek     */
183bbcebde5f47d070d82270a367bec2f375c55d359Selim Cinek    private boolean applyWithoutAnimation(ExpandableView child, ExpandableViewState viewState,
184a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek            StackScrollState finalState) {
185a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek        if (mShadeExpanded) {
186a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek            return false;
187a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek        }
1880cfbef45f037efe966a5f57af63a328f6fd5d989Selim Cinek        if (ViewState.isAnimatingY(child)) {
189a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek            // A Y translation animation is running
190a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek            return false;
191a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek        }
192a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek        if (mHeadsUpDisappearChildren.contains(child) || mHeadsUpAppearChildren.contains(child)) {
193a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek            // This is a heads up animation
194a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek            return false;
195a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek        }
196131c1e2960fa5bdf54bfb6fcd5ac98c9f728f796Selim Cinek        if (NotificationStackScrollLayout.isPinnedHeadsUp(child)) {
1971f3f544f21cab3728f749ab66cd6859e9dfcf389Selim Cinek            // This is another headsUp which might move. Let's animate!
1981f3f544f21cab3728f749ab66cd6859e9dfcf389Selim Cinek            return false;
1991f3f544f21cab3728f749ab66cd6859e9dfcf389Selim Cinek        }
200bbcebde5f47d070d82270a367bec2f375c55d359Selim Cinek        viewState.applyToView(child);
201a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek        return true;
202a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek    }
203a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek
20460d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi    private int findLastNotAddedIndex(StackScrollState finalState) {
20560d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi        int childCount = mHostLayout.getChildCount();
20660d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi        for (int i = childCount - 1; i >= 0; i--) {
20760d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi            final ExpandableView child = (ExpandableView) mHostLayout.getChildAt(i);
20860d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi
209bbcebde5f47d070d82270a367bec2f375c55d359Selim Cinek            ExpandableViewState viewState = finalState.getViewStateForView(child);
21060d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi            if (viewState == null || child.getVisibility() == View.GONE) {
21160d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi                continue;
21260d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi            }
21360d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi            if (!mNewAddChildren.contains(child)) {
21460d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi                return viewState.notGoneIndex;
21560d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi            }
21660d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi        }
21760d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi        return -1;
21860d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi    }
21960d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi
220bbcebde5f47d070d82270a367bec2f375c55d359Selim Cinek    private long calculateChildAnimationDelay(ExpandableViewState viewState,
2218efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek            StackScrollState finalState) {
22260d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi        if (mAnimationFilter.hasGoToFullShadeEvent) {
22360d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi            return calculateDelayGoToFullShade(viewState);
22460d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi        }
2255eb67c22489f5fda22770d1fc824e7a8013ec025Jorim Jaggi        if (mAnimationFilter.hasHeadsUpDisappearClickEvent) {
2265eb67c22489f5fda22770d1fc824e7a8013ec025Jorim Jaggi            return ANIMATION_DELAY_HEADS_UP;
2275eb67c22489f5fda22770d1fc824e7a8013ec025Jorim Jaggi        }
2288efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek        long minDelay = 0;
2298efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek        for (NotificationStackScrollLayout.AnimationEvent event : mNewEvents) {
2308efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek            long delayPerElement = ANIMATION_DELAY_PER_ELEMENT_INTERRUPTING;
2318efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek            switch (event.animationType) {
2328efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek                case NotificationStackScrollLayout.AnimationEvent.ANIMATION_TYPE_ADD: {
2338efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek                    int ownIndex = viewState.notGoneIndex;
2348efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek                    int changingIndex = finalState
2358efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek                            .getViewStateForView(event.changingView).notGoneIndex;
2368efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek                    int difference = Math.abs(ownIndex - changingIndex);
2378efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek                    difference = Math.max(0, Math.min(DELAY_EFFECT_MAX_INDEX_DIFFERENCE,
2388efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek                            difference - 1));
2398efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek                    long delay = (DELAY_EFFECT_MAX_INDEX_DIFFERENCE - difference) * delayPerElement;
2408efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek                    minDelay = Math.max(delay, minDelay);
2418efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek                    break;
2428efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek                }
2438efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek                case NotificationStackScrollLayout.AnimationEvent.ANIMATION_TYPE_REMOVE_SWIPED_OUT:
2448efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek                    delayPerElement = ANIMATION_DELAY_PER_ELEMENT_MANUAL;
2458efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek                case NotificationStackScrollLayout.AnimationEvent.ANIMATION_TYPE_REMOVE: {
2468efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek                    int ownIndex = viewState.notGoneIndex;
2478efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek                    boolean noNextView = event.viewAfterChangingView == null;
2488efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek                    View viewAfterChangingView = noNextView
2498efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek                            ? mHostLayout.getLastChildNotGone()
2508efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek                            : event.viewAfterChangingView;
2510c87f87008a5bbfc00dd82b27cad196131485429Selim Cinek                    if (viewAfterChangingView == null) {
2520c87f87008a5bbfc00dd82b27cad196131485429Selim Cinek                        // This can happen when the last view in the list is removed.
2530c87f87008a5bbfc00dd82b27cad196131485429Selim Cinek                        // Since the shelf is still around and the only view, the code still goes
2540c87f87008a5bbfc00dd82b27cad196131485429Selim Cinek                        // in here and tries to calculate the delay for it when case its properties
2550c87f87008a5bbfc00dd82b27cad196131485429Selim Cinek                        // have changed.
2560c87f87008a5bbfc00dd82b27cad196131485429Selim Cinek                        continue;
2570c87f87008a5bbfc00dd82b27cad196131485429Selim Cinek                    }
2588efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek                    int nextIndex = finalState
2598efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek                            .getViewStateForView(viewAfterChangingView).notGoneIndex;
2608efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek                    if (ownIndex >= nextIndex) {
2618efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek                        // we only have the view afterwards
2628efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek                        ownIndex++;
2638efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek                    }
2648efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek                    int difference = Math.abs(ownIndex - nextIndex);
2658efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek                    difference = Math.max(0, Math.min(DELAY_EFFECT_MAX_INDEX_DIFFERENCE,
2668efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek                            difference - 1));
2678efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek                    long delay = difference * delayPerElement;
2688efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek                    minDelay = Math.max(delay, minDelay);
2698efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek                    break;
2708efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek                }
2718efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek                default:
2728efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek                    break;
2738efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek            }
2748efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek        }
2758efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek        return minDelay;
276572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek    }
277572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek
278bbcebde5f47d070d82270a367bec2f375c55d359Selim Cinek    private long calculateDelayGoToFullShade(ExpandableViewState viewState) {
279eccb5de73cf793bd8b32d8861140fbdbfae0203fSelim Cinek        int shelfIndex = mShelf.getNotGoneIndex();
28060d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi        float index = viewState.notGoneIndex;
281eccb5de73cf793bd8b32d8861140fbdbfae0203fSelim Cinek        long result = 0;
282eccb5de73cf793bd8b32d8861140fbdbfae0203fSelim Cinek        if (index > shelfIndex) {
283eccb5de73cf793bd8b32d8861140fbdbfae0203fSelim Cinek            float diff = index - shelfIndex;
284eccb5de73cf793bd8b32d8861140fbdbfae0203fSelim Cinek            diff = (float) Math.pow(diff, 0.7f);
285eccb5de73cf793bd8b32d8861140fbdbfae0203fSelim Cinek            result += (long) (diff * ANIMATION_DELAY_PER_ELEMENT_GO_TO_FULL_SHADE * 0.25);
286eccb5de73cf793bd8b32d8861140fbdbfae0203fSelim Cinek            index = shelfIndex;
287eccb5de73cf793bd8b32d8861140fbdbfae0203fSelim Cinek        }
28860d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi        index = (float) Math.pow(index, 0.7f);
289eccb5de73cf793bd8b32d8861140fbdbfae0203fSelim Cinek        result += (long) (index * ANIMATION_DELAY_PER_ELEMENT_GO_TO_FULL_SHADE);
290eccb5de73cf793bd8b32d8861140fbdbfae0203fSelim Cinek        return result;
29160d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi    }
29260d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi
2933af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek    /**
2943af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek     * @return an adapter which ensures that onAnimationFinished is called once no animation is
2953af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek     *         running anymore
2963af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek     */
2973af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek    private AnimatorListenerAdapter getGlobalAnimationFinishedListener() {
2983af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek        if (!mAnimationListenerPool.empty()) {
2993af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek            return mAnimationListenerPool.pop();
3003af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek        }
3013af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek
3023af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek        // We need to create a new one, no reusable ones found
3033af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek        return new AnimatorListenerAdapter() {
3043af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek            private boolean mWasCancelled;
3053af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek
3063af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek            @Override
3073af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek            public void onAnimationEnd(Animator animation) {
3083af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek                mAnimatorSet.remove(animation);
3093af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek                if (mAnimatorSet.isEmpty() && !mWasCancelled) {
3103af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek                    onAnimationFinished();
3113af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek                }
3123af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek                mAnimationListenerPool.push(this);
3133af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek            }
3143af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek
3153af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek            @Override
3163af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek            public void onAnimationCancel(Animator animation) {
3173af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek                mWasCancelled = true;
3183af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek            }
3193af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek
3203af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek            @Override
3213af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek            public void onAnimationStart(Animator animation) {
3223af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek                mWasCancelled = false;
3230cfbef45f037efe966a5f57af63a328f6fd5d989Selim Cinek                mAnimatorSet.add(animation);
3243af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek            }
3253af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek        };
3263af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek    }
3273af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek
328eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek    private void onAnimationFinished() {
329eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek        mHostLayout.onChildAnimationFinished();
3308f93763cf0f4b3c41f6b0ecac09b621eb0ca328bSelim Cinek        for (View v : mChildrenToClearFromOverlay) {
331a5703187a9dba418f742fab0874a8f683b0bbb0eSelim Cinek            removeFromOverlay(v);
3328f93763cf0f4b3c41f6b0ecac09b621eb0ca328bSelim Cinek        }
3338f93763cf0f4b3c41f6b0ecac09b621eb0ca328bSelim Cinek        mChildrenToClearFromOverlay.clear();
334eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek    }
335eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek
336eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek    /**
337eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek     * Process the animationEvents for a new animation
338eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek     *
339eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek     * @param animationEvents the animation events for the animation to perform
34039610545f0c2714a3526bc935effe57b421542d1Selim Cinek     * @param finalState the final state to animate to
34139610545f0c2714a3526bc935effe57b421542d1Selim Cinek     */
342eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek    private void processAnimationEvents(
3430dd6881ea481c855976214807c17595b34a2920aJorim Jaggi            ArrayList<NotificationStackScrollLayout.AnimationEvent> animationEvents,
344572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek            StackScrollState finalState) {
3458d9ff9c2c66bc1d3b92eb6992d58599ff80ed6dcSelim Cinek        for (NotificationStackScrollLayout.AnimationEvent event : animationEvents) {
3468efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek            final ExpandableView changingView = (ExpandableView) event.changingView;
3478efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek            if (event.animationType ==
3488efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek                    NotificationStackScrollLayout.AnimationEvent.ANIMATION_TYPE_ADD) {
3498efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek
3508efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek                // This item is added, initialize it's properties.
351bbcebde5f47d070d82270a367bec2f375c55d359Selim Cinek                ExpandableViewState viewState = finalState
3528efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek                        .getViewStateForView(changingView);
3538efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek                if (viewState == null) {
3548efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek                    // The position for this child was never generated, let's continue.
3558efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek                    continue;
3568efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek                }
357bbcebde5f47d070d82270a367bec2f375c55d359Selim Cinek                viewState.applyToView(changingView);
3588efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek                mNewAddChildren.add(changingView);
3598efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek
3608efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek            } else if (event.animationType ==
3618efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek                    NotificationStackScrollLayout.AnimationEvent.ANIMATION_TYPE_REMOVE) {
3625b5beb01dc65c210b27bbb770cd0d7e052d8c743Selim Cinek                if (changingView.getVisibility() != View.VISIBLE) {
363a5703187a9dba418f742fab0874a8f683b0bbb0eSelim Cinek                    removeFromOverlay(changingView);
3648efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek                    continue;
3658efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek                }
3668efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek
3678efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek                // Find the amount to translate up. This is needed in order to understand the
3688efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek                // direction of the remove animation (either downwards or upwards)
369bbcebde5f47d070d82270a367bec2f375c55d359Selim Cinek                ExpandableViewState viewState = finalState
3708efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek                        .getViewStateForView(event.viewAfterChangingView);
3718efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek                int actualHeight = changingView.getActualHeight();
3728efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek                // upwards by default
3738efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek                float translationDirection = -1.0f;
3748efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek                if (viewState != null) {
375ef8c2257045f73f0576c90c05c715c5f31ef81d8Selim Cinek                    float ownPosition = changingView.getTranslationY();
376ef8c2257045f73f0576c90c05c715c5f31ef81d8Selim Cinek                    if (changingView instanceof ExpandableNotificationRow
377ef8c2257045f73f0576c90c05c715c5f31ef81d8Selim Cinek                            && event.viewAfterChangingView instanceof ExpandableNotificationRow) {
378ef8c2257045f73f0576c90c05c715c5f31ef81d8Selim Cinek                        ExpandableNotificationRow changingRow =
379ef8c2257045f73f0576c90c05c715c5f31ef81d8Selim Cinek                                (ExpandableNotificationRow) changingView;
380ef8c2257045f73f0576c90c05c715c5f31ef81d8Selim Cinek                        ExpandableNotificationRow nextRow =
381ef8c2257045f73f0576c90c05c715c5f31ef81d8Selim Cinek                                (ExpandableNotificationRow) event.viewAfterChangingView;
382ef8c2257045f73f0576c90c05c715c5f31ef81d8Selim Cinek                        if (changingRow.isRemoved()
383ef8c2257045f73f0576c90c05c715c5f31ef81d8Selim Cinek                                && changingRow.wasChildInGroupWhenRemoved()
384ef8c2257045f73f0576c90c05c715c5f31ef81d8Selim Cinek                                && !nextRow.isChildInGroup()) {
385ef8c2257045f73f0576c90c05c715c5f31ef81d8Selim Cinek                            // the next row isn't actually a child from a group! Let's
386ef8c2257045f73f0576c90c05c715c5f31ef81d8Selim Cinek                            // compare absolute positions!
387ef8c2257045f73f0576c90c05c715c5f31ef81d8Selim Cinek                            ownPosition = changingRow.getTranslationWhenRemoved();
388ef8c2257045f73f0576c90c05c715c5f31ef81d8Selim Cinek                        }
389ef8c2257045f73f0576c90c05c715c5f31ef81d8Selim Cinek                    }
3908efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek                    // there was a view after this one, Approximate the distance the next child
3918efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek                    // travelled
3928efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek                    translationDirection = ((viewState.yTranslation
393ef8c2257045f73f0576c90c05c715c5f31ef81d8Selim Cinek                            - (ownPosition + actualHeight / 2.0f)) * 2 /
3948efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek                            actualHeight);
3958efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek                    translationDirection = Math.max(Math.min(translationDirection, 1.0f),-1.0f);
3968efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek
3978efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek                }
39860d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi                changingView.performRemoveAnimation(ANIMATION_DURATION_APPEAR_DISAPPEAR,
39960d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi                        translationDirection, new Runnable() {
4008efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek                    @Override
4018efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek                    public void run() {
4028efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek                        // remove the temporary overlay
403a5703187a9dba418f742fab0874a8f683b0bbb0eSelim Cinek                        removeFromOverlay(changingView);
4048efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek                    }
4058efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek                });
406b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek            } else if (event.animationType ==
407f336f4c13ad3be15e2b44266cd786685975425f2Selim Cinek                NotificationStackScrollLayout.AnimationEvent.ANIMATION_TYPE_REMOVE_SWIPED_OUT) {
408f336f4c13ad3be15e2b44266cd786685975425f2Selim Cinek                // A race condition can trigger the view to be added to the overlay even though
409d13956475e97da9de83f6519eab514770118e7afSelim Cinek                // it was fully swiped out. So let's remove it
410f336f4c13ad3be15e2b44266cd786685975425f2Selim Cinek                mHostLayout.getOverlay().remove(changingView);
411d13956475e97da9de83f6519eab514770118e7afSelim Cinek                if (Math.abs(changingView.getTranslation()) == changingView.getWidth()
412d13956475e97da9de83f6519eab514770118e7afSelim Cinek                        && changingView.getTransientContainer() != null) {
413d13956475e97da9de83f6519eab514770118e7afSelim Cinek                    changingView.getTransientContainer().removeTransientView(changingView);
414d13956475e97da9de83f6519eab514770118e7afSelim Cinek                }
415b5605e58cb8080c8c887b1885336b707596c8094Selim Cinek            } else if (event.animationType == NotificationStackScrollLayout
416b5605e58cb8080c8c887b1885336b707596c8094Selim Cinek                    .AnimationEvent.ANIMATION_TYPE_GROUP_EXPANSION_CHANGED) {
417b5605e58cb8080c8c887b1885336b707596c8094Selim Cinek                ExpandableNotificationRow row = (ExpandableNotificationRow) event.changingView;
418b5605e58cb8080c8c887b1885336b707596c8094Selim Cinek                row.prepareExpansionChanged(finalState);
419b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek            } else if (event.animationType == NotificationStackScrollLayout
420b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek                    .AnimationEvent.ANIMATION_TYPE_HEADS_UP_APPEAR) {
421b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek                // This item is added, initialize it's properties.
422bbcebde5f47d070d82270a367bec2f375c55d359Selim Cinek                ExpandableViewState viewState = finalState.getViewStateForView(changingView);
423b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek                mTmpState.copyFrom(viewState);
424a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek                if (event.headsUpFromBottom) {
425a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek                    mTmpState.yTranslation = mHeadsUpAppearHeightBottom;
426a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek                } else {
427a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek                    mTmpState.yTranslation = -mTmpState.height;
428a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek                }
429a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek                mHeadsUpAppearChildren.add(changingView);
430bbcebde5f47d070d82270a367bec2f375c55d359Selim Cinek                mTmpState.applyToView(changingView);
431a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek            } else if (event.animationType == NotificationStackScrollLayout
4325eb67c22489f5fda22770d1fc824e7a8013ec025Jorim Jaggi                            .AnimationEvent.ANIMATION_TYPE_HEADS_UP_DISAPPEAR ||
4335eb67c22489f5fda22770d1fc824e7a8013ec025Jorim Jaggi                    event.animationType == NotificationStackScrollLayout
4345eb67c22489f5fda22770d1fc824e7a8013ec025Jorim Jaggi                            .AnimationEvent.ANIMATION_TYPE_HEADS_UP_DISAPPEAR_CLICK) {
435a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek                mHeadsUpDisappearChildren.add(changingView);
436ef5127ea5f34f7a4c961021f6b691174bcb81d2eSelim Cinek                if (changingView.getParent() == null) {
4378f93763cf0f4b3c41f6b0ecac09b621eb0ca328bSelim Cinek                    // This notification was actually removed, so we need to add it to the overlay
4388f93763cf0f4b3c41f6b0ecac09b621eb0ca328bSelim Cinek                    mHostLayout.getOverlay().add(changingView);
439eaee9c01902ecfc253be98d68e5d7b586ed54463Selim Cinek                    mTmpState.initFrom(changingView);
440eaee9c01902ecfc253be98d68e5d7b586ed54463Selim Cinek                    mTmpState.yTranslation = -changingView.getActualHeight();
4418f93763cf0f4b3c41f6b0ecac09b621eb0ca328bSelim Cinek                    // We temporarily enable Y animations, the real filter will be combined
4428f93763cf0f4b3c41f6b0ecac09b621eb0ca328bSelim Cinek                    // afterwards anyway
4438f93763cf0f4b3c41f6b0ecac09b621eb0ca328bSelim Cinek                    mAnimationFilter.animateY = true;
4440cfbef45f037efe966a5f57af63a328f6fd5d989Selim Cinek                    mAnimationProperties.delay =
4455eb67c22489f5fda22770d1fc824e7a8013ec025Jorim Jaggi                            event.animationType == NotificationStackScrollLayout
4465eb67c22489f5fda22770d1fc824e7a8013ec025Jorim Jaggi                                    .AnimationEvent.ANIMATION_TYPE_HEADS_UP_DISAPPEAR_CLICK
4470cfbef45f037efe966a5f57af63a328f6fd5d989Selim Cinek                            ? ANIMATION_DELAY_HEADS_UP
4480cfbef45f037efe966a5f57af63a328f6fd5d989Selim Cinek                            : 0;
4490cfbef45f037efe966a5f57af63a328f6fd5d989Selim Cinek                    mAnimationProperties.duration = ANIMATION_DURATION_HEADS_UP_DISAPPEAR;
4500cfbef45f037efe966a5f57af63a328f6fd5d989Selim Cinek                    mTmpState.animateTo(changingView, mAnimationProperties);
4518f93763cf0f4b3c41f6b0ecac09b621eb0ca328bSelim Cinek                    mChildrenToClearFromOverlay.add(changingView);
4528f93763cf0f4b3c41f6b0ecac09b621eb0ca328bSelim Cinek                }
453572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek            }
4548efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek            mNewEvents.add(event);
455572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek        }
456572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek    }
4578d9ff9c2c66bc1d3b92eb6992d58599ff80ed6dcSelim Cinek
458a5703187a9dba418f742fab0874a8f683b0bbb0eSelim Cinek    public static void removeFromOverlay(View changingView) {
459a5703187a9dba418f742fab0874a8f683b0bbb0eSelim Cinek        ViewGroup parent = (ViewGroup) changingView.getParent();
460a5703187a9dba418f742fab0874a8f683b0bbb0eSelim Cinek        if (parent != null) {
461a5703187a9dba418f742fab0874a8f683b0bbb0eSelim Cinek            parent.removeView(changingView);
462a5703187a9dba418f742fab0874a8f683b0bbb0eSelim Cinek        }
463a5703187a9dba418f742fab0874a8f683b0bbb0eSelim Cinek    }
464a5703187a9dba418f742fab0874a8f683b0bbb0eSelim Cinek
465475b21dfe517ec04f435f6b02f4a53083d040db4Jorim Jaggi    public void animateOverScrollToAmount(float targetAmount, final boolean onTop,
466475b21dfe517ec04f435f6b02f4a53083d040db4Jorim Jaggi            final boolean isRubberbanded) {
4678d9ff9c2c66bc1d3b92eb6992d58599ff80ed6dcSelim Cinek        final float startOverScrollAmount = mHostLayout.getCurrentOverScrollAmount(onTop);
4689012958742c7a66b37ba5f2196f9086bb1980e6bJorim Jaggi        if (targetAmount == startOverScrollAmount) {
4699012958742c7a66b37ba5f2196f9086bb1980e6bJorim Jaggi            return;
4709012958742c7a66b37ba5f2196f9086bb1980e6bJorim Jaggi        }
4718d9ff9c2c66bc1d3b92eb6992d58599ff80ed6dcSelim Cinek        cancelOverScrollAnimators(onTop);
4728d9ff9c2c66bc1d3b92eb6992d58599ff80ed6dcSelim Cinek        ValueAnimator overScrollAnimator = ValueAnimator.ofFloat(startOverScrollAmount,
4738d9ff9c2c66bc1d3b92eb6992d58599ff80ed6dcSelim Cinek                targetAmount);
4748d9ff9c2c66bc1d3b92eb6992d58599ff80ed6dcSelim Cinek        overScrollAnimator.setDuration(ANIMATION_DURATION_STANDARD);
4758d9ff9c2c66bc1d3b92eb6992d58599ff80ed6dcSelim Cinek        overScrollAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
4768d9ff9c2c66bc1d3b92eb6992d58599ff80ed6dcSelim Cinek            @Override
4778d9ff9c2c66bc1d3b92eb6992d58599ff80ed6dcSelim Cinek            public void onAnimationUpdate(ValueAnimator animation) {
4788d9ff9c2c66bc1d3b92eb6992d58599ff80ed6dcSelim Cinek                float currentOverScroll = (float) animation.getAnimatedValue();
47947c85a3525dcd0bbd3168632830e8ab491d18462Jorim Jaggi                mHostLayout.setOverScrollAmount(
480475b21dfe517ec04f435f6b02f4a53083d040db4Jorim Jaggi                        currentOverScroll, onTop, false /* animate */, false /* cancelAnimators */,
481475b21dfe517ec04f435f6b02f4a53083d040db4Jorim Jaggi                        isRubberbanded);
4828d9ff9c2c66bc1d3b92eb6992d58599ff80ed6dcSelim Cinek            }
4838d9ff9c2c66bc1d3b92eb6992d58599ff80ed6dcSelim Cinek        });
484c18010f6720f606003cde3cd376ddacaca30f6e5Selim Cinek        overScrollAnimator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
48547c85a3525dcd0bbd3168632830e8ab491d18462Jorim Jaggi        overScrollAnimator.addListener(new AnimatorListenerAdapter() {
48647c85a3525dcd0bbd3168632830e8ab491d18462Jorim Jaggi            @Override
48747c85a3525dcd0bbd3168632830e8ab491d18462Jorim Jaggi            public void onAnimationEnd(Animator animation) {
48847c85a3525dcd0bbd3168632830e8ab491d18462Jorim Jaggi                if (onTop) {
48947c85a3525dcd0bbd3168632830e8ab491d18462Jorim Jaggi                    mTopOverScrollAnimator = null;
49047c85a3525dcd0bbd3168632830e8ab491d18462Jorim Jaggi                } else {
49147c85a3525dcd0bbd3168632830e8ab491d18462Jorim Jaggi                    mBottomOverScrollAnimator = null;
49247c85a3525dcd0bbd3168632830e8ab491d18462Jorim Jaggi                }
49347c85a3525dcd0bbd3168632830e8ab491d18462Jorim Jaggi            }
49447c85a3525dcd0bbd3168632830e8ab491d18462Jorim Jaggi        });
4958d9ff9c2c66bc1d3b92eb6992d58599ff80ed6dcSelim Cinek        overScrollAnimator.start();
4968d9ff9c2c66bc1d3b92eb6992d58599ff80ed6dcSelim Cinek        if (onTop) {
4978d9ff9c2c66bc1d3b92eb6992d58599ff80ed6dcSelim Cinek            mTopOverScrollAnimator = overScrollAnimator;
4988d9ff9c2c66bc1d3b92eb6992d58599ff80ed6dcSelim Cinek        } else {
4998d9ff9c2c66bc1d3b92eb6992d58599ff80ed6dcSelim Cinek            mBottomOverScrollAnimator = overScrollAnimator;
5008d9ff9c2c66bc1d3b92eb6992d58599ff80ed6dcSelim Cinek        }
5018d9ff9c2c66bc1d3b92eb6992d58599ff80ed6dcSelim Cinek    }
5028d9ff9c2c66bc1d3b92eb6992d58599ff80ed6dcSelim Cinek
5038d9ff9c2c66bc1d3b92eb6992d58599ff80ed6dcSelim Cinek    public void cancelOverScrollAnimators(boolean onTop) {
5048d9ff9c2c66bc1d3b92eb6992d58599ff80ed6dcSelim Cinek        ValueAnimator currentAnimator = onTop ? mTopOverScrollAnimator : mBottomOverScrollAnimator;
5058d9ff9c2c66bc1d3b92eb6992d58599ff80ed6dcSelim Cinek        if (currentAnimator != null) {
5068d9ff9c2c66bc1d3b92eb6992d58599ff80ed6dcSelim Cinek            currentAnimator.cancel();
5078d9ff9c2c66bc1d3b92eb6992d58599ff80ed6dcSelim Cinek        }
5088d9ff9c2c66bc1d3b92eb6992d58599ff80ed6dcSelim Cinek    }
50902af41efe54eb2cc8fde7311e4cf5f0e5ff2373cSelim Cinek
510a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek    public void setHeadsUpAppearHeightBottom(int headsUpAppearHeightBottom) {
511a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek        mHeadsUpAppearHeightBottom = headsUpAppearHeightBottom;
512a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek    }
513a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek
514a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek    public void setShadeExpanded(boolean shadeExpanded) {
515a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek        mShadeExpanded = shadeExpanded;
516a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek    }
517eccb5de73cf793bd8b32d8861140fbdbfae0203fSelim Cinek
518eccb5de73cf793bd8b32d8861140fbdbfae0203fSelim Cinek    public void setShelf(NotificationShelf shelf) {
519eccb5de73cf793bd8b32d8861140fbdbfae0203fSelim Cinek        mShelf = shelf;
520eccb5de73cf793bd8b32d8861140fbdbfae0203fSelim Cinek    }
521572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek}
522