StackStateAnimator.java revision 95ed59283bd25fb363d13c000a7408bcafb5e93e
1572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek/* 2572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek * Copyright (C) 2014 The Android Open Source Project 3572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek * 4572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek * Licensed under the Apache License, Version 2.0 (the "License"); 5572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek * you may not use this file except in compliance with the License. 6572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek * You may obtain a copy of the License at 7572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek * 8572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek * http://www.apache.org/licenses/LICENSE-2.0 9572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek * 10572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek * Unless required by applicable law or agreed to in writing, software 11572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek * distributed under the License is distributed on an "AS IS" BASIS, 12572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek * See the License for the specific language governing permissions and 14572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek * limitations under the License 15572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek */ 16572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek 17572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinekpackage com.android.systemui.statusbar.stack; 18572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek 19eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinekimport android.animation.Animator; 20eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinekimport android.animation.AnimatorListenerAdapter; 21eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinekimport android.animation.ObjectAnimator; 22d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggiimport android.animation.PropertyValuesHolder; 23572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinekimport android.animation.ValueAnimator; 24572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinekimport android.view.View; 25572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinekimport android.view.animation.AnimationUtils; 26572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinekimport android.view.animation.Interpolator; 27eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek 28eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinekimport com.android.systemui.R; 29572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinekimport com.android.systemui.statusbar.ExpandableView; 308b73006a36b3000a9847534dcb01a2e7066e9d93Jorim Jaggiimport com.android.systemui.statusbar.SpeedBumpView; 31572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek 32572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinekimport java.util.ArrayList; 33eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinekimport java.util.HashSet; 34eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinekimport java.util.Set; 35eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinekimport java.util.Stack; 36572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek 37572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek/** 38572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek * An stack state animator which handles animations to new StackScrollStates 39572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek */ 40572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinekpublic class StackStateAnimator { 41572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek 425aa045cc6bca84f5c11f1a99999546ba5e5949a5Jorim Jaggi public static final int ANIMATION_DURATION_STANDARD = 360; 4360d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi public static final int ANIMATION_DURATION_GO_TO_FULL_SHADE = 448; 448efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek public static final int ANIMATION_DURATION_APPEAR_DISAPPEAR = 464; 455aa045cc6bca84f5c11f1a99999546ba5e5949a5Jorim Jaggi public static final int ANIMATION_DURATION_DIMMED_ACTIVATED = 220; 468efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek public static final int ANIMATION_DELAY_PER_ELEMENT_INTERRUPTING = 80; 478efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek public static final int ANIMATION_DELAY_PER_ELEMENT_MANUAL = 32; 4860d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi public static final int ANIMATION_DELAY_PER_ELEMENT_GO_TO_FULL_SHADE = 48; 498efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek private static final int DELAY_EFFECT_MAX_INDEX_DIFFERENCE = 2; 505aa045cc6bca84f5c11f1a99999546ba5e5949a5Jorim Jaggi 51eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek private static final int TAG_ANIMATOR_TRANSLATION_Y = R.id.translation_y_animator_tag; 52eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek private static final int TAG_ANIMATOR_TRANSLATION_Z = R.id.translation_z_animator_tag; 53d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi private static final int TAG_ANIMATOR_SCALE = R.id.scale_animator_tag; 54eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek private static final int TAG_ANIMATOR_ALPHA = R.id.alpha_animator_tag; 55eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek private static final int TAG_ANIMATOR_HEIGHT = R.id.height_animator_tag; 56eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek private static final int TAG_ANIMATOR_TOP_INSET = R.id.top_inset_animator_tag; 57eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek private static final int TAG_END_TRANSLATION_Y = R.id.translation_y_animator_end_value_tag; 58eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek private static final int TAG_END_TRANSLATION_Z = R.id.translation_z_animator_end_value_tag; 59d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi private static final int TAG_END_SCALE = R.id.scale_animator_end_value_tag; 60eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek private static final int TAG_END_ALPHA = R.id.alpha_animator_end_value_tag; 61eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek private static final int TAG_END_HEIGHT = R.id.height_animator_end_value_tag; 62eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek private static final int TAG_END_TOP_INSET = R.id.top_inset_animator_end_value_tag; 638df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek private static final int TAG_START_TRANSLATION_Y = R.id.translation_y_animator_start_value_tag; 648df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek private static final int TAG_START_TRANSLATION_Z = R.id.translation_z_animator_start_value_tag; 658df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek private static final int TAG_START_SCALE = R.id.scale_animator_start_value_tag; 668df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek private static final int TAG_START_ALPHA = R.id.alpha_animator_start_value_tag; 678df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek private static final int TAG_START_HEIGHT = R.id.height_animator_start_value_tag; 688df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek private static final int TAG_START_TOP_INSET = R.id.top_inset_animator_start_value_tag; 69572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek 70572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek private final Interpolator mFastOutSlowInInterpolator; 7160d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi private final int mGoToFullShadeAppearingTranslation; 72572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek public NotificationStackScrollLayout mHostLayout; 73eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek private ArrayList<NotificationStackScrollLayout.AnimationEvent> mNewEvents = 74eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek new ArrayList<>(); 758efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek private ArrayList<View> mNewAddChildren = new ArrayList<>(); 7660d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi private Set<Animator> mAnimatorSet = new HashSet<>(); 7760d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi private Stack<AnimatorListenerAdapter> mAnimationListenerPool = new Stack<>(); 78d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi private AnimationFilter mAnimationFilter = new AnimationFilter(); 795aa045cc6bca84f5c11f1a99999546ba5e5949a5Jorim Jaggi private long mCurrentLength; 80dbc3dce886cb7abba23a9f18e60d0f5af5c93226Jorim Jaggi private long mCurrentAdditionalDelay; 81572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek 8260d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi /** The current index for the last child which was not added in this event set. */ 8360d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi private int mCurrentLastNotAddedIndex; 8460d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi 858d9ff9c2c66bc1d3b92eb6992d58599ff80ed6dcSelim Cinek private ValueAnimator mTopOverScrollAnimator; 868d9ff9c2c66bc1d3b92eb6992d58599ff80ed6dcSelim Cinek private ValueAnimator mBottomOverScrollAnimator; 878d9ff9c2c66bc1d3b92eb6992d58599ff80ed6dcSelim Cinek 88572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek public StackStateAnimator(NotificationStackScrollLayout hostLayout) { 89572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek mHostLayout = hostLayout; 90572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek mFastOutSlowInInterpolator = AnimationUtils.loadInterpolator(hostLayout.getContext(), 91eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek android.R.interpolator.fast_out_slow_in); 9260d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi mGoToFullShadeAppearingTranslation = 9360d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi hostLayout.getContext().getResources().getDimensionPixelSize( 9460d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi R.dimen.go_to_full_shade_appearing_translation); 95572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek } 96572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek 97572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek public boolean isRunning() { 98eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek return !mAnimatorSet.isEmpty(); 99572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek } 100572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek 101572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek public void startAnimationForEvents( 1020dd6881ea481c855976214807c17595b34a2920aJorim Jaggi ArrayList<NotificationStackScrollLayout.AnimationEvent> mAnimationEvents, 103dbc3dce886cb7abba23a9f18e60d0f5af5c93226Jorim Jaggi StackScrollState finalState, long additionalDelay) { 104eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek 105eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek processAnimationEvents(mAnimationEvents, finalState); 106eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek 107572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek int childCount = mHostLayout.getChildCount(); 108d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi mAnimationFilter.applyCombination(mNewEvents); 109dbc3dce886cb7abba23a9f18e60d0f5af5c93226Jorim Jaggi mCurrentAdditionalDelay = additionalDelay; 1105aa045cc6bca84f5c11f1a99999546ba5e5949a5Jorim Jaggi mCurrentLength = NotificationStackScrollLayout.AnimationEvent.combineLength(mNewEvents); 11160d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi mCurrentLastNotAddedIndex = findLastNotAddedIndex(finalState); 112572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek for (int i = 0; i < childCount; i++) { 113572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek final ExpandableView child = (ExpandableView) mHostLayout.getChildAt(i); 1148efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek 115572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek StackScrollState.ViewState viewState = finalState.getViewStateForView(child); 1168efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek if (viewState == null || child.getVisibility() == View.GONE) { 117572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek continue; 118572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek } 119572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek 120572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek child.setClipBounds(null); 1218b73006a36b3000a9847534dcb01a2e7066e9d93Jorim Jaggi startAnimations(child, viewState, finalState, i); 122572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek } 123eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek if (!isRunning()) { 124eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek // no child has preformed any animation, lets finish 125eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek onAnimationFinished(); 126eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek } 1278efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek mNewEvents.clear(); 1288efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek mNewAddChildren.clear(); 129572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek } 130572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek 13160d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi private int findLastNotAddedIndex(StackScrollState finalState) { 13260d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi int childCount = mHostLayout.getChildCount(); 13360d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi for (int i = childCount - 1; i >= 0; i--) { 13460d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi final ExpandableView child = (ExpandableView) mHostLayout.getChildAt(i); 13560d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi 13660d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi StackScrollState.ViewState viewState = finalState.getViewStateForView(child); 13760d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi if (viewState == null || child.getVisibility() == View.GONE) { 13860d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi continue; 13960d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi } 14060d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi if (!mNewAddChildren.contains(child)) { 14160d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi return viewState.notGoneIndex; 14260d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi } 14360d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi } 14460d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi return -1; 14560d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi } 14660d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi 147eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek /** 148eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek * Start an animation to the given viewState 149eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek */ 1508efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek private void startAnimations(final ExpandableView child, StackScrollState.ViewState viewState, 1518b73006a36b3000a9847534dcb01a2e7066e9d93Jorim Jaggi StackScrollState finalState, int i) { 152eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek int childVisibility = child.getVisibility(); 153eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek boolean wasVisible = childVisibility == View.VISIBLE; 154eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek final float alpha = viewState.alpha; 155eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek if (!wasVisible && alpha != 0 && !viewState.gone) { 156eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek child.setVisibility(View.VISIBLE); 157eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek } 1588efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek 1598efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek boolean yTranslationChanging = child.getTranslationY() != viewState.yTranslation; 1608efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek boolean zTranslationChanging = child.getTranslationZ() != viewState.zTranslation; 1618efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek boolean scaleChanging = child.getScaleX() != viewState.scale; 1628efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek boolean alphaChanging = alpha != child.getAlpha(); 1638efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek boolean heightChanging = viewState.height != child.getActualHeight(); 164708a6c120da6750d281195ef15a240a5627efed4Selim Cinek boolean topInsetChanging = viewState.clipTopAmount != child.getClipTopAmount(); 1658efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek boolean wasAdded = mNewAddChildren.contains(child); 1668efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek boolean hasDelays = mAnimationFilter.hasDelays; 1678efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek boolean isDelayRelevant = yTranslationChanging || zTranslationChanging || scaleChanging || 168708a6c120da6750d281195ef15a240a5627efed4Selim Cinek alphaChanging || heightChanging || topInsetChanging; 1698efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek long delay = 0; 17060d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi long duration = mCurrentLength; 1718efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek if (hasDelays && isDelayRelevant || wasAdded) { 172dbc3dce886cb7abba23a9f18e60d0f5af5c93226Jorim Jaggi delay = mCurrentAdditionalDelay + calculateChildAnimationDelay(viewState, finalState); 1738efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek } 1748efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek 17560d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi if (wasAdded && mAnimationFilter.hasGoToFullShadeEvent) { 17660d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi child.setTranslationY(child.getTranslationY() + mGoToFullShadeAppearingTranslation); 17760d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi yTranslationChanging = true; 17860d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi float longerDurationFactor = viewState.notGoneIndex - mCurrentLastNotAddedIndex; 17960d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi longerDurationFactor = (float) Math.pow(longerDurationFactor, 0.7f); 18060d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi duration = ANIMATION_DURATION_APPEAR_DISAPPEAR + 50 + 18160d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi (long) (100 * longerDurationFactor); 18260d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi } 18360d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi 184eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek // start translationY animation 1858efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek if (yTranslationChanging) { 18660d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi startYTranslationAnimation(child, viewState, duration, delay); 187eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek } 1888efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek 189eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek // start translationZ animation 1908efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek if (zTranslationChanging) { 19160d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi startZTranslationAnimation(child, viewState, duration, delay); 192d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi } 1938efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek 194d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi // start scale animation 1958efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek if (scaleChanging) { 19660d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi startScaleAnimation(child, viewState, duration); 197eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek } 1988efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek 199eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek // start alpha animation 200eceda3d83814e20cabddc4f0755d475fa2f3d8ffDan Sandler if (alphaChanging && child.getTranslationX() == 0) { 20160d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi startAlphaAnimation(child, viewState, duration, delay); 202eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek } 2038efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek 204eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek // start height animation 2058efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek if (heightChanging) { 20660d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi startHeightAnimation(child, viewState, duration, delay); 20759b5a356b828fe60ea2874b0680a1bf7c84809a1Jorim Jaggi } 2088efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek 209708a6c120da6750d281195ef15a240a5627efed4Selim Cinek // start top inset animation 210708a6c120da6750d281195ef15a240a5627efed4Selim Cinek if (topInsetChanging) { 21160d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi startInsetAnimation(child, viewState, duration, delay); 212708a6c120da6750d281195ef15a240a5627efed4Selim Cinek } 213708a6c120da6750d281195ef15a240a5627efed4Selim Cinek 214d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi // start dimmed animation 215fa129db84a2985f81eac4c51204180b00122b632Selim Cinek child.setDimmed(viewState.dimmed, mAnimationFilter.animateDimmed && !wasAdded); 2168efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek 217bf370992508c55d1f2493923bdc1834a0710e4baJohn Spurlock // start dark animation 218bf370992508c55d1f2493923bdc1834a0710e4baJohn Spurlock child.setDark(viewState.dark, mAnimationFilter.animateDark); 219bf370992508c55d1f2493923bdc1834a0710e4baJohn Spurlock 2203d2b94bf8e32640e57573ebb17911b1db9440231Selim Cinek // apply speed bump state 2213d2b94bf8e32640e57573ebb17911b1db9440231Selim Cinek child.setBelowSpeedBump(viewState.belowSpeedBump); 2223d2b94bf8e32640e57573ebb17911b1db9440231Selim Cinek 223ae44128776410abd11bd06ae700db9cc4606a773Jorim Jaggi // start hiding sensitive animation 224ae44128776410abd11bd06ae700db9cc4606a773Jorim Jaggi child.setHideSensitive(viewState.hideSensitive, 225ae44128776410abd11bd06ae700db9cc4606a773Jorim Jaggi mAnimationFilter.animateHideSensitive && !wasAdded, delay, duration); 226ae44128776410abd11bd06ae700db9cc4606a773Jorim Jaggi 227f54090e9bb23e9ed1b4d9e500d856f80d2fbe775Selim Cinek // apply scrimming 228f54090e9bb23e9ed1b4d9e500d856f80d2fbe775Selim Cinek child.setScrimAmount(viewState.scrimAmount); 229f54090e9bb23e9ed1b4d9e500d856f80d2fbe775Selim Cinek 2308efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek if (wasAdded) { 23160d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi child.performAddAnimation(delay, mCurrentLength); 2328efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek } 2338b73006a36b3000a9847534dcb01a2e7066e9d93Jorim Jaggi if (child instanceof SpeedBumpView) { 2348b73006a36b3000a9847534dcb01a2e7066e9d93Jorim Jaggi finalState.performSpeedBumpAnimation(i, (SpeedBumpView) child, viewState, 2358b73006a36b3000a9847534dcb01a2e7066e9d93Jorim Jaggi delay + duration); 2368b73006a36b3000a9847534dcb01a2e7066e9d93Jorim Jaggi } 2378efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek } 2388efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek 2398efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek private long calculateChildAnimationDelay(StackScrollState.ViewState viewState, 2408efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek StackScrollState finalState) { 24160d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi if (mAnimationFilter.hasGoToFullShadeEvent) { 24260d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi return calculateDelayGoToFullShade(viewState); 24360d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi } 2448efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek long minDelay = 0; 2458efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek for (NotificationStackScrollLayout.AnimationEvent event : mNewEvents) { 2468efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek long delayPerElement = ANIMATION_DELAY_PER_ELEMENT_INTERRUPTING; 2478efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek switch (event.animationType) { 2488efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek case NotificationStackScrollLayout.AnimationEvent.ANIMATION_TYPE_ADD: { 2498efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek int ownIndex = viewState.notGoneIndex; 2508efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek int changingIndex = finalState 2518efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek .getViewStateForView(event.changingView).notGoneIndex; 2528efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek int difference = Math.abs(ownIndex - changingIndex); 2538efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek difference = Math.max(0, Math.min(DELAY_EFFECT_MAX_INDEX_DIFFERENCE, 2548efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek difference - 1)); 2558efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek long delay = (DELAY_EFFECT_MAX_INDEX_DIFFERENCE - difference) * delayPerElement; 2568efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek minDelay = Math.max(delay, minDelay); 2578efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek break; 2588efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek } 2598efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek case NotificationStackScrollLayout.AnimationEvent.ANIMATION_TYPE_REMOVE_SWIPED_OUT: 2608efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek delayPerElement = ANIMATION_DELAY_PER_ELEMENT_MANUAL; 2618efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek case NotificationStackScrollLayout.AnimationEvent.ANIMATION_TYPE_REMOVE: { 2628efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek int ownIndex = viewState.notGoneIndex; 2638efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek boolean noNextView = event.viewAfterChangingView == null; 2648efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek View viewAfterChangingView = noNextView 2658efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek ? mHostLayout.getLastChildNotGone() 2668efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek : event.viewAfterChangingView; 2678efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek 2688efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek int nextIndex = finalState 2698efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek .getViewStateForView(viewAfterChangingView).notGoneIndex; 2708efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek if (ownIndex >= nextIndex) { 2718efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek // we only have the view afterwards 2728efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek ownIndex++; 2738efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek } 2748efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek int difference = Math.abs(ownIndex - nextIndex); 2758efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek difference = Math.max(0, Math.min(DELAY_EFFECT_MAX_INDEX_DIFFERENCE, 2768efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek difference - 1)); 2778efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek long delay = difference * delayPerElement; 2788efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek minDelay = Math.max(delay, minDelay); 2798efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek break; 2808efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek } 2818efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek default: 2828efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek break; 2838efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek } 2848efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek } 2858efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek return minDelay; 286572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek } 287572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek 28860d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi private long calculateDelayGoToFullShade(StackScrollState.ViewState viewState) { 28960d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi float index = viewState.notGoneIndex; 29060d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi index = (float) Math.pow(index, 0.7f); 29160d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi return (long) (index * ANIMATION_DELAY_PER_ELEMENT_GO_TO_FULL_SHADE); 29260d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi } 29360d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi 294eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek private void startHeightAnimation(final ExpandableView child, 29560d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi StackScrollState.ViewState viewState, long duration, long delay) { 2968df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek Integer previousStartValue = getChildTag(child, TAG_START_HEIGHT); 297d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi Integer previousEndValue = getChildTag(child, TAG_END_HEIGHT); 2988df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek int newEndValue = viewState.height; 2998df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek if (previousEndValue != null && previousEndValue == newEndValue) { 300eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek return; 301eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek } 302eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek ValueAnimator previousAnimator = getChildTag(child, TAG_ANIMATOR_HEIGHT); 3038df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek if (!mAnimationFilter.animateHeight) { 3048df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek // just a local update was performed 3058df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek if (previousAnimator != null) { 3068df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek // we need to increase all animation keyframes of the previous animator by the 3078df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek // relative change to the end value 3088df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek PropertyValuesHolder[] values = previousAnimator.getValues(); 3098df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek int relativeDiff = newEndValue - previousEndValue; 3108df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek int newStartValue = previousStartValue + relativeDiff; 3118df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek values[0].setIntValues(newStartValue, newEndValue); 3128df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek child.setTag(TAG_START_HEIGHT, newStartValue); 3138df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek child.setTag(TAG_END_HEIGHT, newEndValue); 3148df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek previousAnimator.setCurrentPlayTime(previousAnimator.getCurrentPlayTime()); 3158df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek return; 3168df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek } else { 3178df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek // no new animation needed, let's just apply the value 3188df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek child.setActualHeight(newEndValue, false); 3198df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek return; 320eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek } 321eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek } 322eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek 3238df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek ValueAnimator animator = ValueAnimator.ofInt(child.getActualHeight(), newEndValue); 324eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { 325572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek @Override 326572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek public void onAnimationUpdate(ValueAnimator animation) { 327d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi child.setActualHeight((int) animation.getAnimatedValue(), 328d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi false /* notifyListeners */); 329572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek } 330572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek }); 331eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek animator.setInterpolator(mFastOutSlowInInterpolator); 33260d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi long newDuration = cancelAnimatorAndGetNewDuration(duration, previousAnimator); 333eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek animator.setDuration(newDuration); 3348efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek if (delay > 0 && (previousAnimator == null || !previousAnimator.isRunning())) { 3358efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek animator.setStartDelay(delay); 3368efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek } 337eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek animator.addListener(getGlobalAnimationFinishedListener()); 338eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek // remove the tag when the animation is finished 339eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek animator.addListener(new AnimatorListenerAdapter() { 340eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek @Override 341eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek public void onAnimationEnd(Animator animation) { 342eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek child.setTag(TAG_ANIMATOR_HEIGHT, null); 3438df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek child.setTag(TAG_START_HEIGHT, null); 344eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek child.setTag(TAG_END_HEIGHT, null); 345eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek } 346eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek }); 3478efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek startAnimator(animator); 348eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek child.setTag(TAG_ANIMATOR_HEIGHT, animator); 3498df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek child.setTag(TAG_START_HEIGHT, child.getActualHeight()); 3508df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek child.setTag(TAG_END_HEIGHT, newEndValue); 351eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek } 352eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek 353708a6c120da6750d281195ef15a240a5627efed4Selim Cinek private void startInsetAnimation(final ExpandableView child, 35460d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi StackScrollState.ViewState viewState, long duration, long delay) { 355708a6c120da6750d281195ef15a240a5627efed4Selim Cinek Integer previousStartValue = getChildTag(child, TAG_START_TOP_INSET); 356708a6c120da6750d281195ef15a240a5627efed4Selim Cinek Integer previousEndValue = getChildTag(child, TAG_END_TOP_INSET); 357708a6c120da6750d281195ef15a240a5627efed4Selim Cinek int newEndValue = viewState.clipTopAmount; 358708a6c120da6750d281195ef15a240a5627efed4Selim Cinek if (previousEndValue != null && previousEndValue == newEndValue) { 359708a6c120da6750d281195ef15a240a5627efed4Selim Cinek return; 360708a6c120da6750d281195ef15a240a5627efed4Selim Cinek } 361708a6c120da6750d281195ef15a240a5627efed4Selim Cinek ValueAnimator previousAnimator = getChildTag(child, TAG_ANIMATOR_TOP_INSET); 362708a6c120da6750d281195ef15a240a5627efed4Selim Cinek if (!mAnimationFilter.animateTopInset) { 363708a6c120da6750d281195ef15a240a5627efed4Selim Cinek // just a local update was performed 364708a6c120da6750d281195ef15a240a5627efed4Selim Cinek if (previousAnimator != null) { 365708a6c120da6750d281195ef15a240a5627efed4Selim Cinek // we need to increase all animation keyframes of the previous animator by the 366708a6c120da6750d281195ef15a240a5627efed4Selim Cinek // relative change to the end value 367708a6c120da6750d281195ef15a240a5627efed4Selim Cinek PropertyValuesHolder[] values = previousAnimator.getValues(); 368708a6c120da6750d281195ef15a240a5627efed4Selim Cinek int relativeDiff = newEndValue - previousEndValue; 369708a6c120da6750d281195ef15a240a5627efed4Selim Cinek int newStartValue = previousStartValue + relativeDiff; 370708a6c120da6750d281195ef15a240a5627efed4Selim Cinek values[0].setIntValues(newStartValue, newEndValue); 371708a6c120da6750d281195ef15a240a5627efed4Selim Cinek child.setTag(TAG_START_TOP_INSET, newStartValue); 372708a6c120da6750d281195ef15a240a5627efed4Selim Cinek child.setTag(TAG_END_TOP_INSET, newEndValue); 373708a6c120da6750d281195ef15a240a5627efed4Selim Cinek previousAnimator.setCurrentPlayTime(previousAnimator.getCurrentPlayTime()); 374708a6c120da6750d281195ef15a240a5627efed4Selim Cinek return; 375708a6c120da6750d281195ef15a240a5627efed4Selim Cinek } else { 376708a6c120da6750d281195ef15a240a5627efed4Selim Cinek // no new animation needed, let's just apply the value 377708a6c120da6750d281195ef15a240a5627efed4Selim Cinek child.setClipTopAmount(newEndValue); 378708a6c120da6750d281195ef15a240a5627efed4Selim Cinek return; 379708a6c120da6750d281195ef15a240a5627efed4Selim Cinek } 380708a6c120da6750d281195ef15a240a5627efed4Selim Cinek } 381708a6c120da6750d281195ef15a240a5627efed4Selim Cinek 382708a6c120da6750d281195ef15a240a5627efed4Selim Cinek ValueAnimator animator = ValueAnimator.ofInt(child.getClipTopAmount(), newEndValue); 383708a6c120da6750d281195ef15a240a5627efed4Selim Cinek animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { 384708a6c120da6750d281195ef15a240a5627efed4Selim Cinek @Override 385708a6c120da6750d281195ef15a240a5627efed4Selim Cinek public void onAnimationUpdate(ValueAnimator animation) { 386708a6c120da6750d281195ef15a240a5627efed4Selim Cinek child.setClipTopAmount((int) animation.getAnimatedValue()); 387708a6c120da6750d281195ef15a240a5627efed4Selim Cinek } 388708a6c120da6750d281195ef15a240a5627efed4Selim Cinek }); 389708a6c120da6750d281195ef15a240a5627efed4Selim Cinek animator.setInterpolator(mFastOutSlowInInterpolator); 39060d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi long newDuration = cancelAnimatorAndGetNewDuration(duration, previousAnimator); 391708a6c120da6750d281195ef15a240a5627efed4Selim Cinek animator.setDuration(newDuration); 392708a6c120da6750d281195ef15a240a5627efed4Selim Cinek if (delay > 0 && (previousAnimator == null || !previousAnimator.isRunning())) { 393708a6c120da6750d281195ef15a240a5627efed4Selim Cinek animator.setStartDelay(delay); 394708a6c120da6750d281195ef15a240a5627efed4Selim Cinek } 395708a6c120da6750d281195ef15a240a5627efed4Selim Cinek animator.addListener(getGlobalAnimationFinishedListener()); 396708a6c120da6750d281195ef15a240a5627efed4Selim Cinek // remove the tag when the animation is finished 397708a6c120da6750d281195ef15a240a5627efed4Selim Cinek animator.addListener(new AnimatorListenerAdapter() { 398708a6c120da6750d281195ef15a240a5627efed4Selim Cinek @Override 399708a6c120da6750d281195ef15a240a5627efed4Selim Cinek public void onAnimationEnd(Animator animation) { 400708a6c120da6750d281195ef15a240a5627efed4Selim Cinek child.setTag(TAG_ANIMATOR_TOP_INSET, null); 401708a6c120da6750d281195ef15a240a5627efed4Selim Cinek child.setTag(TAG_START_TOP_INSET, null); 402708a6c120da6750d281195ef15a240a5627efed4Selim Cinek child.setTag(TAG_END_TOP_INSET, null); 403708a6c120da6750d281195ef15a240a5627efed4Selim Cinek } 404708a6c120da6750d281195ef15a240a5627efed4Selim Cinek }); 405708a6c120da6750d281195ef15a240a5627efed4Selim Cinek startAnimator(animator); 406708a6c120da6750d281195ef15a240a5627efed4Selim Cinek child.setTag(TAG_ANIMATOR_TOP_INSET, animator); 407708a6c120da6750d281195ef15a240a5627efed4Selim Cinek child.setTag(TAG_START_TOP_INSET, child.getClipTopAmount()); 408708a6c120da6750d281195ef15a240a5627efed4Selim Cinek child.setTag(TAG_END_TOP_INSET, newEndValue); 409708a6c120da6750d281195ef15a240a5627efed4Selim Cinek } 410708a6c120da6750d281195ef15a240a5627efed4Selim Cinek 411eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek private void startAlphaAnimation(final ExpandableView child, 41260d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi final StackScrollState.ViewState viewState, long duration, long delay) { 4138df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek Float previousStartValue = getChildTag(child,TAG_START_ALPHA); 414eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek Float previousEndValue = getChildTag(child,TAG_END_ALPHA); 4158df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek final float newEndValue = viewState.alpha; 4168df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek if (previousEndValue != null && previousEndValue == newEndValue) { 417eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek return; 418eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek } 419eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek ObjectAnimator previousAnimator = getChildTag(child, TAG_ANIMATOR_ALPHA); 4208df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek if (!mAnimationFilter.animateAlpha) { 4218df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek // just a local update was performed 4228df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek if (previousAnimator != null) { 4238df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek // we need to increase all animation keyframes of the previous animator by the 4248df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek // relative change to the end value 4258df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek PropertyValuesHolder[] values = previousAnimator.getValues(); 4268df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek float relativeDiff = newEndValue - previousEndValue; 4278df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek float newStartValue = previousStartValue + relativeDiff; 4288df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek values[0].setFloatValues(newStartValue, newEndValue); 4298df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek child.setTag(TAG_START_ALPHA, newStartValue); 4308df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek child.setTag(TAG_END_ALPHA, newEndValue); 4318df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek previousAnimator.setCurrentPlayTime(previousAnimator.getCurrentPlayTime()); 4328df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek return; 4338df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek } else { 4348df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek // no new animation needed, let's just apply the value 4358df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek child.setAlpha(newEndValue); 4368df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek if (newEndValue == 0) { 4378df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek child.setVisibility(View.INVISIBLE); 4388df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek } 439eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek } 440eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek } 441eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek 442eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek ObjectAnimator animator = ObjectAnimator.ofFloat(child, View.ALPHA, 4438df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek child.getAlpha(), newEndValue); 444eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek animator.setInterpolator(mFastOutSlowInInterpolator); 445eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek // Handle layer type 446eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek child.setLayerType(View.LAYER_TYPE_HARDWARE, null); 447eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek animator.addListener(new AnimatorListenerAdapter() { 448eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek public boolean mWasCancelled; 449eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek 450eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek @Override 451eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek public void onAnimationEnd(Animator animation) { 4521efb240c1a0aeca9492cf8891794712adfdb1fa7Selim Cinek child.setLayerType(View.LAYER_TYPE_NONE, null); 4538df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek if (newEndValue == 0 && !mWasCancelled) { 454eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek child.setVisibility(View.INVISIBLE); 455eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek } 456eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek child.setTag(TAG_ANIMATOR_ALPHA, null); 4578df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek child.setTag(TAG_START_ALPHA, null); 458eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek child.setTag(TAG_END_ALPHA, null); 459eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek } 460eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek 461eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek @Override 462eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek public void onAnimationCancel(Animator animation) { 463eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek mWasCancelled = true; 464eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek } 465eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek 466eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek @Override 467eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek public void onAnimationStart(Animator animation) { 468eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek mWasCancelled = false; 469eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek } 470eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek }); 47160d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi long newDuration = cancelAnimatorAndGetNewDuration(duration, previousAnimator); 4723af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek animator.setDuration(newDuration); 4738efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek if (delay > 0 && (previousAnimator == null || !previousAnimator.isRunning())) { 4748efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek animator.setStartDelay(delay); 4758efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek } 476eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek animator.addListener(getGlobalAnimationFinishedListener()); 477eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek // remove the tag when the animation is finished 478eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek animator.addListener(new AnimatorListenerAdapter() { 479eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek @Override 480eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek public void onAnimationEnd(Animator animation) { 481eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek 482eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek } 483eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek }); 4848efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek startAnimator(animator); 485eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek child.setTag(TAG_ANIMATOR_ALPHA, animator); 4868df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek child.setTag(TAG_START_ALPHA, child.getAlpha()); 4878df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek child.setTag(TAG_END_ALPHA, newEndValue); 488572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek } 489572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek 490eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek private void startZTranslationAnimation(final ExpandableView child, 49160d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi final StackScrollState.ViewState viewState, long duration, long delay) { 4928df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek Float previousStartValue = getChildTag(child,TAG_START_TRANSLATION_Z); 493eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek Float previousEndValue = getChildTag(child,TAG_END_TRANSLATION_Z); 4948df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek float newEndValue = viewState.zTranslation; 4958df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek if (previousEndValue != null && previousEndValue == newEndValue) { 496eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek return; 497eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek } 498eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek ObjectAnimator previousAnimator = getChildTag(child, TAG_ANIMATOR_TRANSLATION_Z); 4998df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek if (!mAnimationFilter.animateZ) { 5008df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek // just a local update was performed 5018df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek if (previousAnimator != null) { 5028df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek // we need to increase all animation keyframes of the previous animator by the 5038df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek // relative change to the end value 5048df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek PropertyValuesHolder[] values = previousAnimator.getValues(); 5058df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek float relativeDiff = newEndValue - previousEndValue; 5068df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek float newStartValue = previousStartValue + relativeDiff; 5078df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek values[0].setFloatValues(newStartValue, newEndValue); 5088df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek child.setTag(TAG_START_TRANSLATION_Z, newStartValue); 5098df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek child.setTag(TAG_END_TRANSLATION_Z, newEndValue); 5108df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek previousAnimator.setCurrentPlayTime(previousAnimator.getCurrentPlayTime()); 5118df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek return; 5128df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek } else { 5138df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek // no new animation needed, let's just apply the value 5148df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek child.setTranslationZ(newEndValue); 515eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek } 516eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek } 517eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek 518eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek ObjectAnimator animator = ObjectAnimator.ofFloat(child, View.TRANSLATION_Z, 5198df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek child.getTranslationZ(), newEndValue); 520eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek animator.setInterpolator(mFastOutSlowInInterpolator); 52160d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi long newDuration = cancelAnimatorAndGetNewDuration(duration, previousAnimator); 5223af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek animator.setDuration(newDuration); 5238efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek if (delay > 0 && (previousAnimator == null || !previousAnimator.isRunning())) { 5248efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek animator.setStartDelay(delay); 5258efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek } 526eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek animator.addListener(getGlobalAnimationFinishedListener()); 527eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek // remove the tag when the animation is finished 528eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek animator.addListener(new AnimatorListenerAdapter() { 529eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek @Override 530eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek public void onAnimationEnd(Animator animation) { 531eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek child.setTag(TAG_ANIMATOR_TRANSLATION_Z, null); 5328df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek child.setTag(TAG_START_TRANSLATION_Z, null); 533eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek child.setTag(TAG_END_TRANSLATION_Z, null); 534eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek } 535eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek }); 5368efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek startAnimator(animator); 537eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek child.setTag(TAG_ANIMATOR_TRANSLATION_Z, animator); 5388df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek child.setTag(TAG_START_TRANSLATION_Z, child.getTranslationZ()); 5398df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek child.setTag(TAG_END_TRANSLATION_Z, newEndValue); 540eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek } 541eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek 542eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek private void startYTranslationAnimation(final ExpandableView child, 54360d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi StackScrollState.ViewState viewState, long duration, long delay) { 5448df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek Float previousStartValue = getChildTag(child,TAG_START_TRANSLATION_Y); 545eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek Float previousEndValue = getChildTag(child,TAG_END_TRANSLATION_Y); 5468df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek float newEndValue = viewState.yTranslation; 5478df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek if (previousEndValue != null && previousEndValue == newEndValue) { 548eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek return; 549eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek } 550eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek ObjectAnimator previousAnimator = getChildTag(child, TAG_ANIMATOR_TRANSLATION_Y); 5518df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek if (!mAnimationFilter.animateY) { 5528df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek // just a local update was performed 5538df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek if (previousAnimator != null) { 5548df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek // we need to increase all animation keyframes of the previous animator by the 5558df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek // relative change to the end value 5568df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek PropertyValuesHolder[] values = previousAnimator.getValues(); 5578df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek float relativeDiff = newEndValue - previousEndValue; 5588df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek float newStartValue = previousStartValue + relativeDiff; 5598df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek values[0].setFloatValues(newStartValue, newEndValue); 5608df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek child.setTag(TAG_START_TRANSLATION_Y, newStartValue); 5618df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek child.setTag(TAG_END_TRANSLATION_Y, newEndValue); 5628df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek previousAnimator.setCurrentPlayTime(previousAnimator.getCurrentPlayTime()); 5638df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek return; 5648df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek } else { 5658df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek // no new animation needed, let's just apply the value 5668df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek child.setTranslationY(newEndValue); 5678df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek return; 568eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek } 569eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek } 570eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek 571eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek ObjectAnimator animator = ObjectAnimator.ofFloat(child, View.TRANSLATION_Y, 5728df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek child.getTranslationY(), newEndValue); 573eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek animator.setInterpolator(mFastOutSlowInInterpolator); 57460d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi long newDuration = cancelAnimatorAndGetNewDuration(duration, previousAnimator); 5753af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek animator.setDuration(newDuration); 5768efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek if (delay > 0 && (previousAnimator == null || !previousAnimator.isRunning())) { 5778efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek animator.setStartDelay(delay); 5788efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek } 579eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek animator.addListener(getGlobalAnimationFinishedListener()); 580eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek // remove the tag when the animation is finished 581eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek animator.addListener(new AnimatorListenerAdapter() { 582eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek @Override 583eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek public void onAnimationEnd(Animator animation) { 584eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek child.setTag(TAG_ANIMATOR_TRANSLATION_Y, null); 5858df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek child.setTag(TAG_START_TRANSLATION_Y, null); 586eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek child.setTag(TAG_END_TRANSLATION_Y, null); 587eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek } 588eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek }); 5898efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek startAnimator(animator); 590eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek child.setTag(TAG_ANIMATOR_TRANSLATION_Y, animator); 5918df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek child.setTag(TAG_START_TRANSLATION_Y, child.getTranslationY()); 5928df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek child.setTag(TAG_END_TRANSLATION_Y, newEndValue); 593eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek } 594eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek 595d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi private void startScaleAnimation(final ExpandableView child, 59660d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi StackScrollState.ViewState viewState, long duration) { 5978df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek Float previousStartValue = getChildTag(child, TAG_START_SCALE); 598d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi Float previousEndValue = getChildTag(child, TAG_END_SCALE); 5998df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek float newEndValue = viewState.scale; 6008df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek if (previousEndValue != null && previousEndValue == newEndValue) { 601d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi return; 602d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi } 603d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi ObjectAnimator previousAnimator = getChildTag(child, TAG_ANIMATOR_SCALE); 6048df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek if (!mAnimationFilter.animateScale) { 6058df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek // just a local update was performed 6068df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek if (previousAnimator != null) { 6078df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek // we need to increase all animation keyframes of the previous animator by the 6088df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek // relative change to the end value 6098df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek PropertyValuesHolder[] values = previousAnimator.getValues(); 6108df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek float relativeDiff = newEndValue - previousEndValue; 6118df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek float newStartValue = previousStartValue + relativeDiff; 6128df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek values[0].setFloatValues(newStartValue, newEndValue); 6138df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek values[1].setFloatValues(newStartValue, newEndValue); 6148df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek child.setTag(TAG_START_SCALE, newStartValue); 6158df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek child.setTag(TAG_END_SCALE, newEndValue); 6168df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek previousAnimator.setCurrentPlayTime(previousAnimator.getCurrentPlayTime()); 6178df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek return; 6188df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek } else { 6198df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek // no new animation needed, let's just apply the value 6208df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek child.setScaleX(newEndValue); 6218df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek child.setScaleY(newEndValue); 622d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi } 623d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi } 624d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi 625d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi PropertyValuesHolder holderX = 6268df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek PropertyValuesHolder.ofFloat(View.SCALE_X, child.getScaleX(), newEndValue); 627d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi PropertyValuesHolder holderY = 6288df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek PropertyValuesHolder.ofFloat(View.SCALE_Y, child.getScaleY(), newEndValue); 629d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi ObjectAnimator animator = ObjectAnimator.ofPropertyValuesHolder(child, holderX, holderY); 630d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi animator.setInterpolator(mFastOutSlowInInterpolator); 63160d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi long newDuration = cancelAnimatorAndGetNewDuration(duration, previousAnimator); 632d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi animator.setDuration(newDuration); 633d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi animator.addListener(getGlobalAnimationFinishedListener()); 634d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi // remove the tag when the animation is finished 635d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi animator.addListener(new AnimatorListenerAdapter() { 636d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi @Override 637d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi public void onAnimationEnd(Animator animation) { 638d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi child.setTag(TAG_ANIMATOR_SCALE, null); 6398df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek child.setTag(TAG_START_SCALE, null); 640d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi child.setTag(TAG_END_SCALE, null); 641d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi } 642d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi }); 6438efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek startAnimator(animator); 644d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi child.setTag(TAG_ANIMATOR_SCALE, animator); 6458df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek child.setTag(TAG_START_SCALE, child.getScaleX()); 6468df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek child.setTag(TAG_END_SCALE, newEndValue); 647d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi } 648d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi 6498efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek private void startAnimator(ValueAnimator animator) { 6508efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek mAnimatorSet.add(animator); 6513af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek animator.start(); 6523af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek } 6533af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek 6543af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek /** 6553af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek * @return an adapter which ensures that onAnimationFinished is called once no animation is 6563af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek * running anymore 6573af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek */ 6583af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek private AnimatorListenerAdapter getGlobalAnimationFinishedListener() { 6593af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek if (!mAnimationListenerPool.empty()) { 6603af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek return mAnimationListenerPool.pop(); 6613af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek } 6623af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek 6633af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek // We need to create a new one, no reusable ones found 6643af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek return new AnimatorListenerAdapter() { 6653af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek private boolean mWasCancelled; 6663af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek 6673af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek @Override 6683af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek public void onAnimationEnd(Animator animation) { 6693af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek mAnimatorSet.remove(animation); 6703af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek if (mAnimatorSet.isEmpty() && !mWasCancelled) { 6713af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek onAnimationFinished(); 6723af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek } 6733af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek mAnimationListenerPool.push(this); 6743af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek } 6753af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek 6763af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek @Override 6773af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek public void onAnimationCancel(Animator animation) { 6783af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek mWasCancelled = true; 6793af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek } 6803af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek 6813af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek @Override 6823af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek public void onAnimationStart(Animator animation) { 6833af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek mWasCancelled = false; 6843af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek } 6853af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek }; 6863af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek } 6873af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek 688eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek private <T> T getChildTag(View child, int tag) { 689eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek return (T) child.getTag(tag); 690eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek } 691eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek 692eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek /** 693eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek * Cancel the previous animator and get the duration of the new animation. 69439610545f0c2714a3526bc935effe57b421542d1Selim Cinek * 69560d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi * @param duration the new duration 696eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek * @param previousAnimator the animator which was running before 697eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek * @return the new duration 698eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek */ 69960d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi private long cancelAnimatorAndGetNewDuration(long duration, ValueAnimator previousAnimator) { 70060d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi long newDuration = duration; 701eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek if (previousAnimator != null) { 7028df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek // We take either the desired length of the new animation or the remaining time of 7038df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek // the previous animator, whichever is longer. 7048df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek newDuration = Math.max(previousAnimator.getDuration() 7058df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek - previousAnimator.getCurrentPlayTime(), newDuration); 7063af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek previousAnimator.cancel(); 707eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek } 7083af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek return newDuration; 709eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek } 710eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek 711eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek private void onAnimationFinished() { 712eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek mHostLayout.onChildAnimationFinished(); 713eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek } 714eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek 715eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek /** 716eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek * Process the animationEvents for a new animation 717eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek * 718eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek * @param animationEvents the animation events for the animation to perform 71939610545f0c2714a3526bc935effe57b421542d1Selim Cinek * @param finalState the final state to animate to 72039610545f0c2714a3526bc935effe57b421542d1Selim Cinek */ 721eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek private void processAnimationEvents( 7220dd6881ea481c855976214807c17595b34a2920aJorim Jaggi ArrayList<NotificationStackScrollLayout.AnimationEvent> animationEvents, 723572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek StackScrollState finalState) { 7248d9ff9c2c66bc1d3b92eb6992d58599ff80ed6dcSelim Cinek for (NotificationStackScrollLayout.AnimationEvent event : animationEvents) { 7258efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek final ExpandableView changingView = (ExpandableView) event.changingView; 7268efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek if (event.animationType == 7278efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek NotificationStackScrollLayout.AnimationEvent.ANIMATION_TYPE_ADD) { 7288efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek 7298efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek // This item is added, initialize it's properties. 7308efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek StackScrollState.ViewState viewState = finalState 7318efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek .getViewStateForView(changingView); 7328efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek if (viewState == null) { 7338efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek // The position for this child was never generated, let's continue. 7348efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek continue; 7358efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek } 7368efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek if (changingView.getVisibility() == View.GONE) { 7378efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek // The view was set to gone but the state never removed 7388efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek finalState.removeViewStateForView(changingView); 7398efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek continue; 74039610545f0c2714a3526bc935effe57b421542d1Selim Cinek } 7418efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek changingView.setAlpha(viewState.alpha); 7428efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek changingView.setTranslationY(viewState.yTranslation); 7438efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek changingView.setTranslationZ(viewState.zTranslation); 7448efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek changingView.setActualHeight(viewState.height, false); 7458efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek mNewAddChildren.add(changingView); 7468efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek 7478efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek } else if (event.animationType == 7488efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek NotificationStackScrollLayout.AnimationEvent.ANIMATION_TYPE_REMOVE) { 7498efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek if (changingView.getVisibility() == View.GONE) { 75095ed59283bd25fb363d13c000a7408bcafb5e93eSelim Cinek mHostLayout.getOverlay().remove(changingView); 7518efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek continue; 7528efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek } 7538efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek 7548efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek // Find the amount to translate up. This is needed in order to understand the 7558efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek // direction of the remove animation (either downwards or upwards) 7568efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek StackScrollState.ViewState viewState = finalState 7578efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek .getViewStateForView(event.viewAfterChangingView); 7588efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek int actualHeight = changingView.getActualHeight(); 7598efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek // upwards by default 7608efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek float translationDirection = -1.0f; 7618efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek if (viewState != null) { 7628efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek // there was a view after this one, Approximate the distance the next child 7638efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek // travelled 7648efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek translationDirection = ((viewState.yTranslation 7658efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek - (changingView.getTranslationY() + actualHeight / 2.0f)) * 2 / 7668efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek actualHeight); 7678efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek translationDirection = Math.max(Math.min(translationDirection, 1.0f),-1.0f); 7688efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek 7698efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek } 77060d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi changingView.performRemoveAnimation(ANIMATION_DURATION_APPEAR_DISAPPEAR, 77160d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi translationDirection, new Runnable() { 7728efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek @Override 7738efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek public void run() { 7748efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek // remove the temporary overlay 7758efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek mHostLayout.getOverlay().remove(changingView); 7768efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek } 7778efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek }); 778572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek } 7798efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek mNewEvents.add(event); 780572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek } 781572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek } 7828d9ff9c2c66bc1d3b92eb6992d58599ff80ed6dcSelim Cinek 783475b21dfe517ec04f435f6b02f4a53083d040db4Jorim Jaggi public void animateOverScrollToAmount(float targetAmount, final boolean onTop, 784475b21dfe517ec04f435f6b02f4a53083d040db4Jorim Jaggi final boolean isRubberbanded) { 7858d9ff9c2c66bc1d3b92eb6992d58599ff80ed6dcSelim Cinek final float startOverScrollAmount = mHostLayout.getCurrentOverScrollAmount(onTop); 7869012958742c7a66b37ba5f2196f9086bb1980e6bJorim Jaggi if (targetAmount == startOverScrollAmount) { 7879012958742c7a66b37ba5f2196f9086bb1980e6bJorim Jaggi return; 7889012958742c7a66b37ba5f2196f9086bb1980e6bJorim Jaggi } 7898d9ff9c2c66bc1d3b92eb6992d58599ff80ed6dcSelim Cinek cancelOverScrollAnimators(onTop); 7908d9ff9c2c66bc1d3b92eb6992d58599ff80ed6dcSelim Cinek ValueAnimator overScrollAnimator = ValueAnimator.ofFloat(startOverScrollAmount, 7918d9ff9c2c66bc1d3b92eb6992d58599ff80ed6dcSelim Cinek targetAmount); 7928d9ff9c2c66bc1d3b92eb6992d58599ff80ed6dcSelim Cinek overScrollAnimator.setDuration(ANIMATION_DURATION_STANDARD); 7938d9ff9c2c66bc1d3b92eb6992d58599ff80ed6dcSelim Cinek overScrollAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { 7948d9ff9c2c66bc1d3b92eb6992d58599ff80ed6dcSelim Cinek @Override 7958d9ff9c2c66bc1d3b92eb6992d58599ff80ed6dcSelim Cinek public void onAnimationUpdate(ValueAnimator animation) { 7968d9ff9c2c66bc1d3b92eb6992d58599ff80ed6dcSelim Cinek float currentOverScroll = (float) animation.getAnimatedValue(); 79747c85a3525dcd0bbd3168632830e8ab491d18462Jorim Jaggi mHostLayout.setOverScrollAmount( 798475b21dfe517ec04f435f6b02f4a53083d040db4Jorim Jaggi currentOverScroll, onTop, false /* animate */, false /* cancelAnimators */, 799475b21dfe517ec04f435f6b02f4a53083d040db4Jorim Jaggi isRubberbanded); 8008d9ff9c2c66bc1d3b92eb6992d58599ff80ed6dcSelim Cinek } 8018d9ff9c2c66bc1d3b92eb6992d58599ff80ed6dcSelim Cinek }); 8028d9ff9c2c66bc1d3b92eb6992d58599ff80ed6dcSelim Cinek overScrollAnimator.setInterpolator(mFastOutSlowInInterpolator); 80347c85a3525dcd0bbd3168632830e8ab491d18462Jorim Jaggi overScrollAnimator.addListener(new AnimatorListenerAdapter() { 80447c85a3525dcd0bbd3168632830e8ab491d18462Jorim Jaggi @Override 80547c85a3525dcd0bbd3168632830e8ab491d18462Jorim Jaggi public void onAnimationEnd(Animator animation) { 80647c85a3525dcd0bbd3168632830e8ab491d18462Jorim Jaggi if (onTop) { 80747c85a3525dcd0bbd3168632830e8ab491d18462Jorim Jaggi mTopOverScrollAnimator = null; 80847c85a3525dcd0bbd3168632830e8ab491d18462Jorim Jaggi } else { 80947c85a3525dcd0bbd3168632830e8ab491d18462Jorim Jaggi mBottomOverScrollAnimator = null; 81047c85a3525dcd0bbd3168632830e8ab491d18462Jorim Jaggi } 81147c85a3525dcd0bbd3168632830e8ab491d18462Jorim Jaggi } 81247c85a3525dcd0bbd3168632830e8ab491d18462Jorim Jaggi }); 8138d9ff9c2c66bc1d3b92eb6992d58599ff80ed6dcSelim Cinek overScrollAnimator.start(); 8148d9ff9c2c66bc1d3b92eb6992d58599ff80ed6dcSelim Cinek if (onTop) { 8158d9ff9c2c66bc1d3b92eb6992d58599ff80ed6dcSelim Cinek mTopOverScrollAnimator = overScrollAnimator; 8168d9ff9c2c66bc1d3b92eb6992d58599ff80ed6dcSelim Cinek } else { 8178d9ff9c2c66bc1d3b92eb6992d58599ff80ed6dcSelim Cinek mBottomOverScrollAnimator = overScrollAnimator; 8188d9ff9c2c66bc1d3b92eb6992d58599ff80ed6dcSelim Cinek } 8198d9ff9c2c66bc1d3b92eb6992d58599ff80ed6dcSelim Cinek } 8208d9ff9c2c66bc1d3b92eb6992d58599ff80ed6dcSelim Cinek 8218d9ff9c2c66bc1d3b92eb6992d58599ff80ed6dcSelim Cinek public void cancelOverScrollAnimators(boolean onTop) { 8228d9ff9c2c66bc1d3b92eb6992d58599ff80ed6dcSelim Cinek ValueAnimator currentAnimator = onTop ? mTopOverScrollAnimator : mBottomOverScrollAnimator; 8238d9ff9c2c66bc1d3b92eb6992d58599ff80ed6dcSelim Cinek if (currentAnimator != null) { 8248d9ff9c2c66bc1d3b92eb6992d58599ff80ed6dcSelim Cinek currentAnimator.cancel(); 8258d9ff9c2c66bc1d3b92eb6992d58599ff80ed6dcSelim Cinek } 8268d9ff9c2c66bc1d3b92eb6992d58599ff80ed6dcSelim Cinek } 827572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek} 828