StackStateAnimator.java revision 8df56452cb696ebdee82df6fb255892eabf3febc
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; 21d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggiimport android.animation.AnimatorSet; 22eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinekimport android.animation.ObjectAnimator; 23d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggiimport android.animation.PropertyValuesHolder; 24572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinekimport android.animation.ValueAnimator; 25572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinekimport android.view.View; 26572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinekimport android.view.animation.AnimationUtils; 27572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinekimport android.view.animation.Interpolator; 28eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek 29eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinekimport com.android.systemui.R; 30572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinekimport com.android.systemui.statusbar.ExpandableView; 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; 435aa045cc6bca84f5c11f1a99999546ba5e5949a5Jorim Jaggi public static final int ANIMATION_DURATION_DIMMED_ACTIVATED = 220; 445aa045cc6bca84f5c11f1a99999546ba5e5949a5Jorim Jaggi 45eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek private static final int TAG_ANIMATOR_TRANSLATION_Y = R.id.translation_y_animator_tag; 46eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek private static final int TAG_ANIMATOR_TRANSLATION_Z = R.id.translation_z_animator_tag; 47d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi private static final int TAG_ANIMATOR_SCALE = R.id.scale_animator_tag; 48eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek private static final int TAG_ANIMATOR_ALPHA = R.id.alpha_animator_tag; 49eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek private static final int TAG_ANIMATOR_HEIGHT = R.id.height_animator_tag; 50eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek private static final int TAG_ANIMATOR_TOP_INSET = R.id.top_inset_animator_tag; 51eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek private static final int TAG_END_TRANSLATION_Y = R.id.translation_y_animator_end_value_tag; 52eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek private static final int TAG_END_TRANSLATION_Z = R.id.translation_z_animator_end_value_tag; 53d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi private static final int TAG_END_SCALE = R.id.scale_animator_end_value_tag; 54eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek private static final int TAG_END_ALPHA = R.id.alpha_animator_end_value_tag; 55eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek private static final int TAG_END_HEIGHT = R.id.height_animator_end_value_tag; 56eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek private static final int TAG_END_TOP_INSET = R.id.top_inset_animator_end_value_tag; 578df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek private static final int TAG_START_TRANSLATION_Y = R.id.translation_y_animator_start_value_tag; 588df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek private static final int TAG_START_TRANSLATION_Z = R.id.translation_z_animator_start_value_tag; 598df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek private static final int TAG_START_SCALE = R.id.scale_animator_start_value_tag; 608df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek private static final int TAG_START_ALPHA = R.id.alpha_animator_start_value_tag; 618df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek private static final int TAG_START_HEIGHT = R.id.height_animator_start_value_tag; 628df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek private static final int TAG_START_TOP_INSET = R.id.top_inset_animator_start_value_tag; 63572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek 64572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek private final Interpolator mFastOutSlowInInterpolator; 65572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek public NotificationStackScrollLayout mHostLayout; 660dd6881ea481c855976214807c17595b34a2920aJorim Jaggi private ArrayList<NotificationStackScrollLayout.AnimationEvent> mHandledEvents = 67572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek new ArrayList<>(); 68eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek private ArrayList<NotificationStackScrollLayout.AnimationEvent> mNewEvents = 69eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek new ArrayList<>(); 70eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek private Set<Animator> mAnimatorSet = new HashSet<Animator>(); 71eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek private Stack<AnimatorListenerAdapter> mAnimationListenerPool 72eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek = new Stack<AnimatorListenerAdapter>(); 73d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi private AnimationFilter mAnimationFilter = new AnimationFilter(); 745aa045cc6bca84f5c11f1a99999546ba5e5949a5Jorim Jaggi private long mCurrentLength; 75572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek 76572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek public StackStateAnimator(NotificationStackScrollLayout hostLayout) { 77572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek mHostLayout = hostLayout; 78572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek mFastOutSlowInInterpolator = AnimationUtils.loadInterpolator(hostLayout.getContext(), 79eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek android.R.interpolator.fast_out_slow_in); 80572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek } 81572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek 82572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek public boolean isRunning() { 83eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek return !mAnimatorSet.isEmpty(); 84572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek } 85572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek 86572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek public void startAnimationForEvents( 870dd6881ea481c855976214807c17595b34a2920aJorim Jaggi ArrayList<NotificationStackScrollLayout.AnimationEvent> mAnimationEvents, 88572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek StackScrollState finalState) { 89eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek 90eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek processAnimationEvents(mAnimationEvents, finalState); 91eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek 92572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek int childCount = mHostLayout.getChildCount(); 93d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi mAnimationFilter.applyCombination(mNewEvents); 945aa045cc6bca84f5c11f1a99999546ba5e5949a5Jorim Jaggi mCurrentLength = NotificationStackScrollLayout.AnimationEvent.combineLength(mNewEvents); 95572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek for (int i = 0; i < childCount; i++) { 96572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek final ExpandableView child = (ExpandableView) mHostLayout.getChildAt(i); 97572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek StackScrollState.ViewState viewState = finalState.getViewStateForView(child); 98572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek if (viewState == null) { 99572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek continue; 100572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek } 101572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek 102d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi startAnimations(child, viewState); 103572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek 104572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek child.setClipBounds(null); 105572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek } 106eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek if (!isRunning()) { 107eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek // no child has preformed any animation, lets finish 108eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek onAnimationFinished(); 109eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek } 110572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek } 111572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek 112eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek /** 113eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek * Start an animation to the given viewState 114eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek */ 115d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi private void startAnimations(final ExpandableView child, StackScrollState.ViewState viewState) { 116eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek int childVisibility = child.getVisibility(); 117eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek boolean wasVisible = childVisibility == View.VISIBLE; 118eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek final float alpha = viewState.alpha; 119eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek if (!wasVisible && alpha != 0 && !viewState.gone) { 120eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek child.setVisibility(View.VISIBLE); 121eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek } 122eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek // start translationY animation 123eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek if (child.getTranslationY() != viewState.yTranslation) { 124d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi startYTranslationAnimation(child, viewState); 125eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek } 126eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek // start translationZ animation 127eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek if (child.getTranslationZ() != viewState.zTranslation) { 128d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi startZTranslationAnimation(child, viewState); 129d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi } 130d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi // start scale animation 131d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi if (child.getScaleX() != viewState.scale) { 132d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi startScaleAnimation(child, viewState); 133eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek } 134eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek // start alpha animation 13559b5a356b828fe60ea2874b0680a1bf7c84809a1Jorim Jaggi if (alpha != child.getAlpha()) { 136d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi startAlphaAnimation(child, viewState); 137eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek } 138eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek // start height animation 139eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek if (viewState.height != child.getActualHeight()) { 140d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi startHeightAnimation(child, viewState); 14159b5a356b828fe60ea2874b0680a1bf7c84809a1Jorim Jaggi } 142d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi // start dimmed animation 143d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi child.setDimmed(viewState.dimmed, mAnimationFilter.animateDimmed); 144572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek } 145572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek 146eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek private void startHeightAnimation(final ExpandableView child, 147d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi StackScrollState.ViewState viewState) { 1488df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek Integer previousStartValue = getChildTag(child, TAG_START_HEIGHT); 149d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi Integer previousEndValue = getChildTag(child, TAG_END_HEIGHT); 1508df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek int newEndValue = viewState.height; 1518df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek if (previousEndValue != null && previousEndValue == newEndValue) { 152eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek return; 153eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek } 154eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek ValueAnimator previousAnimator = getChildTag(child, TAG_ANIMATOR_HEIGHT); 1558df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek if (!mAnimationFilter.animateHeight) { 1568df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek // just a local update was performed 1578df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek if (previousAnimator != null) { 1588df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek // we need to increase all animation keyframes of the previous animator by the 1598df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek // relative change to the end value 1608df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek PropertyValuesHolder[] values = previousAnimator.getValues(); 1618df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek int relativeDiff = newEndValue - previousEndValue; 1628df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek int newStartValue = previousStartValue + relativeDiff; 1638df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek values[0].setIntValues(newStartValue, newEndValue); 1648df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek child.setTag(TAG_START_HEIGHT, newStartValue); 1658df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek child.setTag(TAG_END_HEIGHT, newEndValue); 1668df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek previousAnimator.setCurrentPlayTime(previousAnimator.getCurrentPlayTime()); 1678df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek return; 1688df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek } else { 1698df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek // no new animation needed, let's just apply the value 1708df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek child.setActualHeight(newEndValue, false); 1718df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek return; 172eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek } 173eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek } 174eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek 1758df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek ValueAnimator animator = ValueAnimator.ofInt(child.getActualHeight(), newEndValue); 176eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { 177572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek @Override 178572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek public void onAnimationUpdate(ValueAnimator animation) { 179d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi child.setActualHeight((int) animation.getAnimatedValue(), 180d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi false /* notifyListeners */); 181572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek } 182572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek }); 183eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek animator.setInterpolator(mFastOutSlowInInterpolator); 1848df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek long newDuration = cancelAnimatorAndGetNewDuration(previousAnimator); 185eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek animator.setDuration(newDuration); 186eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek animator.addListener(getGlobalAnimationFinishedListener()); 187eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek // remove the tag when the animation is finished 188eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek animator.addListener(new AnimatorListenerAdapter() { 189eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek @Override 190eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek public void onAnimationEnd(Animator animation) { 191eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek child.setTag(TAG_ANIMATOR_HEIGHT, null); 1928df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek child.setTag(TAG_START_HEIGHT, null); 193eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek child.setTag(TAG_END_HEIGHT, null); 194eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek } 195eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek }); 1963af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek startInstantly(animator); 197eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek child.setTag(TAG_ANIMATOR_HEIGHT, animator); 1988df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek child.setTag(TAG_START_HEIGHT, child.getActualHeight()); 1998df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek child.setTag(TAG_END_HEIGHT, newEndValue); 200eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek } 201eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek 202eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek private void startAlphaAnimation(final ExpandableView child, 203d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi final StackScrollState.ViewState viewState) { 2048df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek Float previousStartValue = getChildTag(child,TAG_START_ALPHA); 205eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek Float previousEndValue = getChildTag(child,TAG_END_ALPHA); 2068df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek final float newEndValue = viewState.alpha; 2078df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek if (previousEndValue != null && previousEndValue == newEndValue) { 208eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek return; 209eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek } 210eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek ObjectAnimator previousAnimator = getChildTag(child, TAG_ANIMATOR_ALPHA); 2118df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek if (!mAnimationFilter.animateAlpha) { 2128df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek // just a local update was performed 2138df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek if (previousAnimator != null) { 2148df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek // we need to increase all animation keyframes of the previous animator by the 2158df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek // relative change to the end value 2168df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek PropertyValuesHolder[] values = previousAnimator.getValues(); 2178df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek float relativeDiff = newEndValue - previousEndValue; 2188df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek float newStartValue = previousStartValue + relativeDiff; 2198df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek values[0].setFloatValues(newStartValue, newEndValue); 2208df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek child.setTag(TAG_START_ALPHA, newStartValue); 2218df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek child.setTag(TAG_END_ALPHA, newEndValue); 2228df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek previousAnimator.setCurrentPlayTime(previousAnimator.getCurrentPlayTime()); 2238df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek return; 2248df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek } else { 2258df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek // no new animation needed, let's just apply the value 2268df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek child.setAlpha(newEndValue); 2278df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek if (newEndValue == 0) { 2288df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek child.setVisibility(View.INVISIBLE); 2298df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek } 230eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek } 231eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek } 232eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek 233eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek ObjectAnimator animator = ObjectAnimator.ofFloat(child, View.ALPHA, 2348df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek child.getAlpha(), newEndValue); 235eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek animator.setInterpolator(mFastOutSlowInInterpolator); 236eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek // Handle layer type 237eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek final int currentLayerType = child.getLayerType(); 238eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek child.setLayerType(View.LAYER_TYPE_HARDWARE, null); 239eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek animator.addListener(new AnimatorListenerAdapter() { 240eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek public boolean mWasCancelled; 241eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek 242eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek @Override 243eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek public void onAnimationEnd(Animator animation) { 244eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek child.setLayerType(currentLayerType, null); 2458df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek if (newEndValue == 0 && !mWasCancelled) { 246eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek child.setVisibility(View.INVISIBLE); 247eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek } 248eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek child.setTag(TAG_ANIMATOR_ALPHA, null); 2498df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek child.setTag(TAG_START_ALPHA, null); 250eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek child.setTag(TAG_END_ALPHA, null); 251eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek } 252eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek 253eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek @Override 254eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek public void onAnimationCancel(Animator animation) { 255eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek mWasCancelled = true; 256eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek } 257eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek 258eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek @Override 259eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek public void onAnimationStart(Animator animation) { 260eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek mWasCancelled = false; 261eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek } 262eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek }); 2638df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek long newDuration = cancelAnimatorAndGetNewDuration(previousAnimator); 2643af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek animator.setDuration(newDuration); 265eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek animator.addListener(getGlobalAnimationFinishedListener()); 266eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek // remove the tag when the animation is finished 267eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek animator.addListener(new AnimatorListenerAdapter() { 268eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek @Override 269eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek public void onAnimationEnd(Animator animation) { 270eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek 271eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek } 272eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek }); 2733af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek startInstantly(animator); 274eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek child.setTag(TAG_ANIMATOR_ALPHA, animator); 2758df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek child.setTag(TAG_START_ALPHA, child.getAlpha()); 2768df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek child.setTag(TAG_END_ALPHA, newEndValue); 277572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek } 278572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek 279eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek private void startZTranslationAnimation(final ExpandableView child, 280d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi final StackScrollState.ViewState viewState) { 2818df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek Float previousStartValue = getChildTag(child,TAG_START_TRANSLATION_Z); 282eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek Float previousEndValue = getChildTag(child,TAG_END_TRANSLATION_Z); 2838df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek float newEndValue = viewState.zTranslation; 2848df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek if (previousEndValue != null && previousEndValue == newEndValue) { 285eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek return; 286eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek } 287eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek ObjectAnimator previousAnimator = getChildTag(child, TAG_ANIMATOR_TRANSLATION_Z); 2888df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek if (!mAnimationFilter.animateZ) { 2898df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek // just a local update was performed 2908df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek if (previousAnimator != null) { 2918df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek // we need to increase all animation keyframes of the previous animator by the 2928df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek // relative change to the end value 2938df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek PropertyValuesHolder[] values = previousAnimator.getValues(); 2948df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek float relativeDiff = newEndValue - previousEndValue; 2958df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek float newStartValue = previousStartValue + relativeDiff; 2968df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek values[0].setFloatValues(newStartValue, newEndValue); 2978df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek child.setTag(TAG_START_TRANSLATION_Z, newStartValue); 2988df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek child.setTag(TAG_END_TRANSLATION_Z, newEndValue); 2998df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek previousAnimator.setCurrentPlayTime(previousAnimator.getCurrentPlayTime()); 3008df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek return; 3018df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek } else { 3028df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek // no new animation needed, let's just apply the value 3038df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek child.setTranslationZ(newEndValue); 304eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek } 305eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek } 306eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek 307eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek ObjectAnimator animator = ObjectAnimator.ofFloat(child, View.TRANSLATION_Z, 3088df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek child.getTranslationZ(), newEndValue); 309eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek animator.setInterpolator(mFastOutSlowInInterpolator); 3108df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek long newDuration = cancelAnimatorAndGetNewDuration(previousAnimator); 3113af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek animator.setDuration(newDuration); 312eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek animator.addListener(getGlobalAnimationFinishedListener()); 313eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek // remove the tag when the animation is finished 314eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek animator.addListener(new AnimatorListenerAdapter() { 315eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek @Override 316eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek public void onAnimationEnd(Animator animation) { 317eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek child.setTag(TAG_ANIMATOR_TRANSLATION_Z, null); 3188df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek child.setTag(TAG_START_TRANSLATION_Z, null); 319eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek child.setTag(TAG_END_TRANSLATION_Z, null); 320eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek } 321eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek }); 3223af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek startInstantly(animator); 323eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek child.setTag(TAG_ANIMATOR_TRANSLATION_Z, animator); 3248df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek child.setTag(TAG_START_TRANSLATION_Z, child.getTranslationZ()); 3258df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek child.setTag(TAG_END_TRANSLATION_Z, newEndValue); 326eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek } 327eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek 328eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek private void startYTranslationAnimation(final ExpandableView child, 329d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi StackScrollState.ViewState viewState) { 3308df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek Float previousStartValue = getChildTag(child,TAG_START_TRANSLATION_Y); 331eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek Float previousEndValue = getChildTag(child,TAG_END_TRANSLATION_Y); 3328df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek float newEndValue = viewState.yTranslation; 3338df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek if (previousEndValue != null && previousEndValue == newEndValue) { 334eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek return; 335eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek } 336eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek ObjectAnimator previousAnimator = getChildTag(child, TAG_ANIMATOR_TRANSLATION_Y); 3378df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek if (!mAnimationFilter.animateY) { 3388df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek // just a local update was performed 3398df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek if (previousAnimator != null) { 3408df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek // we need to increase all animation keyframes of the previous animator by the 3418df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek // relative change to the end value 3428df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek PropertyValuesHolder[] values = previousAnimator.getValues(); 3438df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek float relativeDiff = newEndValue - previousEndValue; 3448df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek float newStartValue = previousStartValue + relativeDiff; 3458df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek values[0].setFloatValues(newStartValue, newEndValue); 3468df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek child.setTag(TAG_START_TRANSLATION_Y, newStartValue); 3478df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek child.setTag(TAG_END_TRANSLATION_Y, newEndValue); 3488df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek previousAnimator.setCurrentPlayTime(previousAnimator.getCurrentPlayTime()); 3498df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek return; 3508df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek } else { 3518df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek // no new animation needed, let's just apply the value 3528df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek child.setTranslationY(newEndValue); 3538df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek return; 354eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek } 355eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek } 356eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek 357eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek ObjectAnimator animator = ObjectAnimator.ofFloat(child, View.TRANSLATION_Y, 3588df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek child.getTranslationY(), newEndValue); 359eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek animator.setInterpolator(mFastOutSlowInInterpolator); 3608df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek long newDuration = cancelAnimatorAndGetNewDuration(previousAnimator); 3613af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek animator.setDuration(newDuration); 362eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek animator.addListener(getGlobalAnimationFinishedListener()); 363eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek // remove the tag when the animation is finished 364eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek animator.addListener(new AnimatorListenerAdapter() { 365eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek @Override 366eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek public void onAnimationEnd(Animator animation) { 367eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek child.setTag(TAG_ANIMATOR_TRANSLATION_Y, null); 3688df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek child.setTag(TAG_START_TRANSLATION_Y, null); 369eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek child.setTag(TAG_END_TRANSLATION_Y, null); 370eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek } 371eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek }); 3723af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek startInstantly(animator); 373eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek child.setTag(TAG_ANIMATOR_TRANSLATION_Y, animator); 3748df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek child.setTag(TAG_START_TRANSLATION_Y, child.getTranslationY()); 3758df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek child.setTag(TAG_END_TRANSLATION_Y, newEndValue); 376eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek } 377eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek 378d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi private void startScaleAnimation(final ExpandableView child, 379d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi StackScrollState.ViewState viewState) { 3808df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek Float previousStartValue = getChildTag(child, TAG_START_SCALE); 381d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi Float previousEndValue = getChildTag(child, TAG_END_SCALE); 3828df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek float newEndValue = viewState.scale; 3838df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek if (previousEndValue != null && previousEndValue == newEndValue) { 384d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi return; 385d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi } 386d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi ObjectAnimator previousAnimator = getChildTag(child, TAG_ANIMATOR_SCALE); 3878df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek if (!mAnimationFilter.animateScale) { 3888df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek // just a local update was performed 3898df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek if (previousAnimator != null) { 3908df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek // we need to increase all animation keyframes of the previous animator by the 3918df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek // relative change to the end value 3928df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek PropertyValuesHolder[] values = previousAnimator.getValues(); 3938df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek float relativeDiff = newEndValue - previousEndValue; 3948df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek float newStartValue = previousStartValue + relativeDiff; 3958df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek values[0].setFloatValues(newStartValue, newEndValue); 3968df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek values[1].setFloatValues(newStartValue, newEndValue); 3978df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek child.setTag(TAG_START_SCALE, newStartValue); 3988df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek child.setTag(TAG_END_SCALE, newEndValue); 3998df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek previousAnimator.setCurrentPlayTime(previousAnimator.getCurrentPlayTime()); 4008df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek return; 4018df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek } else { 4028df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek // no new animation needed, let's just apply the value 4038df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek child.setScaleX(newEndValue); 4048df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek child.setScaleY(newEndValue); 405d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi } 406d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi } 407d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi 408d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi PropertyValuesHolder holderX = 4098df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek PropertyValuesHolder.ofFloat(View.SCALE_X, child.getScaleX(), newEndValue); 410d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi PropertyValuesHolder holderY = 4118df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek PropertyValuesHolder.ofFloat(View.SCALE_Y, child.getScaleY(), newEndValue); 412d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi ObjectAnimator animator = ObjectAnimator.ofPropertyValuesHolder(child, holderX, holderY); 413d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi animator.setInterpolator(mFastOutSlowInInterpolator); 4148df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek long newDuration = cancelAnimatorAndGetNewDuration(previousAnimator); 415d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi animator.setDuration(newDuration); 416d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi animator.addListener(getGlobalAnimationFinishedListener()); 417d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi // remove the tag when the animation is finished 418d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi animator.addListener(new AnimatorListenerAdapter() { 419d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi @Override 420d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi public void onAnimationEnd(Animator animation) { 421d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi child.setTag(TAG_ANIMATOR_SCALE, null); 4228df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek child.setTag(TAG_START_SCALE, null); 423d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi child.setTag(TAG_END_SCALE, null); 424d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi } 425d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi }); 426d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi startInstantly(animator); 427d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi child.setTag(TAG_ANIMATOR_SCALE, animator); 4288df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek child.setTag(TAG_START_SCALE, child.getScaleX()); 4298df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek child.setTag(TAG_END_SCALE, newEndValue); 430d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi } 431d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi 4323af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek /** 4333af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek * Start an animator instantly instead of waiting on the next synchronization frame 4343af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek */ 4353af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek private void startInstantly(ValueAnimator animator) { 4363af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek animator.start(); 4373af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek animator.setCurrentPlayTime(0); 4383af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek } 4393af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek 4403af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek /** 4413af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek * @return an adapter which ensures that onAnimationFinished is called once no animation is 4423af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek * running anymore 4433af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek */ 4443af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek private AnimatorListenerAdapter getGlobalAnimationFinishedListener() { 4453af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek if (!mAnimationListenerPool.empty()) { 4463af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek return mAnimationListenerPool.pop(); 4473af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek } 4483af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek 4493af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek // We need to create a new one, no reusable ones found 4503af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek return new AnimatorListenerAdapter() { 4513af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek private boolean mWasCancelled; 4523af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek 4533af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek @Override 4543af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek public void onAnimationEnd(Animator animation) { 4553af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek mAnimatorSet.remove(animation); 4563af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek if (mAnimatorSet.isEmpty() && !mWasCancelled) { 4573af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek onAnimationFinished(); 4583af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek } 4593af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek mAnimationListenerPool.push(this); 4603af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek } 4613af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek 4623af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek @Override 4633af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek public void onAnimationCancel(Animator animation) { 4643af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek mWasCancelled = true; 4653af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek } 4663af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek 4673af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek @Override 4683af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek public void onAnimationStart(Animator animation) { 4693af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek mAnimatorSet.add(animation); 4703af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek mWasCancelled = false; 4713af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek } 4723af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek }; 4733af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek } 4743af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek 475eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek private <T> T getChildTag(View child, int tag) { 476eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek return (T) child.getTag(tag); 477eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek } 478eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek 479eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek /** 480eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek * Cancel the previous animator and get the duration of the new animation. 48139610545f0c2714a3526bc935effe57b421542d1Selim Cinek * 482eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek * @param previousAnimator the animator which was running before 483eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek * @return the new duration 484eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek */ 4858df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek private long cancelAnimatorAndGetNewDuration(ValueAnimator previousAnimator) { 4865aa045cc6bca84f5c11f1a99999546ba5e5949a5Jorim Jaggi long newDuration = mCurrentLength; 487eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek if (previousAnimator != null) { 4888df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek // We take either the desired length of the new animation or the remaining time of 4898df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek // the previous animator, whichever is longer. 4908df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek newDuration = Math.max(previousAnimator.getDuration() 4918df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek - previousAnimator.getCurrentPlayTime(), newDuration); 4923af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek previousAnimator.cancel(); 493eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek } 4943af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek return newDuration; 495eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek } 496eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek 497eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek private void onAnimationFinished() { 498eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek mHandledEvents.clear(); 499eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek mNewEvents.clear(); 500eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek mHostLayout.onChildAnimationFinished(); 501eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek } 502eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek 503eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek /** 504eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek * Process the animationEvents for a new animation 505eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek * 506eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek * @param animationEvents the animation events for the animation to perform 50739610545f0c2714a3526bc935effe57b421542d1Selim Cinek * @param finalState the final state to animate to 50839610545f0c2714a3526bc935effe57b421542d1Selim Cinek */ 509eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek private void processAnimationEvents( 5100dd6881ea481c855976214807c17595b34a2920aJorim Jaggi ArrayList<NotificationStackScrollLayout.AnimationEvent> animationEvents, 511572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek StackScrollState finalState) { 512eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek mNewEvents.clear(); 5130dd6881ea481c855976214807c17595b34a2920aJorim Jaggi for (NotificationStackScrollLayout.AnimationEvent event: animationEvents) { 514572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek View changingView = event.changingView; 515eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek if (!mHandledEvents.contains(event)) { 516eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek if (event.animationType == NotificationStackScrollLayout.AnimationEvent 517eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek .ANIMATION_TYPE_ADD) { 518eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek 519eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek // This item is added, initialize it's properties. 520eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek StackScrollState.ViewState viewState = finalState 521eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek .getViewStateForView(changingView); 522eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek if (viewState == null) { 523eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek // The position for this child was never generated, let's continue. 524eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek continue; 525eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek } 526eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek changingView.setAlpha(0); 527eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek changingView.setTranslationY(viewState.yTranslation); 528eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek changingView.setTranslationZ(viewState.zTranslation); 52939610545f0c2714a3526bc935effe57b421542d1Selim Cinek } 530572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek mHandledEvents.add(event); 531eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek mNewEvents.add(event); 532572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek } 533572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek } 534572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek } 535572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek} 536