StackStateAnimator.java revision 3d2b94bf8e32640e57573ebb17911b1db9440231
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; 30572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek 31572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinekimport java.util.ArrayList; 32eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinekimport java.util.HashSet; 33eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinekimport java.util.Set; 34eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinekimport java.util.Stack; 35572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek 36572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek/** 37572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek * An stack state animator which handles animations to new StackScrollStates 38572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek */ 39572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinekpublic class StackStateAnimator { 40572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek 415aa045cc6bca84f5c11f1a99999546ba5e5949a5Jorim Jaggi public static final int ANIMATION_DURATION_STANDARD = 360; 428efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek public static final int ANIMATION_DURATION_APPEAR_DISAPPEAR = 464; 435aa045cc6bca84f5c11f1a99999546ba5e5949a5Jorim Jaggi public static final int ANIMATION_DURATION_DIMMED_ACTIVATED = 220; 448efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek public static final int ANIMATION_DELAY_PER_ELEMENT_INTERRUPTING = 80; 458efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek public static final int ANIMATION_DELAY_PER_ELEMENT_MANUAL = 32; 468efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek private static final int DELAY_EFFECT_MAX_INDEX_DIFFERENCE = 2; 475aa045cc6bca84f5c11f1a99999546ba5e5949a5Jorim Jaggi 48eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek private static final int TAG_ANIMATOR_TRANSLATION_Y = R.id.translation_y_animator_tag; 49eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek private static final int TAG_ANIMATOR_TRANSLATION_Z = R.id.translation_z_animator_tag; 50d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi private static final int TAG_ANIMATOR_SCALE = R.id.scale_animator_tag; 51eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek private static final int TAG_ANIMATOR_ALPHA = R.id.alpha_animator_tag; 52eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek private static final int TAG_ANIMATOR_HEIGHT = R.id.height_animator_tag; 53eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek private static final int TAG_ANIMATOR_TOP_INSET = R.id.top_inset_animator_tag; 54eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek private static final int TAG_END_TRANSLATION_Y = R.id.translation_y_animator_end_value_tag; 55eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek private static final int TAG_END_TRANSLATION_Z = R.id.translation_z_animator_end_value_tag; 56d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi private static final int TAG_END_SCALE = R.id.scale_animator_end_value_tag; 57eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek private static final int TAG_END_ALPHA = R.id.alpha_animator_end_value_tag; 58eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek private static final int TAG_END_HEIGHT = R.id.height_animator_end_value_tag; 59eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek private static final int TAG_END_TOP_INSET = R.id.top_inset_animator_end_value_tag; 608df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek private static final int TAG_START_TRANSLATION_Y = R.id.translation_y_animator_start_value_tag; 618df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek private static final int TAG_START_TRANSLATION_Z = R.id.translation_z_animator_start_value_tag; 628df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek private static final int TAG_START_SCALE = R.id.scale_animator_start_value_tag; 638df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek private static final int TAG_START_ALPHA = R.id.alpha_animator_start_value_tag; 648df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek private static final int TAG_START_HEIGHT = R.id.height_animator_start_value_tag; 658df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek private static final int TAG_START_TOP_INSET = R.id.top_inset_animator_start_value_tag; 66572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek 67572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek private final Interpolator mFastOutSlowInInterpolator; 68572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek public NotificationStackScrollLayout mHostLayout; 69eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek private ArrayList<NotificationStackScrollLayout.AnimationEvent> mNewEvents = 70eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek new ArrayList<>(); 718efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek private ArrayList<View> mNewAddChildren = new ArrayList<>(); 72eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek private Set<Animator> mAnimatorSet = new HashSet<Animator>(); 73eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek private Stack<AnimatorListenerAdapter> mAnimationListenerPool 74eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek = new Stack<AnimatorListenerAdapter>(); 75d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi private AnimationFilter mAnimationFilter = new AnimationFilter(); 765aa045cc6bca84f5c11f1a99999546ba5e5949a5Jorim Jaggi private long mCurrentLength; 77572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek 788d9ff9c2c66bc1d3b92eb6992d58599ff80ed6dcSelim Cinek private ValueAnimator mTopOverScrollAnimator; 798d9ff9c2c66bc1d3b92eb6992d58599ff80ed6dcSelim Cinek private ValueAnimator mBottomOverScrollAnimator; 808d9ff9c2c66bc1d3b92eb6992d58599ff80ed6dcSelim Cinek 81572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek public StackStateAnimator(NotificationStackScrollLayout hostLayout) { 82572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek mHostLayout = hostLayout; 83572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek mFastOutSlowInInterpolator = AnimationUtils.loadInterpolator(hostLayout.getContext(), 84eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek android.R.interpolator.fast_out_slow_in); 85572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek } 86572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek 87572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek public boolean isRunning() { 88eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek return !mAnimatorSet.isEmpty(); 89572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek } 90572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek 91572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek public void startAnimationForEvents( 920dd6881ea481c855976214807c17595b34a2920aJorim Jaggi ArrayList<NotificationStackScrollLayout.AnimationEvent> mAnimationEvents, 93572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek StackScrollState finalState) { 94eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek 95eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek processAnimationEvents(mAnimationEvents, finalState); 96eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek 97572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek int childCount = mHostLayout.getChildCount(); 98d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi mAnimationFilter.applyCombination(mNewEvents); 995aa045cc6bca84f5c11f1a99999546ba5e5949a5Jorim Jaggi mCurrentLength = NotificationStackScrollLayout.AnimationEvent.combineLength(mNewEvents); 100572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek for (int i = 0; i < childCount; i++) { 101572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek final ExpandableView child = (ExpandableView) mHostLayout.getChildAt(i); 1028efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek 103572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek StackScrollState.ViewState viewState = finalState.getViewStateForView(child); 1048efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek if (viewState == null || child.getVisibility() == View.GONE) { 105572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek continue; 106572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek } 107572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek 108572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek child.setClipBounds(null); 1098efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek startAnimations(child, viewState, finalState); 110572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek } 111eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek if (!isRunning()) { 112eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek // no child has preformed any animation, lets finish 113eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek onAnimationFinished(); 114eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek } 1158efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek mNewEvents.clear(); 1168efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek mNewAddChildren.clear(); 117572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek } 118572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek 119eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek /** 120eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek * Start an animation to the given viewState 121eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek */ 1228efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek private void startAnimations(final ExpandableView child, StackScrollState.ViewState viewState, 1238efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek StackScrollState finalState) { 124eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek int childVisibility = child.getVisibility(); 125eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek boolean wasVisible = childVisibility == View.VISIBLE; 126eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek final float alpha = viewState.alpha; 127eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek if (!wasVisible && alpha != 0 && !viewState.gone) { 128eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek child.setVisibility(View.VISIBLE); 129eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek } 1308efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek 1318efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek boolean yTranslationChanging = child.getTranslationY() != viewState.yTranslation; 1328efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek boolean zTranslationChanging = child.getTranslationZ() != viewState.zTranslation; 1338efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek boolean scaleChanging = child.getScaleX() != viewState.scale; 1348efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek boolean alphaChanging = alpha != child.getAlpha(); 1358efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek boolean heightChanging = viewState.height != child.getActualHeight(); 136708a6c120da6750d281195ef15a240a5627efed4Selim Cinek boolean topInsetChanging = viewState.clipTopAmount != child.getClipTopAmount(); 1378efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek boolean wasAdded = mNewAddChildren.contains(child); 1388efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek boolean hasDelays = mAnimationFilter.hasDelays; 1398efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek boolean isDelayRelevant = yTranslationChanging || zTranslationChanging || scaleChanging || 140708a6c120da6750d281195ef15a240a5627efed4Selim Cinek alphaChanging || heightChanging || topInsetChanging; 1418efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek long delay = 0; 1428efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek if (hasDelays && isDelayRelevant || wasAdded) { 1438efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek delay = calculateChildAnimationDelay(viewState, finalState); 1448efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek } 1458efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek 146eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek // start translationY animation 1478efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek if (yTranslationChanging) { 1488efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek startYTranslationAnimation(child, viewState, delay); 149eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek } 1508efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek 151eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek // start translationZ animation 1528efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek if (zTranslationChanging) { 1538efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek startZTranslationAnimation(child, viewState, delay); 154d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi } 1558efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek 156d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi // start scale animation 1578efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek if (scaleChanging) { 158d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi startScaleAnimation(child, viewState); 159eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek } 1608efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek 161eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek // start alpha animation 1628efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek if (alphaChanging) { 1638efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek startAlphaAnimation(child, viewState, delay); 164eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek } 1658efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek 166eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek // start height animation 1678efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek if (heightChanging) { 1688efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek startHeightAnimation(child, viewState, delay); 16959b5a356b828fe60ea2874b0680a1bf7c84809a1Jorim Jaggi } 1708efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek 171708a6c120da6750d281195ef15a240a5627efed4Selim Cinek // start top inset animation 172708a6c120da6750d281195ef15a240a5627efed4Selim Cinek if (topInsetChanging) { 173708a6c120da6750d281195ef15a240a5627efed4Selim Cinek startInsetAnimation(child, viewState, delay); 174708a6c120da6750d281195ef15a240a5627efed4Selim Cinek } 175708a6c120da6750d281195ef15a240a5627efed4Selim Cinek 176d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi // start dimmed animation 177fa129db84a2985f81eac4c51204180b00122b632Selim Cinek child.setDimmed(viewState.dimmed, mAnimationFilter.animateDimmed && !wasAdded); 1788efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek 179bf370992508c55d1f2493923bdc1834a0710e4baJohn Spurlock // start dark animation 180bf370992508c55d1f2493923bdc1834a0710e4baJohn Spurlock child.setDark(viewState.dark, mAnimationFilter.animateDark); 181bf370992508c55d1f2493923bdc1834a0710e4baJohn Spurlock 1823d2b94bf8e32640e57573ebb17911b1db9440231Selim Cinek // apply speed bump state 1833d2b94bf8e32640e57573ebb17911b1db9440231Selim Cinek child.setBelowSpeedBump(viewState.belowSpeedBump); 1843d2b94bf8e32640e57573ebb17911b1db9440231Selim Cinek 185f54090e9bb23e9ed1b4d9e500d856f80d2fbe775Selim Cinek // apply scrimming 186f54090e9bb23e9ed1b4d9e500d856f80d2fbe775Selim Cinek child.setScrimAmount(viewState.scrimAmount); 187f54090e9bb23e9ed1b4d9e500d856f80d2fbe775Selim Cinek 1888efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek if (wasAdded) { 1898efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek child.performAddAnimation(delay); 1908efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek } 1918efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek } 1928efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek 1938efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek private long calculateChildAnimationDelay(StackScrollState.ViewState viewState, 1948efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek StackScrollState finalState) { 1958efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek long minDelay = 0; 1968efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek for (NotificationStackScrollLayout.AnimationEvent event : mNewEvents) { 1978efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek long delayPerElement = ANIMATION_DELAY_PER_ELEMENT_INTERRUPTING; 1988efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek switch (event.animationType) { 1998efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek case NotificationStackScrollLayout.AnimationEvent.ANIMATION_TYPE_ADD: { 2008efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek int ownIndex = viewState.notGoneIndex; 2018efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek int changingIndex = finalState 2028efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek .getViewStateForView(event.changingView).notGoneIndex; 2038efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek int difference = Math.abs(ownIndex - changingIndex); 2048efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek difference = Math.max(0, Math.min(DELAY_EFFECT_MAX_INDEX_DIFFERENCE, 2058efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek difference - 1)); 2068efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek long delay = (DELAY_EFFECT_MAX_INDEX_DIFFERENCE - difference) * delayPerElement; 2078efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek minDelay = Math.max(delay, minDelay); 2088efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek break; 2098efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek } 2108efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek case NotificationStackScrollLayout.AnimationEvent.ANIMATION_TYPE_REMOVE_SWIPED_OUT: 2118efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek delayPerElement = ANIMATION_DELAY_PER_ELEMENT_MANUAL; 2128efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek case NotificationStackScrollLayout.AnimationEvent.ANIMATION_TYPE_REMOVE: { 2138efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek int ownIndex = viewState.notGoneIndex; 2148efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek boolean noNextView = event.viewAfterChangingView == null; 2158efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek View viewAfterChangingView = noNextView 2168efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek ? mHostLayout.getLastChildNotGone() 2178efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek : event.viewAfterChangingView; 2188efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek 2198efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek int nextIndex = finalState 2208efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek .getViewStateForView(viewAfterChangingView).notGoneIndex; 2218efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek if (ownIndex >= nextIndex) { 2228efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek // we only have the view afterwards 2238efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek ownIndex++; 2248efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek } 2258efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek int difference = Math.abs(ownIndex - nextIndex); 2268efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek difference = Math.max(0, Math.min(DELAY_EFFECT_MAX_INDEX_DIFFERENCE, 2278efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek difference - 1)); 2288efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek long delay = difference * delayPerElement; 2298efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek minDelay = Math.max(delay, minDelay); 2308efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek break; 2318efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek } 2328efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek default: 2338efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek break; 2348efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek } 2358efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek } 2368efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek return minDelay; 237572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek } 238572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek 239eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek private void startHeightAnimation(final ExpandableView child, 2408efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek StackScrollState.ViewState viewState, long delay) { 2418df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek Integer previousStartValue = getChildTag(child, TAG_START_HEIGHT); 242d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi Integer previousEndValue = getChildTag(child, TAG_END_HEIGHT); 2438df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek int newEndValue = viewState.height; 2448df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek if (previousEndValue != null && previousEndValue == newEndValue) { 245eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek return; 246eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek } 247eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek ValueAnimator previousAnimator = getChildTag(child, TAG_ANIMATOR_HEIGHT); 2488df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek if (!mAnimationFilter.animateHeight) { 2498df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek // just a local update was performed 2508df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek if (previousAnimator != null) { 2518df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek // we need to increase all animation keyframes of the previous animator by the 2528df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek // relative change to the end value 2538df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek PropertyValuesHolder[] values = previousAnimator.getValues(); 2548df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek int relativeDiff = newEndValue - previousEndValue; 2558df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek int newStartValue = previousStartValue + relativeDiff; 2568df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek values[0].setIntValues(newStartValue, newEndValue); 2578df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek child.setTag(TAG_START_HEIGHT, newStartValue); 2588df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek child.setTag(TAG_END_HEIGHT, newEndValue); 2598df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek previousAnimator.setCurrentPlayTime(previousAnimator.getCurrentPlayTime()); 2608df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek return; 2618df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek } else { 2628df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek // no new animation needed, let's just apply the value 2638df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek child.setActualHeight(newEndValue, false); 2648df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek return; 265eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek } 266eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek } 267eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek 2688df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek ValueAnimator animator = ValueAnimator.ofInt(child.getActualHeight(), newEndValue); 269eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { 270572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek @Override 271572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek public void onAnimationUpdate(ValueAnimator animation) { 272d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi child.setActualHeight((int) animation.getAnimatedValue(), 273d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi false /* notifyListeners */); 274572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek } 275572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek }); 276eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek animator.setInterpolator(mFastOutSlowInInterpolator); 2778df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek long newDuration = cancelAnimatorAndGetNewDuration(previousAnimator); 278eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek animator.setDuration(newDuration); 2798efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek if (delay > 0 && (previousAnimator == null || !previousAnimator.isRunning())) { 2808efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek animator.setStartDelay(delay); 2818efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek } 282eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek animator.addListener(getGlobalAnimationFinishedListener()); 283eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek // remove the tag when the animation is finished 284eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek animator.addListener(new AnimatorListenerAdapter() { 285eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek @Override 286eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek public void onAnimationEnd(Animator animation) { 287eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek child.setTag(TAG_ANIMATOR_HEIGHT, null); 2888df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek child.setTag(TAG_START_HEIGHT, null); 289eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek child.setTag(TAG_END_HEIGHT, null); 290eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek } 291eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek }); 2928efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek startAnimator(animator); 293eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek child.setTag(TAG_ANIMATOR_HEIGHT, animator); 2948df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek child.setTag(TAG_START_HEIGHT, child.getActualHeight()); 2958df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek child.setTag(TAG_END_HEIGHT, newEndValue); 296eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek } 297eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek 298708a6c120da6750d281195ef15a240a5627efed4Selim Cinek private void startInsetAnimation(final ExpandableView child, 299708a6c120da6750d281195ef15a240a5627efed4Selim Cinek StackScrollState.ViewState viewState, long delay) { 300708a6c120da6750d281195ef15a240a5627efed4Selim Cinek Integer previousStartValue = getChildTag(child, TAG_START_TOP_INSET); 301708a6c120da6750d281195ef15a240a5627efed4Selim Cinek Integer previousEndValue = getChildTag(child, TAG_END_TOP_INSET); 302708a6c120da6750d281195ef15a240a5627efed4Selim Cinek int newEndValue = viewState.clipTopAmount; 303708a6c120da6750d281195ef15a240a5627efed4Selim Cinek if (previousEndValue != null && previousEndValue == newEndValue) { 304708a6c120da6750d281195ef15a240a5627efed4Selim Cinek return; 305708a6c120da6750d281195ef15a240a5627efed4Selim Cinek } 306708a6c120da6750d281195ef15a240a5627efed4Selim Cinek ValueAnimator previousAnimator = getChildTag(child, TAG_ANIMATOR_TOP_INSET); 307708a6c120da6750d281195ef15a240a5627efed4Selim Cinek if (!mAnimationFilter.animateTopInset) { 308708a6c120da6750d281195ef15a240a5627efed4Selim Cinek // just a local update was performed 309708a6c120da6750d281195ef15a240a5627efed4Selim Cinek if (previousAnimator != null) { 310708a6c120da6750d281195ef15a240a5627efed4Selim Cinek // we need to increase all animation keyframes of the previous animator by the 311708a6c120da6750d281195ef15a240a5627efed4Selim Cinek // relative change to the end value 312708a6c120da6750d281195ef15a240a5627efed4Selim Cinek PropertyValuesHolder[] values = previousAnimator.getValues(); 313708a6c120da6750d281195ef15a240a5627efed4Selim Cinek int relativeDiff = newEndValue - previousEndValue; 314708a6c120da6750d281195ef15a240a5627efed4Selim Cinek int newStartValue = previousStartValue + relativeDiff; 315708a6c120da6750d281195ef15a240a5627efed4Selim Cinek values[0].setIntValues(newStartValue, newEndValue); 316708a6c120da6750d281195ef15a240a5627efed4Selim Cinek child.setTag(TAG_START_TOP_INSET, newStartValue); 317708a6c120da6750d281195ef15a240a5627efed4Selim Cinek child.setTag(TAG_END_TOP_INSET, newEndValue); 318708a6c120da6750d281195ef15a240a5627efed4Selim Cinek previousAnimator.setCurrentPlayTime(previousAnimator.getCurrentPlayTime()); 319708a6c120da6750d281195ef15a240a5627efed4Selim Cinek return; 320708a6c120da6750d281195ef15a240a5627efed4Selim Cinek } else { 321708a6c120da6750d281195ef15a240a5627efed4Selim Cinek // no new animation needed, let's just apply the value 322708a6c120da6750d281195ef15a240a5627efed4Selim Cinek child.setClipTopAmount(newEndValue); 323708a6c120da6750d281195ef15a240a5627efed4Selim Cinek return; 324708a6c120da6750d281195ef15a240a5627efed4Selim Cinek } 325708a6c120da6750d281195ef15a240a5627efed4Selim Cinek } 326708a6c120da6750d281195ef15a240a5627efed4Selim Cinek 327708a6c120da6750d281195ef15a240a5627efed4Selim Cinek ValueAnimator animator = ValueAnimator.ofInt(child.getClipTopAmount(), newEndValue); 328708a6c120da6750d281195ef15a240a5627efed4Selim Cinek animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { 329708a6c120da6750d281195ef15a240a5627efed4Selim Cinek @Override 330708a6c120da6750d281195ef15a240a5627efed4Selim Cinek public void onAnimationUpdate(ValueAnimator animation) { 331708a6c120da6750d281195ef15a240a5627efed4Selim Cinek child.setClipTopAmount((int) animation.getAnimatedValue()); 332708a6c120da6750d281195ef15a240a5627efed4Selim Cinek } 333708a6c120da6750d281195ef15a240a5627efed4Selim Cinek }); 334708a6c120da6750d281195ef15a240a5627efed4Selim Cinek animator.setInterpolator(mFastOutSlowInInterpolator); 335708a6c120da6750d281195ef15a240a5627efed4Selim Cinek long newDuration = cancelAnimatorAndGetNewDuration(previousAnimator); 336708a6c120da6750d281195ef15a240a5627efed4Selim Cinek animator.setDuration(newDuration); 337708a6c120da6750d281195ef15a240a5627efed4Selim Cinek if (delay > 0 && (previousAnimator == null || !previousAnimator.isRunning())) { 338708a6c120da6750d281195ef15a240a5627efed4Selim Cinek animator.setStartDelay(delay); 339708a6c120da6750d281195ef15a240a5627efed4Selim Cinek } 340708a6c120da6750d281195ef15a240a5627efed4Selim Cinek animator.addListener(getGlobalAnimationFinishedListener()); 341708a6c120da6750d281195ef15a240a5627efed4Selim Cinek // remove the tag when the animation is finished 342708a6c120da6750d281195ef15a240a5627efed4Selim Cinek animator.addListener(new AnimatorListenerAdapter() { 343708a6c120da6750d281195ef15a240a5627efed4Selim Cinek @Override 344708a6c120da6750d281195ef15a240a5627efed4Selim Cinek public void onAnimationEnd(Animator animation) { 345708a6c120da6750d281195ef15a240a5627efed4Selim Cinek child.setTag(TAG_ANIMATOR_TOP_INSET, null); 346708a6c120da6750d281195ef15a240a5627efed4Selim Cinek child.setTag(TAG_START_TOP_INSET, null); 347708a6c120da6750d281195ef15a240a5627efed4Selim Cinek child.setTag(TAG_END_TOP_INSET, null); 348708a6c120da6750d281195ef15a240a5627efed4Selim Cinek } 349708a6c120da6750d281195ef15a240a5627efed4Selim Cinek }); 350708a6c120da6750d281195ef15a240a5627efed4Selim Cinek startAnimator(animator); 351708a6c120da6750d281195ef15a240a5627efed4Selim Cinek child.setTag(TAG_ANIMATOR_TOP_INSET, animator); 352708a6c120da6750d281195ef15a240a5627efed4Selim Cinek child.setTag(TAG_START_TOP_INSET, child.getClipTopAmount()); 353708a6c120da6750d281195ef15a240a5627efed4Selim Cinek child.setTag(TAG_END_TOP_INSET, newEndValue); 354708a6c120da6750d281195ef15a240a5627efed4Selim Cinek } 355708a6c120da6750d281195ef15a240a5627efed4Selim Cinek 356eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek private void startAlphaAnimation(final ExpandableView child, 3578efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek final StackScrollState.ViewState viewState, long delay) { 3588df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek Float previousStartValue = getChildTag(child,TAG_START_ALPHA); 359eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek Float previousEndValue = getChildTag(child,TAG_END_ALPHA); 3608df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek final float newEndValue = viewState.alpha; 3618df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek if (previousEndValue != null && previousEndValue == newEndValue) { 362eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek return; 363eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek } 364eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek ObjectAnimator previousAnimator = getChildTag(child, TAG_ANIMATOR_ALPHA); 3658df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek if (!mAnimationFilter.animateAlpha) { 3668df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek // just a local update was performed 3678df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek if (previousAnimator != null) { 3688df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek // we need to increase all animation keyframes of the previous animator by the 3698df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek // relative change to the end value 3708df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek PropertyValuesHolder[] values = previousAnimator.getValues(); 3718df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek float relativeDiff = newEndValue - previousEndValue; 3728df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek float newStartValue = previousStartValue + relativeDiff; 3738df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek values[0].setFloatValues(newStartValue, newEndValue); 3748df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek child.setTag(TAG_START_ALPHA, newStartValue); 3758df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek child.setTag(TAG_END_ALPHA, newEndValue); 3768df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek previousAnimator.setCurrentPlayTime(previousAnimator.getCurrentPlayTime()); 3778df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek return; 3788df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek } else { 3798df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek // no new animation needed, let's just apply the value 3808df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek child.setAlpha(newEndValue); 3818df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek if (newEndValue == 0) { 3828df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek child.setVisibility(View.INVISIBLE); 3838df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek } 384eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek } 385eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek } 386eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek 387eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek ObjectAnimator animator = ObjectAnimator.ofFloat(child, View.ALPHA, 3888df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek child.getAlpha(), newEndValue); 389eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek animator.setInterpolator(mFastOutSlowInInterpolator); 390eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek // Handle layer type 391eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek child.setLayerType(View.LAYER_TYPE_HARDWARE, null); 392eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek animator.addListener(new AnimatorListenerAdapter() { 393eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek public boolean mWasCancelled; 394eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek 395eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek @Override 396eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek public void onAnimationEnd(Animator animation) { 3971efb240c1a0aeca9492cf8891794712adfdb1fa7Selim Cinek child.setLayerType(View.LAYER_TYPE_NONE, null); 3988df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek if (newEndValue == 0 && !mWasCancelled) { 399eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek child.setVisibility(View.INVISIBLE); 400eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek } 401eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek child.setTag(TAG_ANIMATOR_ALPHA, null); 4028df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek child.setTag(TAG_START_ALPHA, null); 403eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek child.setTag(TAG_END_ALPHA, null); 404eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek } 405eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek 406eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek @Override 407eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek public void onAnimationCancel(Animator animation) { 408eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek mWasCancelled = true; 409eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek } 410eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek 411eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek @Override 412eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek public void onAnimationStart(Animator animation) { 413eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek mWasCancelled = false; 414eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek } 415eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek }); 4168df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek long newDuration = cancelAnimatorAndGetNewDuration(previousAnimator); 4173af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek animator.setDuration(newDuration); 4188efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek if (delay > 0 && (previousAnimator == null || !previousAnimator.isRunning())) { 4198efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek animator.setStartDelay(delay); 4208efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek } 421eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek animator.addListener(getGlobalAnimationFinishedListener()); 422eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek // remove the tag when the animation is finished 423eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek animator.addListener(new AnimatorListenerAdapter() { 424eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek @Override 425eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek public void onAnimationEnd(Animator animation) { 426eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek 427eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek } 428eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek }); 4298efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek startAnimator(animator); 430eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek child.setTag(TAG_ANIMATOR_ALPHA, animator); 4318df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek child.setTag(TAG_START_ALPHA, child.getAlpha()); 4328df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek child.setTag(TAG_END_ALPHA, newEndValue); 433572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek } 434572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek 435eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek private void startZTranslationAnimation(final ExpandableView child, 4368efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek final StackScrollState.ViewState viewState, long delay) { 4378df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek Float previousStartValue = getChildTag(child,TAG_START_TRANSLATION_Z); 438eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek Float previousEndValue = getChildTag(child,TAG_END_TRANSLATION_Z); 4398df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek float newEndValue = viewState.zTranslation; 4408df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek if (previousEndValue != null && previousEndValue == newEndValue) { 441eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek return; 442eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek } 443eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek ObjectAnimator previousAnimator = getChildTag(child, TAG_ANIMATOR_TRANSLATION_Z); 4448df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek if (!mAnimationFilter.animateZ) { 4458df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek // just a local update was performed 4468df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek if (previousAnimator != null) { 4478df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek // we need to increase all animation keyframes of the previous animator by the 4488df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek // relative change to the end value 4498df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek PropertyValuesHolder[] values = previousAnimator.getValues(); 4508df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek float relativeDiff = newEndValue - previousEndValue; 4518df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek float newStartValue = previousStartValue + relativeDiff; 4528df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek values[0].setFloatValues(newStartValue, newEndValue); 4538df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek child.setTag(TAG_START_TRANSLATION_Z, newStartValue); 4548df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek child.setTag(TAG_END_TRANSLATION_Z, newEndValue); 4558df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek previousAnimator.setCurrentPlayTime(previousAnimator.getCurrentPlayTime()); 4568df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek return; 4578df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek } else { 4588df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek // no new animation needed, let's just apply the value 4598df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek child.setTranslationZ(newEndValue); 460eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek } 461eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek } 462eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek 463eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek ObjectAnimator animator = ObjectAnimator.ofFloat(child, View.TRANSLATION_Z, 4648df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek child.getTranslationZ(), newEndValue); 465eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek animator.setInterpolator(mFastOutSlowInInterpolator); 4668df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek long newDuration = cancelAnimatorAndGetNewDuration(previousAnimator); 4673af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek animator.setDuration(newDuration); 4688efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek if (delay > 0 && (previousAnimator == null || !previousAnimator.isRunning())) { 4698efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek animator.setStartDelay(delay); 4708efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek } 471eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek animator.addListener(getGlobalAnimationFinishedListener()); 472eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek // remove the tag when the animation is finished 473eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek animator.addListener(new AnimatorListenerAdapter() { 474eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek @Override 475eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek public void onAnimationEnd(Animator animation) { 476eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek child.setTag(TAG_ANIMATOR_TRANSLATION_Z, null); 4778df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek child.setTag(TAG_START_TRANSLATION_Z, null); 478eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek child.setTag(TAG_END_TRANSLATION_Z, null); 479eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek } 480eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek }); 4818efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek startAnimator(animator); 482eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek child.setTag(TAG_ANIMATOR_TRANSLATION_Z, animator); 4838df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek child.setTag(TAG_START_TRANSLATION_Z, child.getTranslationZ()); 4848df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek child.setTag(TAG_END_TRANSLATION_Z, newEndValue); 485eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek } 486eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek 487eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek private void startYTranslationAnimation(final ExpandableView child, 4888efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek StackScrollState.ViewState viewState, long delay) { 4898df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek Float previousStartValue = getChildTag(child,TAG_START_TRANSLATION_Y); 490eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek Float previousEndValue = getChildTag(child,TAG_END_TRANSLATION_Y); 4918df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek float newEndValue = viewState.yTranslation; 4928df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek if (previousEndValue != null && previousEndValue == newEndValue) { 493eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek return; 494eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek } 495eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek ObjectAnimator previousAnimator = getChildTag(child, TAG_ANIMATOR_TRANSLATION_Y); 4968df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek if (!mAnimationFilter.animateY) { 4978df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek // just a local update was performed 4988df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek if (previousAnimator != null) { 4998df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek // we need to increase all animation keyframes of the previous animator by the 5008df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek // relative change to the end value 5018df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek PropertyValuesHolder[] values = previousAnimator.getValues(); 5028df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek float relativeDiff = newEndValue - previousEndValue; 5038df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek float newStartValue = previousStartValue + relativeDiff; 5048df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek values[0].setFloatValues(newStartValue, newEndValue); 5058df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek child.setTag(TAG_START_TRANSLATION_Y, newStartValue); 5068df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek child.setTag(TAG_END_TRANSLATION_Y, newEndValue); 5078df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek previousAnimator.setCurrentPlayTime(previousAnimator.getCurrentPlayTime()); 5088df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek return; 5098df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek } else { 5108df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek // no new animation needed, let's just apply the value 5118df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek child.setTranslationY(newEndValue); 5128df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek return; 513eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek } 514eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek } 515eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek 516eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek ObjectAnimator animator = ObjectAnimator.ofFloat(child, View.TRANSLATION_Y, 5178df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek child.getTranslationY(), newEndValue); 518eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek animator.setInterpolator(mFastOutSlowInInterpolator); 5198df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek long newDuration = cancelAnimatorAndGetNewDuration(previousAnimator); 5203af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek animator.setDuration(newDuration); 5218efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek if (delay > 0 && (previousAnimator == null || !previousAnimator.isRunning())) { 5228efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek animator.setStartDelay(delay); 5238efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek } 524eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek animator.addListener(getGlobalAnimationFinishedListener()); 525eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek // remove the tag when the animation is finished 526eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek animator.addListener(new AnimatorListenerAdapter() { 527eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek @Override 528eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek public void onAnimationEnd(Animator animation) { 529eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek child.setTag(TAG_ANIMATOR_TRANSLATION_Y, null); 5308df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek child.setTag(TAG_START_TRANSLATION_Y, null); 531eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek child.setTag(TAG_END_TRANSLATION_Y, null); 532eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek } 533eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek }); 5348efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek startAnimator(animator); 535eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek child.setTag(TAG_ANIMATOR_TRANSLATION_Y, animator); 5368df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek child.setTag(TAG_START_TRANSLATION_Y, child.getTranslationY()); 5378df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek child.setTag(TAG_END_TRANSLATION_Y, newEndValue); 538eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek } 539eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek 540d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi private void startScaleAnimation(final ExpandableView child, 541d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi StackScrollState.ViewState viewState) { 5428df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek Float previousStartValue = getChildTag(child, TAG_START_SCALE); 543d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi Float previousEndValue = getChildTag(child, TAG_END_SCALE); 5448df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek float newEndValue = viewState.scale; 5458df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek if (previousEndValue != null && previousEndValue == newEndValue) { 546d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi return; 547d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi } 548d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi ObjectAnimator previousAnimator = getChildTag(child, TAG_ANIMATOR_SCALE); 5498df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek if (!mAnimationFilter.animateScale) { 5508df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek // just a local update was performed 5518df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek if (previousAnimator != null) { 5528df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek // we need to increase all animation keyframes of the previous animator by the 5538df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek // relative change to the end value 5548df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek PropertyValuesHolder[] values = previousAnimator.getValues(); 5558df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek float relativeDiff = newEndValue - previousEndValue; 5568df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek float newStartValue = previousStartValue + relativeDiff; 5578df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek values[0].setFloatValues(newStartValue, newEndValue); 5588df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek values[1].setFloatValues(newStartValue, newEndValue); 5598df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek child.setTag(TAG_START_SCALE, newStartValue); 5608df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek child.setTag(TAG_END_SCALE, newEndValue); 5618df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek previousAnimator.setCurrentPlayTime(previousAnimator.getCurrentPlayTime()); 5628df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek return; 5638df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek } else { 5648df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek // no new animation needed, let's just apply the value 5658df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek child.setScaleX(newEndValue); 5668df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek child.setScaleY(newEndValue); 567d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi } 568d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi } 569d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi 570d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi PropertyValuesHolder holderX = 5718df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek PropertyValuesHolder.ofFloat(View.SCALE_X, child.getScaleX(), newEndValue); 572d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi PropertyValuesHolder holderY = 5738df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek PropertyValuesHolder.ofFloat(View.SCALE_Y, child.getScaleY(), newEndValue); 574d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi ObjectAnimator animator = ObjectAnimator.ofPropertyValuesHolder(child, holderX, holderY); 575d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi animator.setInterpolator(mFastOutSlowInInterpolator); 5768df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek long newDuration = cancelAnimatorAndGetNewDuration(previousAnimator); 577d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi animator.setDuration(newDuration); 578d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi animator.addListener(getGlobalAnimationFinishedListener()); 579d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi // remove the tag when the animation is finished 580d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi animator.addListener(new AnimatorListenerAdapter() { 581d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi @Override 582d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi public void onAnimationEnd(Animator animation) { 583d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi child.setTag(TAG_ANIMATOR_SCALE, null); 5848df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek child.setTag(TAG_START_SCALE, null); 585d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi child.setTag(TAG_END_SCALE, null); 586d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi } 587d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi }); 5888efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek startAnimator(animator); 589d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi child.setTag(TAG_ANIMATOR_SCALE, animator); 5908df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek child.setTag(TAG_START_SCALE, child.getScaleX()); 5918df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek child.setTag(TAG_END_SCALE, newEndValue); 592d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi } 593d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi 5948efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek private void startAnimator(ValueAnimator animator) { 5958efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek mAnimatorSet.add(animator); 5963af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek animator.start(); 5973af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek } 5983af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek 5993af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek /** 6003af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek * @return an adapter which ensures that onAnimationFinished is called once no animation is 6013af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek * running anymore 6023af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek */ 6033af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek private AnimatorListenerAdapter getGlobalAnimationFinishedListener() { 6043af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek if (!mAnimationListenerPool.empty()) { 6053af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek return mAnimationListenerPool.pop(); 6063af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek } 6073af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek 6083af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek // We need to create a new one, no reusable ones found 6093af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek return new AnimatorListenerAdapter() { 6103af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek private boolean mWasCancelled; 6113af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek 6123af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek @Override 6133af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek public void onAnimationEnd(Animator animation) { 6143af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek mAnimatorSet.remove(animation); 6153af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek if (mAnimatorSet.isEmpty() && !mWasCancelled) { 6163af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek onAnimationFinished(); 6173af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek } 6183af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek mAnimationListenerPool.push(this); 6193af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek } 6203af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek 6213af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek @Override 6223af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek public void onAnimationCancel(Animator animation) { 6233af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek mWasCancelled = true; 6243af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek } 6253af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek 6263af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek @Override 6273af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek public void onAnimationStart(Animator animation) { 6283af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek mWasCancelled = false; 6293af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek } 6303af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek }; 6313af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek } 6323af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek 633eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek private <T> T getChildTag(View child, int tag) { 634eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek return (T) child.getTag(tag); 635eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek } 636eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek 637eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek /** 638eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek * Cancel the previous animator and get the duration of the new animation. 63939610545f0c2714a3526bc935effe57b421542d1Selim Cinek * 640eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek * @param previousAnimator the animator which was running before 641eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek * @return the new duration 642eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek */ 6438df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek private long cancelAnimatorAndGetNewDuration(ValueAnimator previousAnimator) { 6445aa045cc6bca84f5c11f1a99999546ba5e5949a5Jorim Jaggi long newDuration = mCurrentLength; 645eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek if (previousAnimator != null) { 6468df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek // We take either the desired length of the new animation or the remaining time of 6478df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek // the previous animator, whichever is longer. 6488df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek newDuration = Math.max(previousAnimator.getDuration() 6498df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek - previousAnimator.getCurrentPlayTime(), newDuration); 6503af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek previousAnimator.cancel(); 651eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek } 6523af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek return newDuration; 653eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek } 654eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek 655eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek private void onAnimationFinished() { 656eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek mHostLayout.onChildAnimationFinished(); 657eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek } 658eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek 659eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek /** 660eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek * Process the animationEvents for a new animation 661eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek * 662eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek * @param animationEvents the animation events for the animation to perform 66339610545f0c2714a3526bc935effe57b421542d1Selim Cinek * @param finalState the final state to animate to 66439610545f0c2714a3526bc935effe57b421542d1Selim Cinek */ 665eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek private void processAnimationEvents( 6660dd6881ea481c855976214807c17595b34a2920aJorim Jaggi ArrayList<NotificationStackScrollLayout.AnimationEvent> animationEvents, 667572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek StackScrollState finalState) { 6688d9ff9c2c66bc1d3b92eb6992d58599ff80ed6dcSelim Cinek for (NotificationStackScrollLayout.AnimationEvent event : animationEvents) { 6698efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek final ExpandableView changingView = (ExpandableView) event.changingView; 6708efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek if (event.animationType == 6718efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek NotificationStackScrollLayout.AnimationEvent.ANIMATION_TYPE_ADD) { 6728efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek 6738efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek // This item is added, initialize it's properties. 6748efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek StackScrollState.ViewState viewState = finalState 6758efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek .getViewStateForView(changingView); 6768efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek if (viewState == null) { 6778efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek // The position for this child was never generated, let's continue. 6788efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek continue; 6798efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek } 6808efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek if (changingView.getVisibility() == View.GONE) { 6818efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek // The view was set to gone but the state never removed 6828efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek finalState.removeViewStateForView(changingView); 6838efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek continue; 68439610545f0c2714a3526bc935effe57b421542d1Selim Cinek } 6858efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek changingView.setAlpha(viewState.alpha); 6868efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek changingView.setTranslationY(viewState.yTranslation); 6878efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek changingView.setTranslationZ(viewState.zTranslation); 6888efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek changingView.setActualHeight(viewState.height, false); 6898efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek mNewAddChildren.add(changingView); 6908efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek 6918efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek } else if (event.animationType == 6928efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek NotificationStackScrollLayout.AnimationEvent.ANIMATION_TYPE_REMOVE) { 6938efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek if (changingView.getVisibility() == View.GONE) { 6948efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek continue; 6958efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek } 6968efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek 6978efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek // Find the amount to translate up. This is needed in order to understand the 6988efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek // direction of the remove animation (either downwards or upwards) 6998efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek StackScrollState.ViewState viewState = finalState 7008efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek .getViewStateForView(event.viewAfterChangingView); 7018efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek int actualHeight = changingView.getActualHeight(); 7028efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek // upwards by default 7038efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek float translationDirection = -1.0f; 7048efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek if (viewState != null) { 7058efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek // there was a view after this one, Approximate the distance the next child 7068efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek // travelled 7078efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek translationDirection = ((viewState.yTranslation 7088efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek - (changingView.getTranslationY() + actualHeight / 2.0f)) * 2 / 7098efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek actualHeight); 7108efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek translationDirection = Math.max(Math.min(translationDirection, 1.0f),-1.0f); 7118efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek 7128efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek } 7138efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek changingView.performRemoveAnimation(translationDirection, new Runnable() { 7148efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek @Override 7158efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek public void run() { 7168efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek // remove the temporary overlay 7178efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek mHostLayout.getOverlay().remove(changingView); 7188efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek } 7198efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek }); 720572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek } 7218efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek mNewEvents.add(event); 722572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek } 723572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek } 7248d9ff9c2c66bc1d3b92eb6992d58599ff80ed6dcSelim Cinek 725475b21dfe517ec04f435f6b02f4a53083d040db4Jorim Jaggi public void animateOverScrollToAmount(float targetAmount, final boolean onTop, 726475b21dfe517ec04f435f6b02f4a53083d040db4Jorim Jaggi final boolean isRubberbanded) { 7278d9ff9c2c66bc1d3b92eb6992d58599ff80ed6dcSelim Cinek final float startOverScrollAmount = mHostLayout.getCurrentOverScrollAmount(onTop); 7289012958742c7a66b37ba5f2196f9086bb1980e6bJorim Jaggi if (targetAmount == startOverScrollAmount) { 7299012958742c7a66b37ba5f2196f9086bb1980e6bJorim Jaggi return; 7309012958742c7a66b37ba5f2196f9086bb1980e6bJorim Jaggi } 7318d9ff9c2c66bc1d3b92eb6992d58599ff80ed6dcSelim Cinek cancelOverScrollAnimators(onTop); 7328d9ff9c2c66bc1d3b92eb6992d58599ff80ed6dcSelim Cinek ValueAnimator overScrollAnimator = ValueAnimator.ofFloat(startOverScrollAmount, 7338d9ff9c2c66bc1d3b92eb6992d58599ff80ed6dcSelim Cinek targetAmount); 7348d9ff9c2c66bc1d3b92eb6992d58599ff80ed6dcSelim Cinek overScrollAnimator.setDuration(ANIMATION_DURATION_STANDARD); 7358d9ff9c2c66bc1d3b92eb6992d58599ff80ed6dcSelim Cinek overScrollAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { 7368d9ff9c2c66bc1d3b92eb6992d58599ff80ed6dcSelim Cinek @Override 7378d9ff9c2c66bc1d3b92eb6992d58599ff80ed6dcSelim Cinek public void onAnimationUpdate(ValueAnimator animation) { 7388d9ff9c2c66bc1d3b92eb6992d58599ff80ed6dcSelim Cinek float currentOverScroll = (float) animation.getAnimatedValue(); 73947c85a3525dcd0bbd3168632830e8ab491d18462Jorim Jaggi mHostLayout.setOverScrollAmount( 740475b21dfe517ec04f435f6b02f4a53083d040db4Jorim Jaggi currentOverScroll, onTop, false /* animate */, false /* cancelAnimators */, 741475b21dfe517ec04f435f6b02f4a53083d040db4Jorim Jaggi isRubberbanded); 7428d9ff9c2c66bc1d3b92eb6992d58599ff80ed6dcSelim Cinek } 7438d9ff9c2c66bc1d3b92eb6992d58599ff80ed6dcSelim Cinek }); 7448d9ff9c2c66bc1d3b92eb6992d58599ff80ed6dcSelim Cinek overScrollAnimator.setInterpolator(mFastOutSlowInInterpolator); 74547c85a3525dcd0bbd3168632830e8ab491d18462Jorim Jaggi overScrollAnimator.addListener(new AnimatorListenerAdapter() { 74647c85a3525dcd0bbd3168632830e8ab491d18462Jorim Jaggi @Override 74747c85a3525dcd0bbd3168632830e8ab491d18462Jorim Jaggi public void onAnimationEnd(Animator animation) { 74847c85a3525dcd0bbd3168632830e8ab491d18462Jorim Jaggi if (onTop) { 74947c85a3525dcd0bbd3168632830e8ab491d18462Jorim Jaggi mTopOverScrollAnimator = null; 75047c85a3525dcd0bbd3168632830e8ab491d18462Jorim Jaggi } else { 75147c85a3525dcd0bbd3168632830e8ab491d18462Jorim Jaggi mBottomOverScrollAnimator = null; 75247c85a3525dcd0bbd3168632830e8ab491d18462Jorim Jaggi } 75347c85a3525dcd0bbd3168632830e8ab491d18462Jorim Jaggi } 75447c85a3525dcd0bbd3168632830e8ab491d18462Jorim Jaggi }); 7558d9ff9c2c66bc1d3b92eb6992d58599ff80ed6dcSelim Cinek overScrollAnimator.start(); 7568d9ff9c2c66bc1d3b92eb6992d58599ff80ed6dcSelim Cinek if (onTop) { 7578d9ff9c2c66bc1d3b92eb6992d58599ff80ed6dcSelim Cinek mTopOverScrollAnimator = overScrollAnimator; 7588d9ff9c2c66bc1d3b92eb6992d58599ff80ed6dcSelim Cinek } else { 7598d9ff9c2c66bc1d3b92eb6992d58599ff80ed6dcSelim Cinek mBottomOverScrollAnimator = overScrollAnimator; 7608d9ff9c2c66bc1d3b92eb6992d58599ff80ed6dcSelim Cinek } 7618d9ff9c2c66bc1d3b92eb6992d58599ff80ed6dcSelim Cinek } 7628d9ff9c2c66bc1d3b92eb6992d58599ff80ed6dcSelim Cinek 7638d9ff9c2c66bc1d3b92eb6992d58599ff80ed6dcSelim Cinek public void cancelOverScrollAnimators(boolean onTop) { 7648d9ff9c2c66bc1d3b92eb6992d58599ff80ed6dcSelim Cinek ValueAnimator currentAnimator = onTop ? mTopOverScrollAnimator : mBottomOverScrollAnimator; 7658d9ff9c2c66bc1d3b92eb6992d58599ff80ed6dcSelim Cinek if (currentAnimator != null) { 7668d9ff9c2c66bc1d3b92eb6992d58599ff80ed6dcSelim Cinek currentAnimator.cancel(); 7678d9ff9c2c66bc1d3b92eb6992d58599ff80ed6dcSelim Cinek } 7688d9ff9c2c66bc1d3b92eb6992d58599ff80ed6dcSelim Cinek } 769572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek} 770