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; 320fccc729fd3b19a62efd90ae13b207faa3d1e8bbSelim Cinekimport com.android.systemui.statusbar.policy.HeadsUpManager; 33572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek 34572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinekimport java.util.ArrayList; 35eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinekimport java.util.HashSet; 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; 48a7840af09d808ecd3f7547bbdea58d4422bd4d8fSelim Cinek public static final int ANIMATION_DURATION_HEADS_UP_APPEAR = 650; 49a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek public static final int ANIMATION_DURATION_HEADS_UP_DISAPPEAR = 230; 508efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek public static final int ANIMATION_DELAY_PER_ELEMENT_INTERRUPTING = 80; 51b5605e58cb8080c8c887b1885336b707596c8094Selim Cinek public static final int ANIMATION_DELAY_PER_ELEMENT_EXPAND_CHILDREN = 54; 528efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek public static final int ANIMATION_DELAY_PER_ELEMENT_MANUAL = 32; 5360d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi public static final int ANIMATION_DELAY_PER_ELEMENT_GO_TO_FULL_SHADE = 48; 544e857f4ef0357e05806819d0488a73a12208fe8fJorim Jaggi public static final int ANIMATION_DELAY_PER_ELEMENT_DARK = 24; 55b5605e58cb8080c8c887b1885336b707596c8094Selim Cinek public static final int DELAY_EFFECT_MAX_INDEX_DIFFERENCE = 2; 56b5605e58cb8080c8c887b1885336b707596c8094Selim Cinek public static final int DELAY_EFFECT_MAX_INDEX_DIFFERENCE_CHILDREN = 3; 575aa045cc6bca84f5c11f1a99999546ba5e5949a5Jorim Jaggi 58eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek private static final int TAG_ANIMATOR_TRANSLATION_Y = R.id.translation_y_animator_tag; 59eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek private static final int TAG_ANIMATOR_TRANSLATION_Z = R.id.translation_z_animator_tag; 60d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi private static final int TAG_ANIMATOR_SCALE = R.id.scale_animator_tag; 61eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek private static final int TAG_ANIMATOR_ALPHA = R.id.alpha_animator_tag; 62eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek private static final int TAG_ANIMATOR_HEIGHT = R.id.height_animator_tag; 63eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek private static final int TAG_ANIMATOR_TOP_INSET = R.id.top_inset_animator_tag; 64eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek private static final int TAG_END_TRANSLATION_Y = R.id.translation_y_animator_end_value_tag; 65eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek private static final int TAG_END_TRANSLATION_Z = R.id.translation_z_animator_end_value_tag; 66d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi private static final int TAG_END_SCALE = R.id.scale_animator_end_value_tag; 67eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek private static final int TAG_END_ALPHA = R.id.alpha_animator_end_value_tag; 68eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek private static final int TAG_END_HEIGHT = R.id.height_animator_end_value_tag; 69eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek private static final int TAG_END_TOP_INSET = R.id.top_inset_animator_end_value_tag; 708df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek private static final int TAG_START_TRANSLATION_Y = R.id.translation_y_animator_start_value_tag; 718df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek private static final int TAG_START_TRANSLATION_Z = R.id.translation_z_animator_start_value_tag; 728df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek private static final int TAG_START_SCALE = R.id.scale_animator_start_value_tag; 738df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek private static final int TAG_START_ALPHA = R.id.alpha_animator_start_value_tag; 748df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek private static final int TAG_START_HEIGHT = R.id.height_animator_start_value_tag; 758df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek private static final int TAG_START_TOP_INSET = R.id.top_inset_animator_start_value_tag; 76572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek 77572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek private final Interpolator mFastOutSlowInInterpolator; 78a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek private final Interpolator mHeadsUpAppearInterpolator; 7960d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi private final int mGoToFullShadeAppearingTranslation; 80684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek private final StackViewState mTmpState = new StackViewState(); 81572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek public NotificationStackScrollLayout mHostLayout; 82eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek private ArrayList<NotificationStackScrollLayout.AnimationEvent> mNewEvents = 83eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek new ArrayList<>(); 848efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek private ArrayList<View> mNewAddChildren = new ArrayList<>(); 85a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek private HashSet<View> mHeadsUpAppearChildren = new HashSet<>(); 86a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek private HashSet<View> mHeadsUpDisappearChildren = new HashSet<>(); 87a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek private HashSet<Animator> mAnimatorSet = new HashSet<>(); 8860d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi private Stack<AnimatorListenerAdapter> mAnimationListenerPool = new Stack<>(); 89d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi private AnimationFilter mAnimationFilter = new AnimationFilter(); 905aa045cc6bca84f5c11f1a99999546ba5e5949a5Jorim Jaggi private long mCurrentLength; 91dbc3dce886cb7abba23a9f18e60d0f5af5c93226Jorim Jaggi private long mCurrentAdditionalDelay; 92572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek 9360d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi /** The current index for the last child which was not added in this event set. */ 9460d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi private int mCurrentLastNotAddedIndex; 958d9ff9c2c66bc1d3b92eb6992d58599ff80ed6dcSelim Cinek private ValueAnimator mTopOverScrollAnimator; 968d9ff9c2c66bc1d3b92eb6992d58599ff80ed6dcSelim Cinek private ValueAnimator mBottomOverScrollAnimator; 97b5605e58cb8080c8c887b1885336b707596c8094Selim Cinek private ExpandableNotificationRow mChildExpandingView; 98a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek private int mHeadsUpAppearHeightBottom; 99a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek private boolean mShadeExpanded; 1008f93763cf0f4b3c41f6b0ecac09b621eb0ca328bSelim Cinek private ArrayList<View> mChildrenToClearFromOverlay = new ArrayList<>(); 1018d9ff9c2c66bc1d3b92eb6992d58599ff80ed6dcSelim Cinek 102572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek public StackStateAnimator(NotificationStackScrollLayout hostLayout) { 103572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek mHostLayout = hostLayout; 104572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek mFastOutSlowInInterpolator = AnimationUtils.loadInterpolator(hostLayout.getContext(), 105eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek android.R.interpolator.fast_out_slow_in); 10660d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi mGoToFullShadeAppearingTranslation = 10760d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi hostLayout.getContext().getResources().getDimensionPixelSize( 10860d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi R.dimen.go_to_full_shade_appearing_translation); 109684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek mHeadsUpAppearInterpolator = new HeadsUpAppearInterpolator(); 110572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek } 111572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek 112572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek public boolean isRunning() { 113eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek return !mAnimatorSet.isEmpty(); 114572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek } 115572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek 116572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek public void startAnimationForEvents( 1170dd6881ea481c855976214807c17595b34a2920aJorim Jaggi ArrayList<NotificationStackScrollLayout.AnimationEvent> mAnimationEvents, 118dbc3dce886cb7abba23a9f18e60d0f5af5c93226Jorim Jaggi StackScrollState finalState, long additionalDelay) { 119eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek 120eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek processAnimationEvents(mAnimationEvents, finalState); 121eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek 122572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek int childCount = mHostLayout.getChildCount(); 123d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi mAnimationFilter.applyCombination(mNewEvents); 124dbc3dce886cb7abba23a9f18e60d0f5af5c93226Jorim Jaggi mCurrentAdditionalDelay = additionalDelay; 1255aa045cc6bca84f5c11f1a99999546ba5e5949a5Jorim Jaggi mCurrentLength = NotificationStackScrollLayout.AnimationEvent.combineLength(mNewEvents); 12660d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi mCurrentLastNotAddedIndex = findLastNotAddedIndex(finalState); 127572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek for (int i = 0; i < childCount; i++) { 128572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek final ExpandableView child = (ExpandableView) mHostLayout.getChildAt(i); 1298efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek 130b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek StackViewState viewState = finalState.getViewStateForView(child); 131a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek if (viewState == null || child.getVisibility() == View.GONE 132a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek || applyWithoutAnimation(child, viewState, finalState)) { 133572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek continue; 134572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek } 135572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek 136a272dfed9a4f31d8245099c0d99a73e79b90670cSelim Cinek child.setClipTopOptimization(0); 137b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek startStackAnimations(child, viewState, finalState, i, -1 /* fixedDelay */); 138572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek } 139eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek if (!isRunning()) { 140eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek // no child has preformed any animation, lets finish 141eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek onAnimationFinished(); 142eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek } 143a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek mHeadsUpAppearChildren.clear(); 144a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek mHeadsUpDisappearChildren.clear(); 1458efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek mNewEvents.clear(); 1468efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek mNewAddChildren.clear(); 147b5605e58cb8080c8c887b1885336b707596c8094Selim Cinek mChildExpandingView = null; 148572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek } 149572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek 150a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek /** 151a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek * Determines if a view should not perform an animation and applies it directly. 152a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek * 153a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek * @return true if no animation should be performed 154a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek */ 155a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek private boolean applyWithoutAnimation(ExpandableView child, StackViewState viewState, 156a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek StackScrollState finalState) { 157a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek if (mShadeExpanded) { 158a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek return false; 159a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek } 160a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek if (getChildTag(child, TAG_ANIMATOR_TRANSLATION_Y) != null) { 161a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek // A Y translation animation is running 162a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek return false; 163a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek } 164a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek if (mHeadsUpDisappearChildren.contains(child) || mHeadsUpAppearChildren.contains(child)) { 165a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek // This is a heads up animation 166a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek return false; 167a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek } 168131c1e2960fa5bdf54bfb6fcd5ac98c9f728f796Selim Cinek if (NotificationStackScrollLayout.isPinnedHeadsUp(child)) { 1691f3f544f21cab3728f749ab66cd6859e9dfcf389Selim Cinek // This is another headsUp which might move. Let's animate! 1701f3f544f21cab3728f749ab66cd6859e9dfcf389Selim Cinek return false; 1711f3f544f21cab3728f749ab66cd6859e9dfcf389Selim Cinek } 172a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek finalState.applyState(child, viewState); 173a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek return true; 174a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek } 175a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek 17660d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi private int findLastNotAddedIndex(StackScrollState finalState) { 17760d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi int childCount = mHostLayout.getChildCount(); 17860d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi for (int i = childCount - 1; i >= 0; i--) { 17960d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi final ExpandableView child = (ExpandableView) mHostLayout.getChildAt(i); 18060d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi 181b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek StackViewState viewState = finalState.getViewStateForView(child); 18260d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi if (viewState == null || child.getVisibility() == View.GONE) { 18360d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi continue; 18460d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi } 18560d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi if (!mNewAddChildren.contains(child)) { 18660d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi return viewState.notGoneIndex; 18760d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi } 18860d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi } 18960d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi return -1; 19060d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi } 19160d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi 192b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek 193eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek /** 194b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek * Start an animation to the given {@link StackViewState}. 195b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek * 196b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek * @param child the child to start the animation on 197b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek * @param viewState the {@link StackViewState} of the view to animate to 198b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek * @param finalState the final state after the animation 199b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek * @param i the index of the view; only relevant if the view is the speed bump and is 200b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek * ignored otherwise 201b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek * @param fixedDelay a fixed delay if desired or -1 if the delay should be calculated 202eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek */ 203b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek public void startStackAnimations(final ExpandableView child, StackViewState viewState, 204b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek StackScrollState finalState, int i, long fixedDelay) { 205eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek final float alpha = viewState.alpha; 206b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek boolean wasAdded = mNewAddChildren.contains(child); 207b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek long duration = mCurrentLength; 208b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek if (wasAdded && mAnimationFilter.hasGoToFullShadeEvent) { 209b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek child.setTranslationY(child.getTranslationY() + mGoToFullShadeAppearingTranslation); 210b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek float longerDurationFactor = viewState.notGoneIndex - mCurrentLastNotAddedIndex; 211b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek longerDurationFactor = (float) Math.pow(longerDurationFactor, 0.7f); 212b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek duration = ANIMATION_DURATION_APPEAR_DISAPPEAR + 50 + 213b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek (long) (100 * longerDurationFactor); 214eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek } 2158efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek boolean yTranslationChanging = child.getTranslationY() != viewState.yTranslation; 2168efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek boolean zTranslationChanging = child.getTranslationZ() != viewState.zTranslation; 2178efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek boolean scaleChanging = child.getScaleX() != viewState.scale; 2188efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek boolean alphaChanging = alpha != child.getAlpha(); 2198efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek boolean heightChanging = viewState.height != child.getActualHeight(); 2204e857f4ef0357e05806819d0488a73a12208fe8fJorim Jaggi boolean darkChanging = viewState.dark != child.isDark(); 221708a6c120da6750d281195ef15a240a5627efed4Selim Cinek boolean topInsetChanging = viewState.clipTopAmount != child.getClipTopAmount(); 2228efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek boolean hasDelays = mAnimationFilter.hasDelays; 2238efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek boolean isDelayRelevant = yTranslationChanging || zTranslationChanging || scaleChanging || 2244e857f4ef0357e05806819d0488a73a12208fe8fJorim Jaggi alphaChanging || heightChanging || topInsetChanging || darkChanging; 2258efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek long delay = 0; 226b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek if (fixedDelay != -1) { 227b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek delay = fixedDelay; 228b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek } else if (hasDelays && isDelayRelevant || wasAdded) { 229dbc3dce886cb7abba23a9f18e60d0f5af5c93226Jorim Jaggi delay = mCurrentAdditionalDelay + calculateChildAnimationDelay(viewState, finalState); 2308efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek } 2318efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek 232b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek startViewAnimations(child, viewState, delay, duration); 2338efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek 234eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek // start height animation 235d2319fbe6a53ac4c38ca02e4d8e32da86ed0994bSelim Cinek if (heightChanging && child.getActualHeight() != 0) { 236b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek startHeightAnimation(child, viewState, duration, delay); 23759b5a356b828fe60ea2874b0680a1bf7c84809a1Jorim Jaggi } 2388efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek 239708a6c120da6750d281195ef15a240a5627efed4Selim Cinek // start top inset animation 240708a6c120da6750d281195ef15a240a5627efed4Selim Cinek if (topInsetChanging) { 241b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek startInsetAnimation(child, viewState, duration, delay); 242708a6c120da6750d281195ef15a240a5627efed4Selim Cinek } 243708a6c120da6750d281195ef15a240a5627efed4Selim Cinek 244d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi // start dimmed animation 245b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek child.setDimmed(viewState.dimmed, mAnimationFilter.animateDimmed); 2468efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek 2473d2b94bf8e32640e57573ebb17911b1db9440231Selim Cinek // apply speed bump state 2483d2b94bf8e32640e57573ebb17911b1db9440231Selim Cinek child.setBelowSpeedBump(viewState.belowSpeedBump); 2493d2b94bf8e32640e57573ebb17911b1db9440231Selim Cinek 250ae44128776410abd11bd06ae700db9cc4606a773Jorim Jaggi // start hiding sensitive animation 251b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek child.setHideSensitive(viewState.hideSensitive, mAnimationFilter.animateHideSensitive, 252b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek delay, duration); 253ae44128776410abd11bd06ae700db9cc4606a773Jorim Jaggi 254021eee53e52c880a529ece28a3a9edb3cd0776b7Jorim Jaggi // start dark animation 255021eee53e52c880a529ece28a3a9edb3cd0776b7Jorim Jaggi child.setDark(viewState.dark, mAnimationFilter.animateDark, delay); 256021eee53e52c880a529ece28a3a9edb3cd0776b7Jorim Jaggi 2578efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek if (wasAdded) { 25860d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi child.performAddAnimation(delay, mCurrentLength); 2598efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek } 2608b73006a36b3000a9847534dcb01a2e7066e9d93Jorim Jaggi if (child instanceof SpeedBumpView) { 2618b73006a36b3000a9847534dcb01a2e7066e9d93Jorim Jaggi finalState.performSpeedBumpAnimation(i, (SpeedBumpView) child, viewState, 2628b73006a36b3000a9847534dcb01a2e7066e9d93Jorim Jaggi delay + duration); 263b5605e58cb8080c8c887b1885336b707596c8094Selim Cinek } else if (child instanceof ExpandableNotificationRow) { 264b5605e58cb8080c8c887b1885336b707596c8094Selim Cinek ExpandableNotificationRow row = (ExpandableNotificationRow) child; 265b5605e58cb8080c8c887b1885336b707596c8094Selim Cinek row.startChildAnimation(finalState, this, child == mChildExpandingView, delay, 266b5605e58cb8080c8c887b1885336b707596c8094Selim Cinek duration); 2678b73006a36b3000a9847534dcb01a2e7066e9d93Jorim Jaggi } 2688efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek } 2698efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek 270b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek /** 271b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek * Start an animation to a new {@link ViewState}. 272b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek * 273b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek * @param child the child to start the animation on 274b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek * @param viewState the {@link StackViewState} of the view to animate to 275b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek * @param delay a fixed delay 276b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek * @param duration the duration of the animation 277b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek */ 278b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek public void startViewAnimations(View child, ViewState viewState, long delay, long duration) { 279b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek boolean wasVisible = child.getVisibility() == View.VISIBLE; 280b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek final float alpha = viewState.alpha; 281b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek if (!wasVisible && alpha != 0 && !viewState.gone) { 282b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek child.setVisibility(View.VISIBLE); 283b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek } 284b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek boolean yTranslationChanging = child.getTranslationY() != viewState.yTranslation; 285b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek boolean zTranslationChanging = child.getTranslationZ() != viewState.zTranslation; 286b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek boolean scaleChanging = child.getScaleX() != viewState.scale; 287b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek float childAlpha = child.getVisibility() == View.INVISIBLE ? 0.0f : child.getAlpha(); 288b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek boolean alphaChanging = viewState.alpha != childAlpha; 2892cd45dfba6a9105a305ea20b110ba5ac078a9dc6Selim Cinek if (child instanceof ExpandableView) { 2902cd45dfba6a9105a305ea20b110ba5ac078a9dc6Selim Cinek // We don't want views to change visibility when they are animating to GONE 2912cd45dfba6a9105a305ea20b110ba5ac078a9dc6Selim Cinek alphaChanging &= !((ExpandableView) child).willBeGone(); 2922cd45dfba6a9105a305ea20b110ba5ac078a9dc6Selim Cinek } 293b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek 294b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek // start translationY animation 295b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek if (yTranslationChanging) { 296b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek startYTranslationAnimation(child, viewState, duration, delay); 297b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek } 298b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek 299b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek // start translationZ animation 300b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek if (zTranslationChanging) { 301b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek startZTranslationAnimation(child, viewState, duration, delay); 302b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek } 303b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek 304b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek // start scale animation 305b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek if (scaleChanging) { 306b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek startScaleAnimation(child, viewState, duration); 307b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek } 308b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek 309b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek // start alpha animation 310b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek if (alphaChanging && child.getTranslationX() == 0) { 311b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek startAlphaAnimation(child, viewState, duration, delay); 312b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek } 313b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek } 314b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek 315b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek private long calculateChildAnimationDelay(StackViewState viewState, 3168efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek StackScrollState finalState) { 3174e857f4ef0357e05806819d0488a73a12208fe8fJorim Jaggi if (mAnimationFilter.hasDarkEvent) { 3184e857f4ef0357e05806819d0488a73a12208fe8fJorim Jaggi return calculateDelayDark(viewState); 3194e857f4ef0357e05806819d0488a73a12208fe8fJorim Jaggi } 32060d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi if (mAnimationFilter.hasGoToFullShadeEvent) { 32160d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi return calculateDelayGoToFullShade(viewState); 32260d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi } 3238efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek long minDelay = 0; 3248efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek for (NotificationStackScrollLayout.AnimationEvent event : mNewEvents) { 3258efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek long delayPerElement = ANIMATION_DELAY_PER_ELEMENT_INTERRUPTING; 3268efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek switch (event.animationType) { 3278efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek case NotificationStackScrollLayout.AnimationEvent.ANIMATION_TYPE_ADD: { 3288efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek int ownIndex = viewState.notGoneIndex; 3298efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek int changingIndex = finalState 3308efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek .getViewStateForView(event.changingView).notGoneIndex; 3318efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek int difference = Math.abs(ownIndex - changingIndex); 3328efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek difference = Math.max(0, Math.min(DELAY_EFFECT_MAX_INDEX_DIFFERENCE, 3338efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek difference - 1)); 3348efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek long delay = (DELAY_EFFECT_MAX_INDEX_DIFFERENCE - difference) * delayPerElement; 3358efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek minDelay = Math.max(delay, minDelay); 3368efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek break; 3378efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek } 3388efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek case NotificationStackScrollLayout.AnimationEvent.ANIMATION_TYPE_REMOVE_SWIPED_OUT: 3398efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek delayPerElement = ANIMATION_DELAY_PER_ELEMENT_MANUAL; 3408efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek case NotificationStackScrollLayout.AnimationEvent.ANIMATION_TYPE_REMOVE: { 3418efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek int ownIndex = viewState.notGoneIndex; 3428efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek boolean noNextView = event.viewAfterChangingView == null; 3438efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek View viewAfterChangingView = noNextView 3448efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek ? mHostLayout.getLastChildNotGone() 3458efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek : event.viewAfterChangingView; 3468efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek 3478efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek int nextIndex = finalState 3488efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek .getViewStateForView(viewAfterChangingView).notGoneIndex; 3498efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek if (ownIndex >= nextIndex) { 3508efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek // we only have the view afterwards 3518efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek ownIndex++; 3528efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek } 3538efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek int difference = Math.abs(ownIndex - nextIndex); 3548efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek difference = Math.max(0, Math.min(DELAY_EFFECT_MAX_INDEX_DIFFERENCE, 3558efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek difference - 1)); 3568efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek long delay = difference * delayPerElement; 3578efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek minDelay = Math.max(delay, minDelay); 3588efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek break; 3598efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek } 3608efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek default: 3618efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek break; 3628efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek } 3638efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek } 3648efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek return minDelay; 365572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek } 366572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek 367b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek private long calculateDelayDark(StackViewState viewState) { 3682a5e452f4f70d2dd717946dd50486cc66ea4cc43Jorim Jaggi int referenceIndex; 3692a5e452f4f70d2dd717946dd50486cc66ea4cc43Jorim Jaggi if (mAnimationFilter.darkAnimationOriginIndex == 3702a5e452f4f70d2dd717946dd50486cc66ea4cc43Jorim Jaggi NotificationStackScrollLayout.AnimationEvent.DARK_ANIMATION_ORIGIN_INDEX_ABOVE) { 3712a5e452f4f70d2dd717946dd50486cc66ea4cc43Jorim Jaggi referenceIndex = 0; 3722a5e452f4f70d2dd717946dd50486cc66ea4cc43Jorim Jaggi } else if (mAnimationFilter.darkAnimationOriginIndex == 3732a5e452f4f70d2dd717946dd50486cc66ea4cc43Jorim Jaggi NotificationStackScrollLayout.AnimationEvent.DARK_ANIMATION_ORIGIN_INDEX_BELOW) { 3742a5e452f4f70d2dd717946dd50486cc66ea4cc43Jorim Jaggi referenceIndex = mHostLayout.getNotGoneChildCount() - 1; 3752a5e452f4f70d2dd717946dd50486cc66ea4cc43Jorim Jaggi } else { 3762a5e452f4f70d2dd717946dd50486cc66ea4cc43Jorim Jaggi referenceIndex = mAnimationFilter.darkAnimationOriginIndex; 3772a5e452f4f70d2dd717946dd50486cc66ea4cc43Jorim Jaggi } 3782a5e452f4f70d2dd717946dd50486cc66ea4cc43Jorim Jaggi return Math.abs(referenceIndex - viewState.notGoneIndex) * ANIMATION_DELAY_PER_ELEMENT_DARK; 3794e857f4ef0357e05806819d0488a73a12208fe8fJorim Jaggi } 3804e857f4ef0357e05806819d0488a73a12208fe8fJorim Jaggi 381b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek private long calculateDelayGoToFullShade(StackViewState viewState) { 38260d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi float index = viewState.notGoneIndex; 38360d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi index = (float) Math.pow(index, 0.7f); 38460d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi return (long) (index * ANIMATION_DELAY_PER_ELEMENT_GO_TO_FULL_SHADE); 38560d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi } 38660d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi 387eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek private void startHeightAnimation(final ExpandableView child, 388b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek StackViewState viewState, long duration, long delay) { 3898df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek Integer previousStartValue = getChildTag(child, TAG_START_HEIGHT); 390d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi Integer previousEndValue = getChildTag(child, TAG_END_HEIGHT); 3918df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek int newEndValue = viewState.height; 3928df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek if (previousEndValue != null && previousEndValue == newEndValue) { 393eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek return; 394eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek } 395eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek ValueAnimator previousAnimator = getChildTag(child, TAG_ANIMATOR_HEIGHT); 3968df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek if (!mAnimationFilter.animateHeight) { 3978df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek // just a local update was performed 3988df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek if (previousAnimator != null) { 3998df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek // we need to increase all animation keyframes of the previous animator by the 4008df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek // relative change to the end value 4018df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek PropertyValuesHolder[] values = previousAnimator.getValues(); 4028df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek int relativeDiff = newEndValue - previousEndValue; 4038df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek int newStartValue = previousStartValue + relativeDiff; 4048df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek values[0].setIntValues(newStartValue, newEndValue); 4058df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek child.setTag(TAG_START_HEIGHT, newStartValue); 4068df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek child.setTag(TAG_END_HEIGHT, newEndValue); 4078df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek previousAnimator.setCurrentPlayTime(previousAnimator.getCurrentPlayTime()); 4088df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek return; 4098df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek } else { 4108df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek // no new animation needed, let's just apply the value 4118df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek child.setActualHeight(newEndValue, false); 4128df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek return; 413eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek } 414eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek } 415eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek 4168df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek ValueAnimator animator = ValueAnimator.ofInt(child.getActualHeight(), newEndValue); 417eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { 418572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek @Override 419572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek public void onAnimationUpdate(ValueAnimator animation) { 420d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi child.setActualHeight((int) animation.getAnimatedValue(), 421d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi false /* notifyListeners */); 422572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek } 423572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek }); 424eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek animator.setInterpolator(mFastOutSlowInInterpolator); 42560d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi long newDuration = cancelAnimatorAndGetNewDuration(duration, previousAnimator); 426eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek animator.setDuration(newDuration); 4278efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek if (delay > 0 && (previousAnimator == null || !previousAnimator.isRunning())) { 4288efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek animator.setStartDelay(delay); 4298efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek } 430eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek animator.addListener(getGlobalAnimationFinishedListener()); 431eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek // remove the tag when the animation is finished 432eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek animator.addListener(new AnimatorListenerAdapter() { 433eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek @Override 434eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek public void onAnimationEnd(Animator animation) { 435eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek child.setTag(TAG_ANIMATOR_HEIGHT, null); 4368df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek child.setTag(TAG_START_HEIGHT, null); 437eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek child.setTag(TAG_END_HEIGHT, null); 438eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek } 439eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek }); 4408efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek startAnimator(animator); 441eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek child.setTag(TAG_ANIMATOR_HEIGHT, animator); 4428df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek child.setTag(TAG_START_HEIGHT, child.getActualHeight()); 4438df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek child.setTag(TAG_END_HEIGHT, newEndValue); 444eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek } 445eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek 446708a6c120da6750d281195ef15a240a5627efed4Selim Cinek private void startInsetAnimation(final ExpandableView child, 447b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek StackViewState viewState, long duration, long delay) { 448708a6c120da6750d281195ef15a240a5627efed4Selim Cinek Integer previousStartValue = getChildTag(child, TAG_START_TOP_INSET); 449708a6c120da6750d281195ef15a240a5627efed4Selim Cinek Integer previousEndValue = getChildTag(child, TAG_END_TOP_INSET); 450708a6c120da6750d281195ef15a240a5627efed4Selim Cinek int newEndValue = viewState.clipTopAmount; 451708a6c120da6750d281195ef15a240a5627efed4Selim Cinek if (previousEndValue != null && previousEndValue == newEndValue) { 452708a6c120da6750d281195ef15a240a5627efed4Selim Cinek return; 453708a6c120da6750d281195ef15a240a5627efed4Selim Cinek } 454708a6c120da6750d281195ef15a240a5627efed4Selim Cinek ValueAnimator previousAnimator = getChildTag(child, TAG_ANIMATOR_TOP_INSET); 455708a6c120da6750d281195ef15a240a5627efed4Selim Cinek if (!mAnimationFilter.animateTopInset) { 456708a6c120da6750d281195ef15a240a5627efed4Selim Cinek // just a local update was performed 457708a6c120da6750d281195ef15a240a5627efed4Selim Cinek if (previousAnimator != null) { 458708a6c120da6750d281195ef15a240a5627efed4Selim Cinek // we need to increase all animation keyframes of the previous animator by the 459708a6c120da6750d281195ef15a240a5627efed4Selim Cinek // relative change to the end value 460708a6c120da6750d281195ef15a240a5627efed4Selim Cinek PropertyValuesHolder[] values = previousAnimator.getValues(); 461708a6c120da6750d281195ef15a240a5627efed4Selim Cinek int relativeDiff = newEndValue - previousEndValue; 462708a6c120da6750d281195ef15a240a5627efed4Selim Cinek int newStartValue = previousStartValue + relativeDiff; 463708a6c120da6750d281195ef15a240a5627efed4Selim Cinek values[0].setIntValues(newStartValue, newEndValue); 464708a6c120da6750d281195ef15a240a5627efed4Selim Cinek child.setTag(TAG_START_TOP_INSET, newStartValue); 465708a6c120da6750d281195ef15a240a5627efed4Selim Cinek child.setTag(TAG_END_TOP_INSET, newEndValue); 466708a6c120da6750d281195ef15a240a5627efed4Selim Cinek previousAnimator.setCurrentPlayTime(previousAnimator.getCurrentPlayTime()); 467708a6c120da6750d281195ef15a240a5627efed4Selim Cinek return; 468708a6c120da6750d281195ef15a240a5627efed4Selim Cinek } else { 469708a6c120da6750d281195ef15a240a5627efed4Selim Cinek // no new animation needed, let's just apply the value 470708a6c120da6750d281195ef15a240a5627efed4Selim Cinek child.setClipTopAmount(newEndValue); 471708a6c120da6750d281195ef15a240a5627efed4Selim Cinek return; 472708a6c120da6750d281195ef15a240a5627efed4Selim Cinek } 473708a6c120da6750d281195ef15a240a5627efed4Selim Cinek } 474708a6c120da6750d281195ef15a240a5627efed4Selim Cinek 475708a6c120da6750d281195ef15a240a5627efed4Selim Cinek ValueAnimator animator = ValueAnimator.ofInt(child.getClipTopAmount(), newEndValue); 476708a6c120da6750d281195ef15a240a5627efed4Selim Cinek animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { 477708a6c120da6750d281195ef15a240a5627efed4Selim Cinek @Override 478708a6c120da6750d281195ef15a240a5627efed4Selim Cinek public void onAnimationUpdate(ValueAnimator animation) { 479708a6c120da6750d281195ef15a240a5627efed4Selim Cinek child.setClipTopAmount((int) animation.getAnimatedValue()); 480708a6c120da6750d281195ef15a240a5627efed4Selim Cinek } 481708a6c120da6750d281195ef15a240a5627efed4Selim Cinek }); 482708a6c120da6750d281195ef15a240a5627efed4Selim Cinek animator.setInterpolator(mFastOutSlowInInterpolator); 48360d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi long newDuration = cancelAnimatorAndGetNewDuration(duration, previousAnimator); 484708a6c120da6750d281195ef15a240a5627efed4Selim Cinek animator.setDuration(newDuration); 485708a6c120da6750d281195ef15a240a5627efed4Selim Cinek if (delay > 0 && (previousAnimator == null || !previousAnimator.isRunning())) { 486708a6c120da6750d281195ef15a240a5627efed4Selim Cinek animator.setStartDelay(delay); 487708a6c120da6750d281195ef15a240a5627efed4Selim Cinek } 488708a6c120da6750d281195ef15a240a5627efed4Selim Cinek animator.addListener(getGlobalAnimationFinishedListener()); 489708a6c120da6750d281195ef15a240a5627efed4Selim Cinek // remove the tag when the animation is finished 490708a6c120da6750d281195ef15a240a5627efed4Selim Cinek animator.addListener(new AnimatorListenerAdapter() { 491708a6c120da6750d281195ef15a240a5627efed4Selim Cinek @Override 492708a6c120da6750d281195ef15a240a5627efed4Selim Cinek public void onAnimationEnd(Animator animation) { 493708a6c120da6750d281195ef15a240a5627efed4Selim Cinek child.setTag(TAG_ANIMATOR_TOP_INSET, null); 494708a6c120da6750d281195ef15a240a5627efed4Selim Cinek child.setTag(TAG_START_TOP_INSET, null); 495708a6c120da6750d281195ef15a240a5627efed4Selim Cinek child.setTag(TAG_END_TOP_INSET, null); 496708a6c120da6750d281195ef15a240a5627efed4Selim Cinek } 497708a6c120da6750d281195ef15a240a5627efed4Selim Cinek }); 498708a6c120da6750d281195ef15a240a5627efed4Selim Cinek startAnimator(animator); 499708a6c120da6750d281195ef15a240a5627efed4Selim Cinek child.setTag(TAG_ANIMATOR_TOP_INSET, animator); 500708a6c120da6750d281195ef15a240a5627efed4Selim Cinek child.setTag(TAG_START_TOP_INSET, child.getClipTopAmount()); 501708a6c120da6750d281195ef15a240a5627efed4Selim Cinek child.setTag(TAG_END_TOP_INSET, newEndValue); 502708a6c120da6750d281195ef15a240a5627efed4Selim Cinek } 503708a6c120da6750d281195ef15a240a5627efed4Selim Cinek 504b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek private void startAlphaAnimation(final View child, 505b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek final ViewState viewState, long duration, long delay) { 5068df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek Float previousStartValue = getChildTag(child,TAG_START_ALPHA); 507eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek Float previousEndValue = getChildTag(child,TAG_END_ALPHA); 5088df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek final float newEndValue = viewState.alpha; 5098df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek if (previousEndValue != null && previousEndValue == newEndValue) { 510eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek return; 511eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek } 512eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek ObjectAnimator previousAnimator = getChildTag(child, TAG_ANIMATOR_ALPHA); 5138df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek if (!mAnimationFilter.animateAlpha) { 5148df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek // just a local update was performed 5158df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek if (previousAnimator != null) { 5168df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek // we need to increase all animation keyframes of the previous animator by the 5178df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek // relative change to the end value 5188df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek PropertyValuesHolder[] values = previousAnimator.getValues(); 5198df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek float relativeDiff = newEndValue - previousEndValue; 5208df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek float newStartValue = previousStartValue + relativeDiff; 5218df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek values[0].setFloatValues(newStartValue, newEndValue); 5228df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek child.setTag(TAG_START_ALPHA, newStartValue); 5238df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek child.setTag(TAG_END_ALPHA, newEndValue); 5248df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek previousAnimator.setCurrentPlayTime(previousAnimator.getCurrentPlayTime()); 5258df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek return; 5268df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek } else { 5278df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek // no new animation needed, let's just apply the value 5288df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek child.setAlpha(newEndValue); 5298df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek if (newEndValue == 0) { 5308df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek child.setVisibility(View.INVISIBLE); 5318df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek } 532eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek } 533eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek } 534eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek 535eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek ObjectAnimator animator = ObjectAnimator.ofFloat(child, View.ALPHA, 5368df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek child.getAlpha(), newEndValue); 537eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek animator.setInterpolator(mFastOutSlowInInterpolator); 538eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek // Handle layer type 539eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek child.setLayerType(View.LAYER_TYPE_HARDWARE, null); 540eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek animator.addListener(new AnimatorListenerAdapter() { 541eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek public boolean mWasCancelled; 542eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek 543eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek @Override 544eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek public void onAnimationEnd(Animator animation) { 5451efb240c1a0aeca9492cf8891794712adfdb1fa7Selim Cinek child.setLayerType(View.LAYER_TYPE_NONE, null); 5468df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek if (newEndValue == 0 && !mWasCancelled) { 547eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek child.setVisibility(View.INVISIBLE); 548eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek } 549c5baa3eb0893cb764e7810f8c68e89b04653df86Selim Cinek // remove the tag when the animation is finished 550eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek child.setTag(TAG_ANIMATOR_ALPHA, null); 5518df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek child.setTag(TAG_START_ALPHA, null); 552eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek child.setTag(TAG_END_ALPHA, null); 553eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek } 554eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek 555eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek @Override 556eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek public void onAnimationCancel(Animator animation) { 557eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek mWasCancelled = true; 558eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek } 559eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek 560eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek @Override 561eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek public void onAnimationStart(Animator animation) { 562eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek mWasCancelled = false; 563eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek } 564eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek }); 56560d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi long newDuration = cancelAnimatorAndGetNewDuration(duration, previousAnimator); 5663af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek animator.setDuration(newDuration); 5678efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek if (delay > 0 && (previousAnimator == null || !previousAnimator.isRunning())) { 5688efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek animator.setStartDelay(delay); 5698efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek } 570eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek animator.addListener(getGlobalAnimationFinishedListener()); 571eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek 5728efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek startAnimator(animator); 573eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek child.setTag(TAG_ANIMATOR_ALPHA, animator); 5748df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek child.setTag(TAG_START_ALPHA, child.getAlpha()); 5758df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek child.setTag(TAG_END_ALPHA, newEndValue); 576572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek } 577572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek 578b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek private void startZTranslationAnimation(final View child, 579b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek final ViewState viewState, long duration, long delay) { 5808df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek Float previousStartValue = getChildTag(child,TAG_START_TRANSLATION_Z); 581eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek Float previousEndValue = getChildTag(child,TAG_END_TRANSLATION_Z); 5828df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek float newEndValue = viewState.zTranslation; 5838df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek if (previousEndValue != null && previousEndValue == newEndValue) { 584eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek return; 585eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek } 586eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek ObjectAnimator previousAnimator = getChildTag(child, TAG_ANIMATOR_TRANSLATION_Z); 5878df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek if (!mAnimationFilter.animateZ) { 5888df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek // just a local update was performed 5898df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek if (previousAnimator != null) { 5908df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek // we need to increase all animation keyframes of the previous animator by the 5918df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek // relative change to the end value 5928df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek PropertyValuesHolder[] values = previousAnimator.getValues(); 5938df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek float relativeDiff = newEndValue - previousEndValue; 5948df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek float newStartValue = previousStartValue + relativeDiff; 5958df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek values[0].setFloatValues(newStartValue, newEndValue); 5968df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek child.setTag(TAG_START_TRANSLATION_Z, newStartValue); 5978df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek child.setTag(TAG_END_TRANSLATION_Z, newEndValue); 5988df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek previousAnimator.setCurrentPlayTime(previousAnimator.getCurrentPlayTime()); 5998df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek return; 6008df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek } else { 6018df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek // no new animation needed, let's just apply the value 6028df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek child.setTranslationZ(newEndValue); 603eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek } 604eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek } 605eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek 606eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek ObjectAnimator animator = ObjectAnimator.ofFloat(child, View.TRANSLATION_Z, 6078df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek child.getTranslationZ(), newEndValue); 608eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek animator.setInterpolator(mFastOutSlowInInterpolator); 60960d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi long newDuration = cancelAnimatorAndGetNewDuration(duration, previousAnimator); 6103af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek animator.setDuration(newDuration); 6118efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek if (delay > 0 && (previousAnimator == null || !previousAnimator.isRunning())) { 6128efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek animator.setStartDelay(delay); 6138efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek } 614eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek animator.addListener(getGlobalAnimationFinishedListener()); 615eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek // remove the tag when the animation is finished 616eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek animator.addListener(new AnimatorListenerAdapter() { 617eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek @Override 618eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek public void onAnimationEnd(Animator animation) { 619eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek child.setTag(TAG_ANIMATOR_TRANSLATION_Z, null); 6208df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek child.setTag(TAG_START_TRANSLATION_Z, null); 621eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek child.setTag(TAG_END_TRANSLATION_Z, null); 622eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek } 623eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek }); 6248efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek startAnimator(animator); 625eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek child.setTag(TAG_ANIMATOR_TRANSLATION_Z, animator); 6268df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek child.setTag(TAG_START_TRANSLATION_Z, child.getTranslationZ()); 6278df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek child.setTag(TAG_END_TRANSLATION_Z, newEndValue); 628eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek } 629eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek 630b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek private void startYTranslationAnimation(final View child, 631b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek ViewState viewState, long duration, long delay) { 6328df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek Float previousStartValue = getChildTag(child,TAG_START_TRANSLATION_Y); 633eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek Float previousEndValue = getChildTag(child,TAG_END_TRANSLATION_Y); 6348df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek float newEndValue = viewState.yTranslation; 6358df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek if (previousEndValue != null && previousEndValue == newEndValue) { 636eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek return; 637eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek } 638eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek ObjectAnimator previousAnimator = getChildTag(child, TAG_ANIMATOR_TRANSLATION_Y); 6398df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek if (!mAnimationFilter.animateY) { 6408df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek // just a local update was performed 6418df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek if (previousAnimator != null) { 6428df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek // we need to increase all animation keyframes of the previous animator by the 6438df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek // relative change to the end value 6448df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek PropertyValuesHolder[] values = previousAnimator.getValues(); 6458df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek float relativeDiff = newEndValue - previousEndValue; 6468df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek float newStartValue = previousStartValue + relativeDiff; 6478df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek values[0].setFloatValues(newStartValue, newEndValue); 6488df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek child.setTag(TAG_START_TRANSLATION_Y, newStartValue); 6498df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek child.setTag(TAG_END_TRANSLATION_Y, newEndValue); 6508df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek previousAnimator.setCurrentPlayTime(previousAnimator.getCurrentPlayTime()); 6518df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek return; 6528df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek } else { 6538df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek // no new animation needed, let's just apply the value 6548df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek child.setTranslationY(newEndValue); 6558df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek return; 656eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek } 657eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek } 658eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek 659eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek ObjectAnimator animator = ObjectAnimator.ofFloat(child, View.TRANSLATION_Y, 6608df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek child.getTranslationY(), newEndValue); 661a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek Interpolator interpolator = mHeadsUpAppearChildren.contains(child) ? 662a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek mHeadsUpAppearInterpolator :mFastOutSlowInInterpolator; 663a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek animator.setInterpolator(interpolator); 66460d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi long newDuration = cancelAnimatorAndGetNewDuration(duration, previousAnimator); 6653af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek animator.setDuration(newDuration); 6668efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek if (delay > 0 && (previousAnimator == null || !previousAnimator.isRunning())) { 6678efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek animator.setStartDelay(delay); 6688efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek } 669eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek animator.addListener(getGlobalAnimationFinishedListener()); 670eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek // remove the tag when the animation is finished 671eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek animator.addListener(new AnimatorListenerAdapter() { 672eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek @Override 673eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek public void onAnimationEnd(Animator animation) { 6740fccc729fd3b19a62efd90ae13b207faa3d1e8bbSelim Cinek HeadsUpManager.setIsClickedNotification(child, false); 675eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek child.setTag(TAG_ANIMATOR_TRANSLATION_Y, null); 6768df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek child.setTag(TAG_START_TRANSLATION_Y, null); 677eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek child.setTag(TAG_END_TRANSLATION_Y, null); 678eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek } 679eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek }); 6808efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek startAnimator(animator); 681eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek child.setTag(TAG_ANIMATOR_TRANSLATION_Y, animator); 6828df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek child.setTag(TAG_START_TRANSLATION_Y, child.getTranslationY()); 6838df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek child.setTag(TAG_END_TRANSLATION_Y, newEndValue); 684eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek } 685eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek 686b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek private void startScaleAnimation(final View child, 687b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek ViewState viewState, long duration) { 6888df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek Float previousStartValue = getChildTag(child, TAG_START_SCALE); 689d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi Float previousEndValue = getChildTag(child, TAG_END_SCALE); 6908df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek float newEndValue = viewState.scale; 6918df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek if (previousEndValue != null && previousEndValue == newEndValue) { 692d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi return; 693d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi } 694d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi ObjectAnimator previousAnimator = getChildTag(child, TAG_ANIMATOR_SCALE); 6958df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek if (!mAnimationFilter.animateScale) { 6968df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek // just a local update was performed 6978df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek if (previousAnimator != null) { 6988df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek // we need to increase all animation keyframes of the previous animator by the 6998df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek // relative change to the end value 7008df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek PropertyValuesHolder[] values = previousAnimator.getValues(); 7018df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek float relativeDiff = newEndValue - previousEndValue; 7028df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek float newStartValue = previousStartValue + relativeDiff; 7038df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek values[0].setFloatValues(newStartValue, newEndValue); 7048df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek values[1].setFloatValues(newStartValue, newEndValue); 7058df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek child.setTag(TAG_START_SCALE, newStartValue); 7068df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek child.setTag(TAG_END_SCALE, newEndValue); 7078df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek previousAnimator.setCurrentPlayTime(previousAnimator.getCurrentPlayTime()); 7088df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek return; 7098df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek } else { 7108df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek // no new animation needed, let's just apply the value 7118df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek child.setScaleX(newEndValue); 7128df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek child.setScaleY(newEndValue); 713d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi } 714d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi } 715d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi 716d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi PropertyValuesHolder holderX = 7178df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek PropertyValuesHolder.ofFloat(View.SCALE_X, child.getScaleX(), newEndValue); 718d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi PropertyValuesHolder holderY = 7198df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek PropertyValuesHolder.ofFloat(View.SCALE_Y, child.getScaleY(), newEndValue); 720d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi ObjectAnimator animator = ObjectAnimator.ofPropertyValuesHolder(child, holderX, holderY); 721d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi animator.setInterpolator(mFastOutSlowInInterpolator); 72260d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi long newDuration = cancelAnimatorAndGetNewDuration(duration, previousAnimator); 723d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi animator.setDuration(newDuration); 724d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi animator.addListener(getGlobalAnimationFinishedListener()); 725d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi // remove the tag when the animation is finished 726d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi animator.addListener(new AnimatorListenerAdapter() { 727d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi @Override 728d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi public void onAnimationEnd(Animator animation) { 729d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi child.setTag(TAG_ANIMATOR_SCALE, null); 7308df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek child.setTag(TAG_START_SCALE, null); 731d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi child.setTag(TAG_END_SCALE, null); 732d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi } 733d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi }); 7348efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek startAnimator(animator); 735d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi child.setTag(TAG_ANIMATOR_SCALE, animator); 7368df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek child.setTag(TAG_START_SCALE, child.getScaleX()); 7378df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek child.setTag(TAG_END_SCALE, newEndValue); 738d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi } 739d552d9d8e964c102e6832610be46cf2c041e8829Jorim Jaggi 7408efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek private void startAnimator(ValueAnimator animator) { 7418efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek mAnimatorSet.add(animator); 7423af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek animator.start(); 7433af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek } 7443af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek 7453af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek /** 7463af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek * @return an adapter which ensures that onAnimationFinished is called once no animation is 7473af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek * running anymore 7483af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek */ 7493af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek private AnimatorListenerAdapter getGlobalAnimationFinishedListener() { 7503af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek if (!mAnimationListenerPool.empty()) { 7513af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek return mAnimationListenerPool.pop(); 7523af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek } 7533af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek 7543af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek // We need to create a new one, no reusable ones found 7553af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek return new AnimatorListenerAdapter() { 7563af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek private boolean mWasCancelled; 7573af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek 7583af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek @Override 7593af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek public void onAnimationEnd(Animator animation) { 7603af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek mAnimatorSet.remove(animation); 7613af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek if (mAnimatorSet.isEmpty() && !mWasCancelled) { 7623af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek onAnimationFinished(); 7633af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek } 7643af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek mAnimationListenerPool.push(this); 7653af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek } 7663af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek 7673af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek @Override 7683af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek public void onAnimationCancel(Animator animation) { 7693af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek mWasCancelled = true; 7703af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek } 7713af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek 7723af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek @Override 7733af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek public void onAnimationStart(Animator animation) { 7743af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek mWasCancelled = false; 7753af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek } 7763af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek }; 7773af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek } 7783af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek 779aac932591d7aa05bae61d2b47ed7647f35da0001Selim Cinek public static <T> T getChildTag(View child, int tag) { 780eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek return (T) child.getTag(tag); 781eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek } 782eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek 783eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek /** 784eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek * Cancel the previous animator and get the duration of the new animation. 78539610545f0c2714a3526bc935effe57b421542d1Selim Cinek * 78660d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi * @param duration the new duration 787eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek * @param previousAnimator the animator which was running before 788eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek * @return the new duration 789eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek */ 79060d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi private long cancelAnimatorAndGetNewDuration(long duration, ValueAnimator previousAnimator) { 79160d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi long newDuration = duration; 792eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek if (previousAnimator != null) { 7938df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek // We take either the desired length of the new animation or the remaining time of 7948df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek // the previous animator, whichever is longer. 7958df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek newDuration = Math.max(previousAnimator.getDuration() 7968df56452cb696ebdee82df6fb255892eabf3febcSelim Cinek - previousAnimator.getCurrentPlayTime(), newDuration); 7973af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek previousAnimator.cancel(); 798eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek } 7993af00cf10660c7fdc0582dc12361c13673d0c9bbSelim Cinek return newDuration; 800eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek } 801eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek 802eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek private void onAnimationFinished() { 803eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek mHostLayout.onChildAnimationFinished(); 8048f93763cf0f4b3c41f6b0ecac09b621eb0ca328bSelim Cinek for (View v : mChildrenToClearFromOverlay) { 8058f93763cf0f4b3c41f6b0ecac09b621eb0ca328bSelim Cinek mHostLayout.getOverlay().remove(v); 8068f93763cf0f4b3c41f6b0ecac09b621eb0ca328bSelim Cinek } 8078f93763cf0f4b3c41f6b0ecac09b621eb0ca328bSelim Cinek mChildrenToClearFromOverlay.clear(); 808eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek } 809eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek 810eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek /** 811eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek * Process the animationEvents for a new animation 812eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek * 813eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek * @param animationEvents the animation events for the animation to perform 81439610545f0c2714a3526bc935effe57b421542d1Selim Cinek * @param finalState the final state to animate to 81539610545f0c2714a3526bc935effe57b421542d1Selim Cinek */ 816eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek private void processAnimationEvents( 8170dd6881ea481c855976214807c17595b34a2920aJorim Jaggi ArrayList<NotificationStackScrollLayout.AnimationEvent> animationEvents, 818572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek StackScrollState finalState) { 8198d9ff9c2c66bc1d3b92eb6992d58599ff80ed6dcSelim Cinek for (NotificationStackScrollLayout.AnimationEvent event : animationEvents) { 8208efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek final ExpandableView changingView = (ExpandableView) event.changingView; 8218efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek if (event.animationType == 8228efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek NotificationStackScrollLayout.AnimationEvent.ANIMATION_TYPE_ADD) { 8238efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek 8248efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek // This item is added, initialize it's properties. 825b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek StackViewState viewState = finalState 8268efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek .getViewStateForView(changingView); 8278efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek if (viewState == null) { 8288efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek // The position for this child was never generated, let's continue. 8298efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek continue; 8308efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek } 8318efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek if (changingView.getVisibility() == View.GONE) { 8328efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek // The view was set to gone but the state never removed 8338efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek finalState.removeViewStateForView(changingView); 8348efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek continue; 83539610545f0c2714a3526bc935effe57b421542d1Selim Cinek } 836b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek finalState.applyState(changingView, viewState); 8378efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek mNewAddChildren.add(changingView); 8388efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek 8398efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek } else if (event.animationType == 8408efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek NotificationStackScrollLayout.AnimationEvent.ANIMATION_TYPE_REMOVE) { 8418efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek if (changingView.getVisibility() == View.GONE) { 84295ed59283bd25fb363d13c000a7408bcafb5e93eSelim Cinek mHostLayout.getOverlay().remove(changingView); 8438efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek continue; 8448efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek } 8458efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek 8468efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek // Find the amount to translate up. This is needed in order to understand the 8478efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek // direction of the remove animation (either downwards or upwards) 848b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek StackViewState viewState = finalState 8498efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek .getViewStateForView(event.viewAfterChangingView); 8508efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek int actualHeight = changingView.getActualHeight(); 8518efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek // upwards by default 8528efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek float translationDirection = -1.0f; 8538efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek if (viewState != null) { 8548efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek // there was a view after this one, Approximate the distance the next child 8558efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek // travelled 8568efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek translationDirection = ((viewState.yTranslation 8578efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek - (changingView.getTranslationY() + actualHeight / 2.0f)) * 2 / 8588efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek actualHeight); 8598efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek translationDirection = Math.max(Math.min(translationDirection, 1.0f),-1.0f); 8608efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek 8618efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek } 86260d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi changingView.performRemoveAnimation(ANIMATION_DURATION_APPEAR_DISAPPEAR, 86360d07c597c3f996deb3f2743466fe5279ca15e8dJorim Jaggi translationDirection, new Runnable() { 8648efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek @Override 8658efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek public void run() { 8668efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek // remove the temporary overlay 8678efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek mHostLayout.getOverlay().remove(changingView); 8688efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek } 8698efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek }); 870b036ca4de8e93d83bcdc093fbf8f096dc18a810dSelim Cinek } else if (event.animationType == 871f336f4c13ad3be15e2b44266cd786685975425f2Selim Cinek NotificationStackScrollLayout.AnimationEvent.ANIMATION_TYPE_REMOVE_SWIPED_OUT) { 872f336f4c13ad3be15e2b44266cd786685975425f2Selim Cinek // A race condition can trigger the view to be added to the overlay even though 873f336f4c13ad3be15e2b44266cd786685975425f2Selim Cinek // it is swiped out. So let's remove it 874f336f4c13ad3be15e2b44266cd786685975425f2Selim Cinek mHostLayout.getOverlay().remove(changingView); 875b5605e58cb8080c8c887b1885336b707596c8094Selim Cinek } else if (event.animationType == NotificationStackScrollLayout 876b5605e58cb8080c8c887b1885336b707596c8094Selim Cinek .AnimationEvent.ANIMATION_TYPE_GROUP_EXPANSION_CHANGED) { 877b5605e58cb8080c8c887b1885336b707596c8094Selim Cinek ExpandableNotificationRow row = (ExpandableNotificationRow) event.changingView; 878b5605e58cb8080c8c887b1885336b707596c8094Selim Cinek row.prepareExpansionChanged(finalState); 879b5605e58cb8080c8c887b1885336b707596c8094Selim Cinek mChildExpandingView = row; 880b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek } else if (event.animationType == NotificationStackScrollLayout 881b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek .AnimationEvent.ANIMATION_TYPE_HEADS_UP_APPEAR) { 882b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek // This item is added, initialize it's properties. 883b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek StackViewState viewState = finalState.getViewStateForView(changingView); 884b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek mTmpState.copyFrom(viewState); 885a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek if (event.headsUpFromBottom) { 886a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek mTmpState.yTranslation = mHeadsUpAppearHeightBottom; 887a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek } else { 888a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek mTmpState.yTranslation = -mTmpState.height; 889a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek } 890a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek mHeadsUpAppearChildren.add(changingView); 891b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek finalState.applyState(changingView, mTmpState); 892a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek } else if (event.animationType == NotificationStackScrollLayout 893a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek .AnimationEvent.ANIMATION_TYPE_HEADS_UP_DISAPPEAR) { 894a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek mHeadsUpDisappearChildren.add(changingView); 8958f93763cf0f4b3c41f6b0ecac09b621eb0ca328bSelim Cinek if (mHostLayout.indexOfChild(changingView) == -1) { 8968f93763cf0f4b3c41f6b0ecac09b621eb0ca328bSelim Cinek // This notification was actually removed, so we need to add it to the overlay 8978f93763cf0f4b3c41f6b0ecac09b621eb0ca328bSelim Cinek mHostLayout.getOverlay().add(changingView); 898eaee9c01902ecfc253be98d68e5d7b586ed54463Selim Cinek mTmpState.initFrom(changingView); 899eaee9c01902ecfc253be98d68e5d7b586ed54463Selim Cinek mTmpState.yTranslation = -changingView.getActualHeight(); 9008f93763cf0f4b3c41f6b0ecac09b621eb0ca328bSelim Cinek // We temporarily enable Y animations, the real filter will be combined 9018f93763cf0f4b3c41f6b0ecac09b621eb0ca328bSelim Cinek // afterwards anyway 9028f93763cf0f4b3c41f6b0ecac09b621eb0ca328bSelim Cinek mAnimationFilter.animateY = true; 903eaee9c01902ecfc253be98d68e5d7b586ed54463Selim Cinek startViewAnimations(changingView, mTmpState, 0, 9048f93763cf0f4b3c41f6b0ecac09b621eb0ca328bSelim Cinek ANIMATION_DURATION_HEADS_UP_DISAPPEAR); 9058f93763cf0f4b3c41f6b0ecac09b621eb0ca328bSelim Cinek mChildrenToClearFromOverlay.add(changingView); 9068f93763cf0f4b3c41f6b0ecac09b621eb0ca328bSelim Cinek } 907572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek } 9088efa6dde2b4f2cdbf046b87b7366404c3cc46219Selim Cinek mNewEvents.add(event); 909572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek } 910572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek } 9118d9ff9c2c66bc1d3b92eb6992d58599ff80ed6dcSelim Cinek 912475b21dfe517ec04f435f6b02f4a53083d040db4Jorim Jaggi public void animateOverScrollToAmount(float targetAmount, final boolean onTop, 913475b21dfe517ec04f435f6b02f4a53083d040db4Jorim Jaggi final boolean isRubberbanded) { 9148d9ff9c2c66bc1d3b92eb6992d58599ff80ed6dcSelim Cinek final float startOverScrollAmount = mHostLayout.getCurrentOverScrollAmount(onTop); 9159012958742c7a66b37ba5f2196f9086bb1980e6bJorim Jaggi if (targetAmount == startOverScrollAmount) { 9169012958742c7a66b37ba5f2196f9086bb1980e6bJorim Jaggi return; 9179012958742c7a66b37ba5f2196f9086bb1980e6bJorim Jaggi } 9188d9ff9c2c66bc1d3b92eb6992d58599ff80ed6dcSelim Cinek cancelOverScrollAnimators(onTop); 9198d9ff9c2c66bc1d3b92eb6992d58599ff80ed6dcSelim Cinek ValueAnimator overScrollAnimator = ValueAnimator.ofFloat(startOverScrollAmount, 9208d9ff9c2c66bc1d3b92eb6992d58599ff80ed6dcSelim Cinek targetAmount); 9218d9ff9c2c66bc1d3b92eb6992d58599ff80ed6dcSelim Cinek overScrollAnimator.setDuration(ANIMATION_DURATION_STANDARD); 9228d9ff9c2c66bc1d3b92eb6992d58599ff80ed6dcSelim Cinek overScrollAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { 9238d9ff9c2c66bc1d3b92eb6992d58599ff80ed6dcSelim Cinek @Override 9248d9ff9c2c66bc1d3b92eb6992d58599ff80ed6dcSelim Cinek public void onAnimationUpdate(ValueAnimator animation) { 9258d9ff9c2c66bc1d3b92eb6992d58599ff80ed6dcSelim Cinek float currentOverScroll = (float) animation.getAnimatedValue(); 92647c85a3525dcd0bbd3168632830e8ab491d18462Jorim Jaggi mHostLayout.setOverScrollAmount( 927475b21dfe517ec04f435f6b02f4a53083d040db4Jorim Jaggi currentOverScroll, onTop, false /* animate */, false /* cancelAnimators */, 928475b21dfe517ec04f435f6b02f4a53083d040db4Jorim Jaggi isRubberbanded); 9298d9ff9c2c66bc1d3b92eb6992d58599ff80ed6dcSelim Cinek } 9308d9ff9c2c66bc1d3b92eb6992d58599ff80ed6dcSelim Cinek }); 9318d9ff9c2c66bc1d3b92eb6992d58599ff80ed6dcSelim Cinek overScrollAnimator.setInterpolator(mFastOutSlowInInterpolator); 93247c85a3525dcd0bbd3168632830e8ab491d18462Jorim Jaggi overScrollAnimator.addListener(new AnimatorListenerAdapter() { 93347c85a3525dcd0bbd3168632830e8ab491d18462Jorim Jaggi @Override 93447c85a3525dcd0bbd3168632830e8ab491d18462Jorim Jaggi public void onAnimationEnd(Animator animation) { 93547c85a3525dcd0bbd3168632830e8ab491d18462Jorim Jaggi if (onTop) { 93647c85a3525dcd0bbd3168632830e8ab491d18462Jorim Jaggi mTopOverScrollAnimator = null; 93747c85a3525dcd0bbd3168632830e8ab491d18462Jorim Jaggi } else { 93847c85a3525dcd0bbd3168632830e8ab491d18462Jorim Jaggi mBottomOverScrollAnimator = null; 93947c85a3525dcd0bbd3168632830e8ab491d18462Jorim Jaggi } 94047c85a3525dcd0bbd3168632830e8ab491d18462Jorim Jaggi } 94147c85a3525dcd0bbd3168632830e8ab491d18462Jorim Jaggi }); 9428d9ff9c2c66bc1d3b92eb6992d58599ff80ed6dcSelim Cinek overScrollAnimator.start(); 9438d9ff9c2c66bc1d3b92eb6992d58599ff80ed6dcSelim Cinek if (onTop) { 9448d9ff9c2c66bc1d3b92eb6992d58599ff80ed6dcSelim Cinek mTopOverScrollAnimator = overScrollAnimator; 9458d9ff9c2c66bc1d3b92eb6992d58599ff80ed6dcSelim Cinek } else { 9468d9ff9c2c66bc1d3b92eb6992d58599ff80ed6dcSelim Cinek mBottomOverScrollAnimator = overScrollAnimator; 9478d9ff9c2c66bc1d3b92eb6992d58599ff80ed6dcSelim Cinek } 9488d9ff9c2c66bc1d3b92eb6992d58599ff80ed6dcSelim Cinek } 9498d9ff9c2c66bc1d3b92eb6992d58599ff80ed6dcSelim Cinek 9508d9ff9c2c66bc1d3b92eb6992d58599ff80ed6dcSelim Cinek public void cancelOverScrollAnimators(boolean onTop) { 9518d9ff9c2c66bc1d3b92eb6992d58599ff80ed6dcSelim Cinek ValueAnimator currentAnimator = onTop ? mTopOverScrollAnimator : mBottomOverScrollAnimator; 9528d9ff9c2c66bc1d3b92eb6992d58599ff80ed6dcSelim Cinek if (currentAnimator != null) { 9538d9ff9c2c66bc1d3b92eb6992d58599ff80ed6dcSelim Cinek currentAnimator.cancel(); 9548d9ff9c2c66bc1d3b92eb6992d58599ff80ed6dcSelim Cinek } 9558d9ff9c2c66bc1d3b92eb6992d58599ff80ed6dcSelim Cinek } 95602af41efe54eb2cc8fde7311e4cf5f0e5ff2373cSelim Cinek 95702af41efe54eb2cc8fde7311e4cf5f0e5ff2373cSelim Cinek /** 95802af41efe54eb2cc8fde7311e4cf5f0e5ff2373cSelim Cinek * Get the end value of the height animation running on a view or the actualHeight 95902af41efe54eb2cc8fde7311e4cf5f0e5ff2373cSelim Cinek * if no animation is running. 96002af41efe54eb2cc8fde7311e4cf5f0e5ff2373cSelim Cinek */ 96102af41efe54eb2cc8fde7311e4cf5f0e5ff2373cSelim Cinek public static int getFinalActualHeight(ExpandableView view) { 96202af41efe54eb2cc8fde7311e4cf5f0e5ff2373cSelim Cinek if (view == null) { 96302af41efe54eb2cc8fde7311e4cf5f0e5ff2373cSelim Cinek return 0; 96402af41efe54eb2cc8fde7311e4cf5f0e5ff2373cSelim Cinek } 96502af41efe54eb2cc8fde7311e4cf5f0e5ff2373cSelim Cinek ValueAnimator heightAnimator = getChildTag(view, TAG_ANIMATOR_HEIGHT); 96602af41efe54eb2cc8fde7311e4cf5f0e5ff2373cSelim Cinek if (heightAnimator == null) { 96702af41efe54eb2cc8fde7311e4cf5f0e5ff2373cSelim Cinek return view.getActualHeight(); 96802af41efe54eb2cc8fde7311e4cf5f0e5ff2373cSelim Cinek } else { 96902af41efe54eb2cc8fde7311e4cf5f0e5ff2373cSelim Cinek return getChildTag(view, TAG_END_HEIGHT); 97002af41efe54eb2cc8fde7311e4cf5f0e5ff2373cSelim Cinek } 97102af41efe54eb2cc8fde7311e4cf5f0e5ff2373cSelim Cinek } 972a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek 973a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek public void setHeadsUpAppearHeightBottom(int headsUpAppearHeightBottom) { 974a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek mHeadsUpAppearHeightBottom = headsUpAppearHeightBottom; 975a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek } 976a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek 977a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek public void setShadeExpanded(boolean shadeExpanded) { 978a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek mShadeExpanded = shadeExpanded; 979a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek } 980572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek} 981