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