StackStateAnimator.java revision b5605e58cb8080c8c887b1885336b707596c8094
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; 29b5605e58cb8080c8c887b1885336b707596c8094Selim Cinekimport com.android.systemui.statusbar.ExpandableNotificationRow; 30572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinekimport com.android.systemui.statusbar.ExpandableView; 318b73006a36b3000a9847534dcb01a2e7066e9d93Jorim Jaggiimport com.android.systemui.statusbar.SpeedBumpView; 32572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek 33572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinekimport java.util.ArrayList; 34eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinekimport java.util.HashSet; 35eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinekimport java.util.Set; 36eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinekimport java.util.Stack; 37572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek 38572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek/** 39572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek * An stack state animator which handles animations to new StackScrollStates 40572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek */ 41572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinekpublic class StackStateAnimator { 42572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek 435aa045cc6bca84f5c11f1a99999546ba5e5949a5Jorim Jaggi public static final int ANIMATION_DURATION_STANDARD = 360; 4460d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi public static final int ANIMATION_DURATION_GO_TO_FULL_SHADE = 448; 458efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek public static final int ANIMATION_DURATION_APPEAR_DISAPPEAR = 464; 46b5605e58cb8080c8c887b1885336b707596c8094Selim Cinek public static final int ANIMATION_DURATION_EXPAND_CLICKED = 360; 475aa045cc6bca84f5c11f1a99999546ba5e5949a5Jorim Jaggi public static final int ANIMATION_DURATION_DIMMED_ACTIVATED = 220; 488efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek public static final int ANIMATION_DELAY_PER_ELEMENT_INTERRUPTING = 80; 49b5605e58cb8080c8c887b1885336b707596c8094Selim Cinek public static final int ANIMATION_DELAY_PER_ELEMENT_EXPAND_CHILDREN = 54; 508efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek public static final int ANIMATION_DELAY_PER_ELEMENT_MANUAL = 32; 5160d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi public static final int ANIMATION_DELAY_PER_ELEMENT_GO_TO_FULL_SHADE = 48; 524e857f4ef0357e05806819d0488a73a12208fe8fJorim Jaggi public static final int ANIMATION_DELAY_PER_ELEMENT_DARK = 24; 53b5605e58cb8080c8c887b1885336b707596c8094Selim Cinek public static final int DELAY_EFFECT_MAX_INDEX_DIFFERENCE = 2; 54b5605e58cb8080c8c887b1885336b707596c8094Selim Cinek public static final int DELAY_EFFECT_MAX_INDEX_DIFFERENCE_CHILDREN = 3; 555aa045cc6bca84f5c11f1a99999546ba5e5949a5Jorim Jaggi 56eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek private static final int TAG_ANIMATOR_TRANSLATION_Y = R.id.translation_y_animator_tag; 57eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek private static final int TAG_ANIMATOR_TRANSLATION_Z = R.id.translation_z_animator_tag; 58d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi private static final int TAG_ANIMATOR_SCALE = R.id.scale_animator_tag; 59eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek private static final int TAG_ANIMATOR_ALPHA = R.id.alpha_animator_tag; 60eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek private static final int TAG_ANIMATOR_HEIGHT = R.id.height_animator_tag; 61eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek private static final int TAG_ANIMATOR_TOP_INSET = R.id.top_inset_animator_tag; 62eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek private static final int TAG_END_TRANSLATION_Y = R.id.translation_y_animator_end_value_tag; 63eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek private static final int TAG_END_TRANSLATION_Z = R.id.translation_z_animator_end_value_tag; 64d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi private static final int TAG_END_SCALE = R.id.scale_animator_end_value_tag; 65eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek private static final int TAG_END_ALPHA = R.id.alpha_animator_end_value_tag; 66eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek private static final int TAG_END_HEIGHT = R.id.height_animator_end_value_tag; 67eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek private static final int TAG_END_TOP_INSET = R.id.top_inset_animator_end_value_tag; 688df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek private static final int TAG_START_TRANSLATION_Y = R.id.translation_y_animator_start_value_tag; 698df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek private static final int TAG_START_TRANSLATION_Z = R.id.translation_z_animator_start_value_tag; 708df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek private static final int TAG_START_SCALE = R.id.scale_animator_start_value_tag; 718df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek private static final int TAG_START_ALPHA = R.id.alpha_animator_start_value_tag; 728df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek private static final int TAG_START_HEIGHT = R.id.height_animator_start_value_tag; 738df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek private static final int TAG_START_TOP_INSET = R.id.top_inset_animator_start_value_tag; 74572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek 75572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek private final Interpolator mFastOutSlowInInterpolator; 7660d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi private final int mGoToFullShadeAppearingTranslation; 77572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek public NotificationStackScrollLayout mHostLayout; 78eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek private ArrayList<NotificationStackScrollLayout.AnimationEvent> mNewEvents = 79eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek new ArrayList<>(); 808efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek private ArrayList<View> mNewAddChildren = new ArrayList<>(); 8160d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi private Set<Animator> mAnimatorSet = new HashSet<>(); 8260d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi private Stack<AnimatorListenerAdapter> mAnimationListenerPool = new Stack<>(); 83d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi private AnimationFilter mAnimationFilter = new AnimationFilter(); 845aa045cc6bca84f5c11f1a99999546ba5e5949a5Jorim Jaggi private long mCurrentLength; 85dbc3dce886cb7abba23a9f18e60d0f5af5c93226Jorim Jaggi private long mCurrentAdditionalDelay; 86572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek 8760d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi /** The current index for the last child which was not added in this event set. */ 8860d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi private int mCurrentLastNotAddedIndex; 8960d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi 908d9ff9c2c66bc1d3b92eb6992d58599ff80ed6dcSelim Cinek private ValueAnimator mTopOverScrollAnimator; 918d9ff9c2c66bc1d3b92eb6992d58599ff80ed6dcSelim Cinek private ValueAnimator mBottomOverScrollAnimator; 92b5605e58cb8080c8c887b1885336b707596c8094Selim Cinek private ExpandableNotificationRow mChildExpandingView; 938d9ff9c2c66bc1d3b92eb6992d58599ff80ed6dcSelim Cinek 94572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek public StackStateAnimator(NotificationStackScrollLayout hostLayout) { 95572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek mHostLayout = hostLayout; 96572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek mFastOutSlowInInterpolator = AnimationUtils.loadInterpolator(hostLayout.getContext(), 97eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek android.R.interpolator.fast_out_slow_in); 9860d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi mGoToFullShadeAppearingTranslation = 9960d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi hostLayout.getContext().getResources().getDimensionPixelSize( 10060d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi R.dimen.go_to_full_shade_appearing_translation); 101572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek } 102572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek 103572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek public boolean isRunning() { 104eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek return !mAnimatorSet.isEmpty(); 105572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek } 106572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek 107572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek public void startAnimationForEvents( 1080dd6881ea481c855976214807c17595b34a2920aJorim Jaggi ArrayList<NotificationStackScrollLayout.AnimationEvent> mAnimationEvents, 109dbc3dce886cb7abba23a9f18e60d0f5af5c93226Jorim Jaggi StackScrollState finalState, long additionalDelay) { 110eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek 111eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek processAnimationEvents(mAnimationEvents, finalState); 112eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek 113572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek int childCount = mHostLayout.getChildCount(); 114d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi mAnimationFilter.applyCombination(mNewEvents); 115dbc3dce886cb7abba23a9f18e60d0f5af5c93226Jorim Jaggi mCurrentAdditionalDelay = additionalDelay; 1165aa045cc6bca84f5c11f1a99999546ba5e5949a5Jorim Jaggi mCurrentLength = NotificationStackScrollLayout.AnimationEvent.combineLength(mNewEvents); 11760d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi mCurrentLastNotAddedIndex = findLastNotAddedIndex(finalState); 118572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek for (int i = 0; i < childCount; i++) { 119572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek final ExpandableView child = (ExpandableView) mHostLayout.getChildAt(i); 1208efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek 121b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek StackViewState viewState = finalState.getViewStateForView(child); 1228efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek if (viewState == null || child.getVisibility() == View.GONE) { 123572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek continue; 124572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek } 125572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek 126a272dfed9a4f31d8245099c0d99a73e79b90670cSelim Cinek child.setClipTopOptimization(0); 127b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek startStackAnimations(child, viewState, finalState, i, -1 /* fixedDelay */); 128572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek } 129eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek if (!isRunning()) { 130eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek // no child has preformed any animation, lets finish 131eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek onAnimationFinished(); 132eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek } 1338efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek mNewEvents.clear(); 1348efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek mNewAddChildren.clear(); 135b5605e58cb8080c8c887b1885336b707596c8094Selim Cinek mChildExpandingView = null; 136572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek } 137572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek 13860d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi private int findLastNotAddedIndex(StackScrollState finalState) { 13960d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi int childCount = mHostLayout.getChildCount(); 14060d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi for (int i = childCount - 1; i >= 0; i--) { 14160d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi final ExpandableView child = (ExpandableView) mHostLayout.getChildAt(i); 14260d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi 143b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek StackViewState viewState = finalState.getViewStateForView(child); 14460d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi if (viewState == null || child.getVisibility() == View.GONE) { 14560d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi continue; 14660d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi } 14760d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi if (!mNewAddChildren.contains(child)) { 14860d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi return viewState.notGoneIndex; 14960d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi } 15060d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi } 15160d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi return -1; 15260d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi } 15360d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi 154b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek 155eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek /** 156b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek * Start an animation to the given {@link StackViewState}. 157b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek * 158b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek * @param child the child to start the animation on 159b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek * @param viewState the {@link StackViewState} of the view to animate to 160b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek * @param finalState the final state after the animation 161b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek * @param i the index of the view; only relevant if the view is the speed bump and is 162b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek * ignored otherwise 163b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek * @param fixedDelay a fixed delay if desired or -1 if the delay should be calculated 164eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek */ 165b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek public void startStackAnimations(final ExpandableView child, StackViewState viewState, 166b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek StackScrollState finalState, int i, long fixedDelay) { 167eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek final float alpha = viewState.alpha; 168b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek boolean wasAdded = mNewAddChildren.contains(child); 169b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek long duration = mCurrentLength; 170b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek if (wasAdded && mAnimationFilter.hasGoToFullShadeEvent) { 171b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek child.setTranslationY(child.getTranslationY() + mGoToFullShadeAppearingTranslation); 172b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek float longerDurationFactor = viewState.notGoneIndex - mCurrentLastNotAddedIndex; 173b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek longerDurationFactor = (float) Math.pow(longerDurationFactor, 0.7f); 174b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek duration = ANIMATION_DURATION_APPEAR_DISAPPEAR + 50 + 175b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek (long) (100 * longerDurationFactor); 176eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek } 1778efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek boolean yTranslationChanging = child.getTranslationY() != viewState.yTranslation; 1788efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek boolean zTranslationChanging = child.getTranslationZ() != viewState.zTranslation; 1798efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek boolean scaleChanging = child.getScaleX() != viewState.scale; 1808efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek boolean alphaChanging = alpha != child.getAlpha(); 1818efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek boolean heightChanging = viewState.height != child.getActualHeight(); 1824e857f4ef0357e05806819d0488a73a12208fe8fJorim Jaggi boolean darkChanging = viewState.dark != child.isDark(); 183708a6c120da6750d281195ef15a240a5627efed4Selim Cinek boolean topInsetChanging = viewState.clipTopAmount != child.getClipTopAmount(); 1848efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek boolean hasDelays = mAnimationFilter.hasDelays; 1858efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek boolean isDelayRelevant = yTranslationChanging || zTranslationChanging || scaleChanging || 1864e857f4ef0357e05806819d0488a73a12208fe8fJorim Jaggi alphaChanging || heightChanging || topInsetChanging || darkChanging; 1878efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek long delay = 0; 188b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek if (fixedDelay != -1) { 189b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek delay = fixedDelay; 190b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek } else if (hasDelays && isDelayRelevant || wasAdded) { 191dbc3dce886cb7abba23a9f18e60d0f5af5c93226Jorim Jaggi delay = mCurrentAdditionalDelay + calculateChildAnimationDelay(viewState, finalState); 1928efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek } 1938efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek 194b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek startViewAnimations(child, viewState, delay, duration); 1958efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek 196eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek // start height animation 197d2319fbe6a53ac4c38ca02e4d8e32da86ed0994bSelim Cinek if (heightChanging && child.getActualHeight() != 0) { 198b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek startHeightAnimation(child, viewState, duration, delay); 19959b5a356b828fe60ea2874b0680a1bf7c84809a1Jorim Jaggi } 2008efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek 201708a6c120da6750d281195ef15a240a5627efed4Selim Cinek // start top inset animation 202708a6c120da6750d281195ef15a240a5627efed4Selim Cinek if (topInsetChanging) { 203b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek startInsetAnimation(child, viewState, duration, delay); 204708a6c120da6750d281195ef15a240a5627efed4Selim Cinek } 205708a6c120da6750d281195ef15a240a5627efed4Selim Cinek 206d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi // start dimmed animation 207b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek child.setDimmed(viewState.dimmed, mAnimationFilter.animateDimmed); 2088efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek 209bf370992508c55d1f2493923bdc1834a0710e4baJohn Spurlock // start dark animation 210b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek child.setDark(viewState.dark, mAnimationFilter.animateDark, delay); 211bf370992508c55d1f2493923bdc1834a0710e4baJohn Spurlock 2123d2b94bf8e32640e57573ebb17911b1db9440231Selim Cinek // apply speed bump state 2133d2b94bf8e32640e57573ebb17911b1db9440231Selim Cinek child.setBelowSpeedBump(viewState.belowSpeedBump); 2143d2b94bf8e32640e57573ebb17911b1db9440231Selim Cinek 215ae44128776410abd11bd06ae700db9cc4606a773Jorim Jaggi // start hiding sensitive animation 216b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek child.setHideSensitive(viewState.hideSensitive, mAnimationFilter.animateHideSensitive, 217b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek delay, duration); 218ae44128776410abd11bd06ae700db9cc4606a773Jorim Jaggi 2198efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek if (wasAdded) { 22060d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi child.performAddAnimation(delay, mCurrentLength); 2218efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek } 2228b73006a36b3000a9847534dcb01a2e7066e9d93Jorim Jaggi if (child instanceof SpeedBumpView) { 2238b73006a36b3000a9847534dcb01a2e7066e9d93Jorim Jaggi finalState.performSpeedBumpAnimation(i, (SpeedBumpView) child, viewState, 2248b73006a36b3000a9847534dcb01a2e7066e9d93Jorim Jaggi delay + duration); 225b5605e58cb8080c8c887b1885336b707596c8094Selim Cinek } else if (child instanceof ExpandableNotificationRow) { 226b5605e58cb8080c8c887b1885336b707596c8094Selim Cinek ExpandableNotificationRow row = (ExpandableNotificationRow) child; 227b5605e58cb8080c8c887b1885336b707596c8094Selim Cinek row.startChildAnimation(finalState, this, child == mChildExpandingView, delay, 228b5605e58cb8080c8c887b1885336b707596c8094Selim Cinek duration); 2298b73006a36b3000a9847534dcb01a2e7066e9d93Jorim Jaggi } 2308efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek } 2318efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek 232b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek /** 233b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek * Start an animation to a new {@link ViewState}. 234b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek * 235b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek * @param child the child to start the animation on 236b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek * @param viewState the {@link StackViewState} of the view to animate to 237b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek * @param delay a fixed delay 238b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek * @param duration the duration of the animation 239b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek */ 240b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek public void startViewAnimations(View child, ViewState viewState, long delay, long duration) { 241b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek boolean wasVisible = child.getVisibility() == View.VISIBLE; 242b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek final float alpha = viewState.alpha; 243b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek if (!wasVisible && alpha != 0 && !viewState.gone) { 244b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek child.setVisibility(View.VISIBLE); 245b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek } 246b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek boolean yTranslationChanging = child.getTranslationY() != viewState.yTranslation; 247b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek boolean zTranslationChanging = child.getTranslationZ() != viewState.zTranslation; 248b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek boolean scaleChanging = child.getScaleX() != viewState.scale; 249b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek float childAlpha = child.getVisibility() == View.INVISIBLE ? 0.0f : child.getAlpha(); 250b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek boolean alphaChanging = viewState.alpha != childAlpha; 251b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek 252b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek // start translationY animation 253b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek if (yTranslationChanging) { 254b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek startYTranslationAnimation(child, viewState, duration, delay); 255b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek } 256b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek 257b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek // start translationZ animation 258b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek if (zTranslationChanging) { 259b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek startZTranslationAnimation(child, viewState, duration, delay); 260b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek } 261b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek 262b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek // start scale animation 263b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek if (scaleChanging) { 264b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek startScaleAnimation(child, viewState, duration); 265b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek } 266b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek 267b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek // start alpha animation 268b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek if (alphaChanging && child.getTranslationX() == 0) { 269b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek startAlphaAnimation(child, viewState, duration, delay); 270b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek } 271b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek } 272b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek 273b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek private long calculateChildAnimationDelay(StackViewState viewState, 2748efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek StackScrollState finalState) { 2754e857f4ef0357e05806819d0488a73a12208fe8fJorim Jaggi if (mAnimationFilter.hasDarkEvent) { 2764e857f4ef0357e05806819d0488a73a12208fe8fJorim Jaggi return calculateDelayDark(viewState); 2774e857f4ef0357e05806819d0488a73a12208fe8fJorim Jaggi } 27860d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi if (mAnimationFilter.hasGoToFullShadeEvent) { 27960d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi return calculateDelayGoToFullShade(viewState); 28060d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi } 2818efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek long minDelay = 0; 2828efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek for (NotificationStackScrollLayout.AnimationEvent event : mNewEvents) { 2838efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek long delayPerElement = ANIMATION_DELAY_PER_ELEMENT_INTERRUPTING; 2848efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek switch (event.animationType) { 2858efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek case NotificationStackScrollLayout.AnimationEvent.ANIMATION_TYPE_ADD: { 2868efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek int ownIndex = viewState.notGoneIndex; 2878efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek int changingIndex = finalState 2888efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek .getViewStateForView(event.changingView).notGoneIndex; 2898efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek int difference = Math.abs(ownIndex - changingIndex); 2908efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek difference = Math.max(0, Math.min(DELAY_EFFECT_MAX_INDEX_DIFFERENCE, 2918efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek difference - 1)); 2928efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek long delay = (DELAY_EFFECT_MAX_INDEX_DIFFERENCE - difference) * delayPerElement; 2938efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek minDelay = Math.max(delay, minDelay); 2948efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek break; 2958efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek } 2968efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek case NotificationStackScrollLayout.AnimationEvent.ANIMATION_TYPE_REMOVE_SWIPED_OUT: 2978efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek delayPerElement = ANIMATION_DELAY_PER_ELEMENT_MANUAL; 2988efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek case NotificationStackScrollLayout.AnimationEvent.ANIMATION_TYPE_REMOVE: { 2998efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek int ownIndex = viewState.notGoneIndex; 3008efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek boolean noNextView = event.viewAfterChangingView == null; 3018efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek View viewAfterChangingView = noNextView 3028efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek ? mHostLayout.getLastChildNotGone() 3038efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek : event.viewAfterChangingView; 3048efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek 3058efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek int nextIndex = finalState 3068efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek .getViewStateForView(viewAfterChangingView).notGoneIndex; 3078efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek if (ownIndex >= nextIndex) { 3088efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek // we only have the view afterwards 3098efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek ownIndex++; 3108efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek } 3118efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek int difference = Math.abs(ownIndex - nextIndex); 3128efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek difference = Math.max(0, Math.min(DELAY_EFFECT_MAX_INDEX_DIFFERENCE, 3138efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek difference - 1)); 3148efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek long delay = difference * delayPerElement; 3158efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek minDelay = Math.max(delay, minDelay); 3168efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek break; 3178efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek } 3188efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek default: 3198efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek break; 3208efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek } 3218efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek } 3228efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek return minDelay; 323572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek } 324572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek 325b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek private long calculateDelayDark(StackViewState viewState) { 3262a5e452f4f70d2dd717946dd50486cc66ea4cc43Jorim Jaggi int referenceIndex; 3272a5e452f4f70d2dd717946dd50486cc66ea4cc43Jorim Jaggi if (mAnimationFilter.darkAnimationOriginIndex == 3282a5e452f4f70d2dd717946dd50486cc66ea4cc43Jorim Jaggi NotificationStackScrollLayout.AnimationEvent.DARK_ANIMATION_ORIGIN_INDEX_ABOVE) { 3292a5e452f4f70d2dd717946dd50486cc66ea4cc43Jorim Jaggi referenceIndex = 0; 3302a5e452f4f70d2dd717946dd50486cc66ea4cc43Jorim Jaggi } else if (mAnimationFilter.darkAnimationOriginIndex == 3312a5e452f4f70d2dd717946dd50486cc66ea4cc43Jorim Jaggi NotificationStackScrollLayout.AnimationEvent.DARK_ANIMATION_ORIGIN_INDEX_BELOW) { 3322a5e452f4f70d2dd717946dd50486cc66ea4cc43Jorim Jaggi referenceIndex = mHostLayout.getNotGoneChildCount() - 1; 3332a5e452f4f70d2dd717946dd50486cc66ea4cc43Jorim Jaggi } else { 3342a5e452f4f70d2dd717946dd50486cc66ea4cc43Jorim Jaggi referenceIndex = mAnimationFilter.darkAnimationOriginIndex; 3352a5e452f4f70d2dd717946dd50486cc66ea4cc43Jorim Jaggi } 3362a5e452f4f70d2dd717946dd50486cc66ea4cc43Jorim Jaggi return Math.abs(referenceIndex - viewState.notGoneIndex) * ANIMATION_DELAY_PER_ELEMENT_DARK; 3374e857f4ef0357e05806819d0488a73a12208fe8fJorim Jaggi } 3384e857f4ef0357e05806819d0488a73a12208fe8fJorim Jaggi 339b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek private long calculateDelayGoToFullShade(StackViewState viewState) { 34060d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi float index = viewState.notGoneIndex; 34160d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi index = (float) Math.pow(index, 0.7f); 34260d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi return (long) (index * ANIMATION_DELAY_PER_ELEMENT_GO_TO_FULL_SHADE); 34360d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi } 34460d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi 345eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek private void startHeightAnimation(final ExpandableView child, 346b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek StackViewState viewState, long duration, long delay) { 3478df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek Integer previousStartValue = getChildTag(child, TAG_START_HEIGHT); 348d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi Integer previousEndValue = getChildTag(child, TAG_END_HEIGHT); 3498df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek int newEndValue = viewState.height; 3508df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek if (previousEndValue != null && previousEndValue == newEndValue) { 351eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek return; 352eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek } 353eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek ValueAnimator previousAnimator = getChildTag(child, TAG_ANIMATOR_HEIGHT); 3548df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek if (!mAnimationFilter.animateHeight) { 3558df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek // just a local update was performed 3568df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek if (previousAnimator != null) { 3578df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek // we need to increase all animation keyframes of the previous animator by the 3588df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek // relative change to the end value 3598df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek PropertyValuesHolder[] values = previousAnimator.getValues(); 3608df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek int relativeDiff = newEndValue - previousEndValue; 3618df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek int newStartValue = previousStartValue + relativeDiff; 3628df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek values[0].setIntValues(newStartValue, newEndValue); 3638df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek child.setTag(TAG_START_HEIGHT, newStartValue); 3648df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek child.setTag(TAG_END_HEIGHT, newEndValue); 3658df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek previousAnimator.setCurrentPlayTime(previousAnimator.getCurrentPlayTime()); 3668df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek return; 3678df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek } else { 3688df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek // no new animation needed, let's just apply the value 3698df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek child.setActualHeight(newEndValue, false); 3708df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek return; 371eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek } 372eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek } 373eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek 3748df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek ValueAnimator animator = ValueAnimator.ofInt(child.getActualHeight(), newEndValue); 375eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { 376572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek @Override 377572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek public void onAnimationUpdate(ValueAnimator animation) { 378d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi child.setActualHeight((int) animation.getAnimatedValue(), 379d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi false /* notifyListeners */); 380572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek } 381572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek }); 382eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek animator.setInterpolator(mFastOutSlowInInterpolator); 38360d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi long newDuration = cancelAnimatorAndGetNewDuration(duration, previousAnimator); 384eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek animator.setDuration(newDuration); 3858efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek if (delay > 0 && (previousAnimator == null || !previousAnimator.isRunning())) { 3868efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek animator.setStartDelay(delay); 3878efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek } 388eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek animator.addListener(getGlobalAnimationFinishedListener()); 389eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek // remove the tag when the animation is finished 390eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek animator.addListener(new AnimatorListenerAdapter() { 391eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek @Override 392eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek public void onAnimationEnd(Animator animation) { 393eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek child.setTag(TAG_ANIMATOR_HEIGHT, null); 3948df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek child.setTag(TAG_START_HEIGHT, null); 395eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek child.setTag(TAG_END_HEIGHT, null); 396eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek } 397eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek }); 3988efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek startAnimator(animator); 399eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek child.setTag(TAG_ANIMATOR_HEIGHT, animator); 4008df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek child.setTag(TAG_START_HEIGHT, child.getActualHeight()); 4018df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek child.setTag(TAG_END_HEIGHT, newEndValue); 402eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek } 403eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek 404708a6c120da6750d281195ef15a240a5627efed4Selim Cinek private void startInsetAnimation(final ExpandableView child, 405b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek StackViewState viewState, long duration, long delay) { 406708a6c120da6750d281195ef15a240a5627efed4Selim Cinek Integer previousStartValue = getChildTag(child, TAG_START_TOP_INSET); 407708a6c120da6750d281195ef15a240a5627efed4Selim Cinek Integer previousEndValue = getChildTag(child, TAG_END_TOP_INSET); 408708a6c120da6750d281195ef15a240a5627efed4Selim Cinek int newEndValue = viewState.clipTopAmount; 409708a6c120da6750d281195ef15a240a5627efed4Selim Cinek if (previousEndValue != null && previousEndValue == newEndValue) { 410708a6c120da6750d281195ef15a240a5627efed4Selim Cinek return; 411708a6c120da6750d281195ef15a240a5627efed4Selim Cinek } 412708a6c120da6750d281195ef15a240a5627efed4Selim Cinek ValueAnimator previousAnimator = getChildTag(child, TAG_ANIMATOR_TOP_INSET); 413708a6c120da6750d281195ef15a240a5627efed4Selim Cinek if (!mAnimationFilter.animateTopInset) { 414708a6c120da6750d281195ef15a240a5627efed4Selim Cinek // just a local update was performed 415708a6c120da6750d281195ef15a240a5627efed4Selim Cinek if (previousAnimator != null) { 416708a6c120da6750d281195ef15a240a5627efed4Selim Cinek // we need to increase all animation keyframes of the previous animator by the 417708a6c120da6750d281195ef15a240a5627efed4Selim Cinek // relative change to the end value 418708a6c120da6750d281195ef15a240a5627efed4Selim Cinek PropertyValuesHolder[] values = previousAnimator.getValues(); 419708a6c120da6750d281195ef15a240a5627efed4Selim Cinek int relativeDiff = newEndValue - previousEndValue; 420708a6c120da6750d281195ef15a240a5627efed4Selim Cinek int newStartValue = previousStartValue + relativeDiff; 421708a6c120da6750d281195ef15a240a5627efed4Selim Cinek values[0].setIntValues(newStartValue, newEndValue); 422708a6c120da6750d281195ef15a240a5627efed4Selim Cinek child.setTag(TAG_START_TOP_INSET, newStartValue); 423708a6c120da6750d281195ef15a240a5627efed4Selim Cinek child.setTag(TAG_END_TOP_INSET, newEndValue); 424708a6c120da6750d281195ef15a240a5627efed4Selim Cinek previousAnimator.setCurrentPlayTime(previousAnimator.getCurrentPlayTime()); 425708a6c120da6750d281195ef15a240a5627efed4Selim Cinek return; 426708a6c120da6750d281195ef15a240a5627efed4Selim Cinek } else { 427708a6c120da6750d281195ef15a240a5627efed4Selim Cinek // no new animation needed, let's just apply the value 428708a6c120da6750d281195ef15a240a5627efed4Selim Cinek child.setClipTopAmount(newEndValue); 429708a6c120da6750d281195ef15a240a5627efed4Selim Cinek return; 430708a6c120da6750d281195ef15a240a5627efed4Selim Cinek } 431708a6c120da6750d281195ef15a240a5627efed4Selim Cinek } 432708a6c120da6750d281195ef15a240a5627efed4Selim Cinek 433708a6c120da6750d281195ef15a240a5627efed4Selim Cinek ValueAnimator animator = ValueAnimator.ofInt(child.getClipTopAmount(), newEndValue); 434708a6c120da6750d281195ef15a240a5627efed4Selim Cinek animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { 435708a6c120da6750d281195ef15a240a5627efed4Selim Cinek @Override 436708a6c120da6750d281195ef15a240a5627efed4Selim Cinek public void onAnimationUpdate(ValueAnimator animation) { 437708a6c120da6750d281195ef15a240a5627efed4Selim Cinek child.setClipTopAmount((int) animation.getAnimatedValue()); 438708a6c120da6750d281195ef15a240a5627efed4Selim Cinek } 439708a6c120da6750d281195ef15a240a5627efed4Selim Cinek }); 440708a6c120da6750d281195ef15a240a5627efed4Selim Cinek animator.setInterpolator(mFastOutSlowInInterpolator); 44160d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi long newDuration = cancelAnimatorAndGetNewDuration(duration, previousAnimator); 442708a6c120da6750d281195ef15a240a5627efed4Selim Cinek animator.setDuration(newDuration); 443708a6c120da6750d281195ef15a240a5627efed4Selim Cinek if (delay > 0 && (previousAnimator == null || !previousAnimator.isRunning())) { 444708a6c120da6750d281195ef15a240a5627efed4Selim Cinek animator.setStartDelay(delay); 445708a6c120da6750d281195ef15a240a5627efed4Selim Cinek } 446708a6c120da6750d281195ef15a240a5627efed4Selim Cinek animator.addListener(getGlobalAnimationFinishedListener()); 447708a6c120da6750d281195ef15a240a5627efed4Selim Cinek // remove the tag when the animation is finished 448708a6c120da6750d281195ef15a240a5627efed4Selim Cinek animator.addListener(new AnimatorListenerAdapter() { 449708a6c120da6750d281195ef15a240a5627efed4Selim Cinek @Override 450708a6c120da6750d281195ef15a240a5627efed4Selim Cinek public void onAnimationEnd(Animator animation) { 451708a6c120da6750d281195ef15a240a5627efed4Selim Cinek child.setTag(TAG_ANIMATOR_TOP_INSET, null); 452708a6c120da6750d281195ef15a240a5627efed4Selim Cinek child.setTag(TAG_START_TOP_INSET, null); 453708a6c120da6750d281195ef15a240a5627efed4Selim Cinek child.setTag(TAG_END_TOP_INSET, null); 454708a6c120da6750d281195ef15a240a5627efed4Selim Cinek } 455708a6c120da6750d281195ef15a240a5627efed4Selim Cinek }); 456708a6c120da6750d281195ef15a240a5627efed4Selim Cinek startAnimator(animator); 457708a6c120da6750d281195ef15a240a5627efed4Selim Cinek child.setTag(TAG_ANIMATOR_TOP_INSET, animator); 458708a6c120da6750d281195ef15a240a5627efed4Selim Cinek child.setTag(TAG_START_TOP_INSET, child.getClipTopAmount()); 459708a6c120da6750d281195ef15a240a5627efed4Selim Cinek child.setTag(TAG_END_TOP_INSET, newEndValue); 460708a6c120da6750d281195ef15a240a5627efed4Selim Cinek } 461708a6c120da6750d281195ef15a240a5627efed4Selim Cinek 462b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek private void startAlphaAnimation(final View child, 463b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek final ViewState viewState, long duration, long delay) { 4648df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek Float previousStartValue = getChildTag(child,TAG_START_ALPHA); 465eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek Float previousEndValue = getChildTag(child,TAG_END_ALPHA); 4668df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek final float newEndValue = viewState.alpha; 4678df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek if (previousEndValue != null && previousEndValue == newEndValue) { 468eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek return; 469eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek } 470eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek ObjectAnimator previousAnimator = getChildTag(child, TAG_ANIMATOR_ALPHA); 4718df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek if (!mAnimationFilter.animateAlpha) { 4728df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek // just a local update was performed 4738df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek if (previousAnimator != null) { 4748df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek // we need to increase all animation keyframes of the previous animator by the 4758df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek // relative change to the end value 4768df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek PropertyValuesHolder[] values = previousAnimator.getValues(); 4778df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek float relativeDiff = newEndValue - previousEndValue; 4788df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek float newStartValue = previousStartValue + relativeDiff; 4798df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek values[0].setFloatValues(newStartValue, newEndValue); 4808df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek child.setTag(TAG_START_ALPHA, newStartValue); 4818df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek child.setTag(TAG_END_ALPHA, newEndValue); 4828df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek previousAnimator.setCurrentPlayTime(previousAnimator.getCurrentPlayTime()); 4838df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek return; 4848df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek } else { 4858df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek // no new animation needed, let's just apply the value 4868df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek child.setAlpha(newEndValue); 4878df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek if (newEndValue == 0) { 4888df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek child.setVisibility(View.INVISIBLE); 4898df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek } 490eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek } 491eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek } 492eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek 493eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek ObjectAnimator animator = ObjectAnimator.ofFloat(child, View.ALPHA, 4948df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek child.getAlpha(), newEndValue); 495eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek animator.setInterpolator(mFastOutSlowInInterpolator); 496eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek // Handle layer type 497eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek child.setLayerType(View.LAYER_TYPE_HARDWARE, null); 498eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek animator.addListener(new AnimatorListenerAdapter() { 499eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek public boolean mWasCancelled; 500eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek 501eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek @Override 502eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek public void onAnimationEnd(Animator animation) { 5031efb240c1a0aeca9492cf8891794712adfdb1fa7Selim Cinek child.setLayerType(View.LAYER_TYPE_NONE, null); 5048df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek if (newEndValue == 0 && !mWasCancelled) { 505eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek child.setVisibility(View.INVISIBLE); 506eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek } 507c5baa3eb0893cb764e7810f8c68e89b04653df86Selim Cinek // remove the tag when the animation is finished 508eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek child.setTag(TAG_ANIMATOR_ALPHA, null); 5098df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek child.setTag(TAG_START_ALPHA, null); 510eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek child.setTag(TAG_END_ALPHA, null); 511eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek } 512eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek 513eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek @Override 514eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek public void onAnimationCancel(Animator animation) { 515eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek mWasCancelled = true; 516eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek } 517eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek 518eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek @Override 519eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek public void onAnimationStart(Animator animation) { 520eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek mWasCancelled = false; 521eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek } 522eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek }); 52360d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi long newDuration = cancelAnimatorAndGetNewDuration(duration, previousAnimator); 5243af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek animator.setDuration(newDuration); 5258efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek if (delay > 0 && (previousAnimator == null || !previousAnimator.isRunning())) { 5268efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek animator.setStartDelay(delay); 5278efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek } 528eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek animator.addListener(getGlobalAnimationFinishedListener()); 529eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek 5308efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek startAnimator(animator); 531eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek child.setTag(TAG_ANIMATOR_ALPHA, animator); 5328df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek child.setTag(TAG_START_ALPHA, child.getAlpha()); 5338df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek child.setTag(TAG_END_ALPHA, newEndValue); 534572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek } 535572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek 536b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek private void startZTranslationAnimation(final View child, 537b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek final ViewState viewState, long duration, long delay) { 5388df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek Float previousStartValue = getChildTag(child,TAG_START_TRANSLATION_Z); 539eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek Float previousEndValue = getChildTag(child,TAG_END_TRANSLATION_Z); 5408df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek float newEndValue = viewState.zTranslation; 5418df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek if (previousEndValue != null && previousEndValue == newEndValue) { 542eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek return; 543eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek } 544eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek ObjectAnimator previousAnimator = getChildTag(child, TAG_ANIMATOR_TRANSLATION_Z); 5458df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek if (!mAnimationFilter.animateZ) { 5468df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek // just a local update was performed 5478df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek if (previousAnimator != null) { 5488df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek // we need to increase all animation keyframes of the previous animator by the 5498df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek // relative change to the end value 5508df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek PropertyValuesHolder[] values = previousAnimator.getValues(); 5518df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek float relativeDiff = newEndValue - previousEndValue; 5528df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek float newStartValue = previousStartValue + relativeDiff; 5538df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek values[0].setFloatValues(newStartValue, newEndValue); 5548df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek child.setTag(TAG_START_TRANSLATION_Z, newStartValue); 5558df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek child.setTag(TAG_END_TRANSLATION_Z, newEndValue); 5568df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek previousAnimator.setCurrentPlayTime(previousAnimator.getCurrentPlayTime()); 5578df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek return; 5588df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek } else { 5598df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek // no new animation needed, let's just apply the value 5608df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek child.setTranslationZ(newEndValue); 561eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek } 562eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek } 563eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek 564eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek ObjectAnimator animator = ObjectAnimator.ofFloat(child, View.TRANSLATION_Z, 5658df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek child.getTranslationZ(), newEndValue); 566eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek animator.setInterpolator(mFastOutSlowInInterpolator); 56760d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi long newDuration = cancelAnimatorAndGetNewDuration(duration, previousAnimator); 5683af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek animator.setDuration(newDuration); 5698efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek if (delay > 0 && (previousAnimator == null || !previousAnimator.isRunning())) { 5708efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek animator.setStartDelay(delay); 5718efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek } 572eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek animator.addListener(getGlobalAnimationFinishedListener()); 573eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek // remove the tag when the animation is finished 574eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek animator.addListener(new AnimatorListenerAdapter() { 575eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek @Override 576eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek public void onAnimationEnd(Animator animation) { 577eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek child.setTag(TAG_ANIMATOR_TRANSLATION_Z, null); 5788df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek child.setTag(TAG_START_TRANSLATION_Z, null); 579eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek child.setTag(TAG_END_TRANSLATION_Z, null); 580eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek } 581eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek }); 5828efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek startAnimator(animator); 583eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek child.setTag(TAG_ANIMATOR_TRANSLATION_Z, animator); 5848df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek child.setTag(TAG_START_TRANSLATION_Z, child.getTranslationZ()); 5858df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek child.setTag(TAG_END_TRANSLATION_Z, newEndValue); 586eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek } 587eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek 588b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek private void startYTranslationAnimation(final View child, 589b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek ViewState viewState, long duration, long delay) { 5908df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek Float previousStartValue = getChildTag(child,TAG_START_TRANSLATION_Y); 591eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek Float previousEndValue = getChildTag(child,TAG_END_TRANSLATION_Y); 5928df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek float newEndValue = viewState.yTranslation; 5938df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek if (previousEndValue != null && previousEndValue == newEndValue) { 594eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek return; 595eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek } 596eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek ObjectAnimator previousAnimator = getChildTag(child, TAG_ANIMATOR_TRANSLATION_Y); 5978df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek if (!mAnimationFilter.animateY) { 5988df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek // just a local update was performed 5998df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek if (previousAnimator != null) { 6008df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek // we need to increase all animation keyframes of the previous animator by the 6018df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek // relative change to the end value 6028df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek PropertyValuesHolder[] values = previousAnimator.getValues(); 6038df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek float relativeDiff = newEndValue - previousEndValue; 6048df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek float newStartValue = previousStartValue + relativeDiff; 6058df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek values[0].setFloatValues(newStartValue, newEndValue); 6068df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek child.setTag(TAG_START_TRANSLATION_Y, newStartValue); 6078df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek child.setTag(TAG_END_TRANSLATION_Y, newEndValue); 6088df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek previousAnimator.setCurrentPlayTime(previousAnimator.getCurrentPlayTime()); 6098df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek return; 6108df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek } else { 6118df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek // no new animation needed, let's just apply the value 6128df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek child.setTranslationY(newEndValue); 6138df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek return; 614eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek } 615eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek } 616eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek 617eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek ObjectAnimator animator = ObjectAnimator.ofFloat(child, View.TRANSLATION_Y, 6188df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek child.getTranslationY(), newEndValue); 619eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek animator.setInterpolator(mFastOutSlowInInterpolator); 62060d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi long newDuration = cancelAnimatorAndGetNewDuration(duration, previousAnimator); 6213af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek animator.setDuration(newDuration); 6228efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek if (delay > 0 && (previousAnimator == null || !previousAnimator.isRunning())) { 6238efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek animator.setStartDelay(delay); 6248efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek } 625eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek animator.addListener(getGlobalAnimationFinishedListener()); 626eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek // remove the tag when the animation is finished 627eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek animator.addListener(new AnimatorListenerAdapter() { 628eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek @Override 629eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek public void onAnimationEnd(Animator animation) { 630eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek child.setTag(TAG_ANIMATOR_TRANSLATION_Y, null); 6318df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek child.setTag(TAG_START_TRANSLATION_Y, null); 632eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek child.setTag(TAG_END_TRANSLATION_Y, null); 633eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek } 634eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek }); 6358efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek startAnimator(animator); 636eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek child.setTag(TAG_ANIMATOR_TRANSLATION_Y, animator); 6378df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek child.setTag(TAG_START_TRANSLATION_Y, child.getTranslationY()); 6388df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek child.setTag(TAG_END_TRANSLATION_Y, newEndValue); 639eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek } 640eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek 641b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek private void startScaleAnimation(final View child, 642b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek ViewState viewState, long duration) { 6438df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek Float previousStartValue = getChildTag(child, TAG_START_SCALE); 644d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi Float previousEndValue = getChildTag(child, TAG_END_SCALE); 6458df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek float newEndValue = viewState.scale; 6468df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek if (previousEndValue != null && previousEndValue == newEndValue) { 647d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi return; 648d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi } 649d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi ObjectAnimator previousAnimator = getChildTag(child, TAG_ANIMATOR_SCALE); 6508df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek if (!mAnimationFilter.animateScale) { 6518df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek // just a local update was performed 6528df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek if (previousAnimator != null) { 6538df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek // we need to increase all animation keyframes of the previous animator by the 6548df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek // relative change to the end value 6558df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek PropertyValuesHolder[] values = previousAnimator.getValues(); 6568df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek float relativeDiff = newEndValue - previousEndValue; 6578df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek float newStartValue = previousStartValue + relativeDiff; 6588df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek values[0].setFloatValues(newStartValue, newEndValue); 6598df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek values[1].setFloatValues(newStartValue, newEndValue); 6608df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek child.setTag(TAG_START_SCALE, newStartValue); 6618df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek child.setTag(TAG_END_SCALE, newEndValue); 6628df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek previousAnimator.setCurrentPlayTime(previousAnimator.getCurrentPlayTime()); 6638df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek return; 6648df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek } else { 6658df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek // no new animation needed, let's just apply the value 6668df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek child.setScaleX(newEndValue); 6678df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek child.setScaleY(newEndValue); 668d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi } 669d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi } 670d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi 671d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi PropertyValuesHolder holderX = 6728df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek PropertyValuesHolder.ofFloat(View.SCALE_X, child.getScaleX(), newEndValue); 673d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi PropertyValuesHolder holderY = 6748df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek PropertyValuesHolder.ofFloat(View.SCALE_Y, child.getScaleY(), newEndValue); 675d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi ObjectAnimator animator = ObjectAnimator.ofPropertyValuesHolder(child, holderX, holderY); 676d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi animator.setInterpolator(mFastOutSlowInInterpolator); 67760d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi long newDuration = cancelAnimatorAndGetNewDuration(duration, previousAnimator); 678d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi animator.setDuration(newDuration); 679d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi animator.addListener(getGlobalAnimationFinishedListener()); 680d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi // remove the tag when the animation is finished 681d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi animator.addListener(new AnimatorListenerAdapter() { 682d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi @Override 683d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi public void onAnimationEnd(Animator animation) { 684d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi child.setTag(TAG_ANIMATOR_SCALE, null); 6858df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek child.setTag(TAG_START_SCALE, null); 686d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi child.setTag(TAG_END_SCALE, null); 687d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi } 688d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi }); 6898efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek startAnimator(animator); 690d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi child.setTag(TAG_ANIMATOR_SCALE, animator); 6918df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek child.setTag(TAG_START_SCALE, child.getScaleX()); 6928df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek child.setTag(TAG_END_SCALE, newEndValue); 693d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi } 694d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi 6958efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek private void startAnimator(ValueAnimator animator) { 6968efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek mAnimatorSet.add(animator); 6973af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek animator.start(); 6983af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek } 6993af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek 7003af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek /** 7013af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek * @return an adapter which ensures that onAnimationFinished is called once no animation is 7023af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek * running anymore 7033af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek */ 7043af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek private AnimatorListenerAdapter getGlobalAnimationFinishedListener() { 7053af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek if (!mAnimationListenerPool.empty()) { 7063af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek return mAnimationListenerPool.pop(); 7073af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek } 7083af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek 7093af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek // We need to create a new one, no reusable ones found 7103af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek return new AnimatorListenerAdapter() { 7113af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek private boolean mWasCancelled; 7123af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek 7133af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek @Override 7143af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek public void onAnimationEnd(Animator animation) { 7153af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek mAnimatorSet.remove(animation); 7163af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek if (mAnimatorSet.isEmpty() && !mWasCancelled) { 7173af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek onAnimationFinished(); 7183af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek } 7193af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek mAnimationListenerPool.push(this); 7203af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek } 7213af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek 7223af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek @Override 7233af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek public void onAnimationCancel(Animator animation) { 7243af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek mWasCancelled = true; 7253af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek } 7263af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek 7273af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek @Override 7283af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek public void onAnimationStart(Animator animation) { 7293af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek mWasCancelled = false; 7303af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek } 7313af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek }; 7323af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek } 7333af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek 73402af41efe54eb2cc8fde7311e4cf5f0e5ff2373cSelim Cinek private static <T> T getChildTag(View child, int tag) { 735eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek return (T) child.getTag(tag); 736eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek } 737eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek 738eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek /** 739eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek * Cancel the previous animator and get the duration of the new animation. 74039610545f0c2714a3526bc935effe57b421542d1Selim Cinek * 74160d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi * @param duration the new duration 742eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek * @param previousAnimator the animator which was running before 743eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek * @return the new duration 744eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek */ 74560d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi private long cancelAnimatorAndGetNewDuration(long duration, ValueAnimator previousAnimator) { 74660d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi long newDuration = duration; 747eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek if (previousAnimator != null) { 7488df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek // We take either the desired length of the new animation or the remaining time of 7498df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek // the previous animator, whichever is longer. 7508df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek newDuration = Math.max(previousAnimator.getDuration() 7518df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek - previousAnimator.getCurrentPlayTime(), newDuration); 7523af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek previousAnimator.cancel(); 753eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek } 7543af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek return newDuration; 755eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek } 756eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek 757eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek private void onAnimationFinished() { 758eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek mHostLayout.onChildAnimationFinished(); 759eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek } 760eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek 761eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek /** 762eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek * Process the animationEvents for a new animation 763eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek * 764eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek * @param animationEvents the animation events for the animation to perform 76539610545f0c2714a3526bc935effe57b421542d1Selim Cinek * @param finalState the final state to animate to 76639610545f0c2714a3526bc935effe57b421542d1Selim Cinek */ 767eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek private void processAnimationEvents( 7680dd6881ea481c855976214807c17595b34a2920aJorim Jaggi ArrayList<NotificationStackScrollLayout.AnimationEvent> animationEvents, 769572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek StackScrollState finalState) { 7708d9ff9c2c66bc1d3b92eb6992d58599ff80ed6dcSelim Cinek for (NotificationStackScrollLayout.AnimationEvent event : animationEvents) { 7718efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek final ExpandableView changingView = (ExpandableView) event.changingView; 7728efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek if (event.animationType == 7738efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek NotificationStackScrollLayout.AnimationEvent.ANIMATION_TYPE_ADD) { 7748efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek 7758efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek // This item is added, initialize it's properties. 776b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek StackViewState viewState = finalState 7778efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek .getViewStateForView(changingView); 7788efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek if (viewState == null) { 7798efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek // The position for this child was never generated, let's continue. 7808efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek continue; 7818efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek } 7828efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek if (changingView.getVisibility() == View.GONE) { 7838efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek // The view was set to gone but the state never removed 7848efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek finalState.removeViewStateForView(changingView); 7858efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek continue; 78639610545f0c2714a3526bc935effe57b421542d1Selim Cinek } 787b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek finalState.applyState(changingView, viewState); 7888efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek mNewAddChildren.add(changingView); 7898efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek 7908efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek } else if (event.animationType == 7918efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek NotificationStackScrollLayout.AnimationEvent.ANIMATION_TYPE_REMOVE) { 7928efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek if (changingView.getVisibility() == View.GONE) { 79395ed59283bd25fb363d13c000a7408bcafb5e93eSelim Cinek mHostLayout.getOverlay().remove(changingView); 7948efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek continue; 7958efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek } 7968efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek 7978efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek // Find the amount to translate up. This is needed in order to understand the 7988efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek // direction of the remove animation (either downwards or upwards) 799b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek StackViewState viewState = finalState 8008efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek .getViewStateForView(event.viewAfterChangingView); 8018efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek int actualHeight = changingView.getActualHeight(); 8028efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek // upwards by default 8038efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek float translationDirection = -1.0f; 8048efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek if (viewState != null) { 8058efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek // there was a view after this one, Approximate the distance the next child 8068efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek // travelled 8078efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek translationDirection = ((viewState.yTranslation 8088efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek - (changingView.getTranslationY() + actualHeight / 2.0f)) * 2 / 8098efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek actualHeight); 8108efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek translationDirection = Math.max(Math.min(translationDirection, 1.0f),-1.0f); 8118efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek 8128efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek } 81360d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi changingView.performRemoveAnimation(ANIMATION_DURATION_APPEAR_DISAPPEAR, 81460d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi translationDirection, new Runnable() { 8158efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek @Override 8168efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek public void run() { 8178efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek // remove the temporary overlay 8188efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek mHostLayout.getOverlay().remove(changingView); 8198efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek } 8208efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek }); 821b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek } else if (event.animationType == 822f336f4c13ad3be15e2b44266cd786685975425f2Selim Cinek NotificationStackScrollLayout.AnimationEvent.ANIMATION_TYPE_REMOVE_SWIPED_OUT) { 823f336f4c13ad3be15e2b44266cd786685975425f2Selim Cinek // A race condition can trigger the view to be added to the overlay even though 824f336f4c13ad3be15e2b44266cd786685975425f2Selim Cinek // it is swiped out. So let's remove it 825f336f4c13ad3be15e2b44266cd786685975425f2Selim Cinek mHostLayout.getOverlay().remove(changingView); 826b5605e58cb8080c8c887b1885336b707596c8094Selim Cinek } else if (event.animationType == NotificationStackScrollLayout 827b5605e58cb8080c8c887b1885336b707596c8094Selim Cinek .AnimationEvent.ANIMATION_TYPE_GROUP_EXPANSION_CHANGED) { 828b5605e58cb8080c8c887b1885336b707596c8094Selim Cinek ExpandableNotificationRow row = (ExpandableNotificationRow) event.changingView; 829b5605e58cb8080c8c887b1885336b707596c8094Selim Cinek row.prepareExpansionChanged(finalState); 830b5605e58cb8080c8c887b1885336b707596c8094Selim Cinek mChildExpandingView = row; 831572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek } 8328efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek mNewEvents.add(event); 833572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek } 834572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek } 8358d9ff9c2c66bc1d3b92eb6992d58599ff80ed6dcSelim Cinek 836475b21dfe517ec04f435f6b02f4a53083d040db4Jorim Jaggi public void animateOverScrollToAmount(float targetAmount, final boolean onTop, 837475b21dfe517ec04f435f6b02f4a53083d040db4Jorim Jaggi final boolean isRubberbanded) { 8388d9ff9c2c66bc1d3b92eb6992d58599ff80ed6dcSelim Cinek final float startOverScrollAmount = mHostLayout.getCurrentOverScrollAmount(onTop); 8399012958742c7a66b37ba5f2196f9086bb1980e6bJorim Jaggi if (targetAmount == startOverScrollAmount) { 8409012958742c7a66b37ba5f2196f9086bb1980e6bJorim Jaggi return; 8419012958742c7a66b37ba5f2196f9086bb1980e6bJorim Jaggi } 8428d9ff9c2c66bc1d3b92eb6992d58599ff80ed6dcSelim Cinek cancelOverScrollAnimators(onTop); 8438d9ff9c2c66bc1d3b92eb6992d58599ff80ed6dcSelim Cinek ValueAnimator overScrollAnimator = ValueAnimator.ofFloat(startOverScrollAmount, 8448d9ff9c2c66bc1d3b92eb6992d58599ff80ed6dcSelim Cinek targetAmount); 8458d9ff9c2c66bc1d3b92eb6992d58599ff80ed6dcSelim Cinek overScrollAnimator.setDuration(ANIMATION_DURATION_STANDARD); 8468d9ff9c2c66bc1d3b92eb6992d58599ff80ed6dcSelim Cinek overScrollAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { 8478d9ff9c2c66bc1d3b92eb6992d58599ff80ed6dcSelim Cinek @Override 8488d9ff9c2c66bc1d3b92eb6992d58599ff80ed6dcSelim Cinek public void onAnimationUpdate(ValueAnimator animation) { 8498d9ff9c2c66bc1d3b92eb6992d58599ff80ed6dcSelim Cinek float currentOverScroll = (float) animation.getAnimatedValue(); 85047c85a3525dcd0bbd3168632830e8ab491d18462Jorim Jaggi mHostLayout.setOverScrollAmount( 851475b21dfe517ec04f435f6b02f4a53083d040db4Jorim Jaggi currentOverScroll, onTop, false /* animate */, false /* cancelAnimators */, 852475b21dfe517ec04f435f6b02f4a53083d040db4Jorim Jaggi isRubberbanded); 8538d9ff9c2c66bc1d3b92eb6992d58599ff80ed6dcSelim Cinek } 8548d9ff9c2c66bc1d3b92eb6992d58599ff80ed6dcSelim Cinek }); 8558d9ff9c2c66bc1d3b92eb6992d58599ff80ed6dcSelim Cinek overScrollAnimator.setInterpolator(mFastOutSlowInInterpolator); 85647c85a3525dcd0bbd3168632830e8ab491d18462Jorim Jaggi overScrollAnimator.addListener(new AnimatorListenerAdapter() { 85747c85a3525dcd0bbd3168632830e8ab491d18462Jorim Jaggi @Override 85847c85a3525dcd0bbd3168632830e8ab491d18462Jorim Jaggi public void onAnimationEnd(Animator animation) { 85947c85a3525dcd0bbd3168632830e8ab491d18462Jorim Jaggi if (onTop) { 86047c85a3525dcd0bbd3168632830e8ab491d18462Jorim Jaggi mTopOverScrollAnimator = null; 86147c85a3525dcd0bbd3168632830e8ab491d18462Jorim Jaggi } else { 86247c85a3525dcd0bbd3168632830e8ab491d18462Jorim Jaggi mBottomOverScrollAnimator = null; 86347c85a3525dcd0bbd3168632830e8ab491d18462Jorim Jaggi } 86447c85a3525dcd0bbd3168632830e8ab491d18462Jorim Jaggi } 86547c85a3525dcd0bbd3168632830e8ab491d18462Jorim Jaggi }); 8668d9ff9c2c66bc1d3b92eb6992d58599ff80ed6dcSelim Cinek overScrollAnimator.start(); 8678d9ff9c2c66bc1d3b92eb6992d58599ff80ed6dcSelim Cinek if (onTop) { 8688d9ff9c2c66bc1d3b92eb6992d58599ff80ed6dcSelim Cinek mTopOverScrollAnimator = overScrollAnimator; 8698d9ff9c2c66bc1d3b92eb6992d58599ff80ed6dcSelim Cinek } else { 8708d9ff9c2c66bc1d3b92eb6992d58599ff80ed6dcSelim Cinek mBottomOverScrollAnimator = overScrollAnimator; 8718d9ff9c2c66bc1d3b92eb6992d58599ff80ed6dcSelim Cinek } 8728d9ff9c2c66bc1d3b92eb6992d58599ff80ed6dcSelim Cinek } 8738d9ff9c2c66bc1d3b92eb6992d58599ff80ed6dcSelim Cinek 8748d9ff9c2c66bc1d3b92eb6992d58599ff80ed6dcSelim Cinek public void cancelOverScrollAnimators(boolean onTop) { 8758d9ff9c2c66bc1d3b92eb6992d58599ff80ed6dcSelim Cinek ValueAnimator currentAnimator = onTop ? mTopOverScrollAnimator : mBottomOverScrollAnimator; 8768d9ff9c2c66bc1d3b92eb6992d58599ff80ed6dcSelim Cinek if (currentAnimator != null) { 8778d9ff9c2c66bc1d3b92eb6992d58599ff80ed6dcSelim Cinek currentAnimator.cancel(); 8788d9ff9c2c66bc1d3b92eb6992d58599ff80ed6dcSelim Cinek } 8798d9ff9c2c66bc1d3b92eb6992d58599ff80ed6dcSelim Cinek } 88002af41efe54eb2cc8fde7311e4cf5f0e5ff2373cSelim Cinek 88102af41efe54eb2cc8fde7311e4cf5f0e5ff2373cSelim Cinek /** 88202af41efe54eb2cc8fde7311e4cf5f0e5ff2373cSelim Cinek * Get the end value of the height animation running on a view or the actualHeight 88302af41efe54eb2cc8fde7311e4cf5f0e5ff2373cSelim Cinek * if no animation is running. 88402af41efe54eb2cc8fde7311e4cf5f0e5ff2373cSelim Cinek */ 88502af41efe54eb2cc8fde7311e4cf5f0e5ff2373cSelim Cinek public static int getFinalActualHeight(ExpandableView view) { 88602af41efe54eb2cc8fde7311e4cf5f0e5ff2373cSelim Cinek if (view == null) { 88702af41efe54eb2cc8fde7311e4cf5f0e5ff2373cSelim Cinek return 0; 88802af41efe54eb2cc8fde7311e4cf5f0e5ff2373cSelim Cinek } 88902af41efe54eb2cc8fde7311e4cf5f0e5ff2373cSelim Cinek ValueAnimator heightAnimator = getChildTag(view, TAG_ANIMATOR_HEIGHT); 89002af41efe54eb2cc8fde7311e4cf5f0e5ff2373cSelim Cinek if (heightAnimator == null) { 89102af41efe54eb2cc8fde7311e4cf5f0e5ff2373cSelim Cinek return view.getActualHeight(); 89202af41efe54eb2cc8fde7311e4cf5f0e5ff2373cSelim Cinek } else { 89302af41efe54eb2cc8fde7311e4cf5f0e5ff2373cSelim Cinek return getChildTag(view, TAG_END_HEIGHT); 89402af41efe54eb2cc8fde7311e4cf5f0e5ff2373cSelim Cinek } 89502af41efe54eb2cc8fde7311e4cf5f0e5ff2373cSelim Cinek } 896572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek} 897